From 80c0a7c1f8675e2f6f4e8b11dc81da4c9d8b130e Mon Sep 17 00:00:00 2001 From: Artyom Bakhtin Date: Tue, 10 Jul 2018 12:15:13 +0300 Subject: [PATCH 01/97] fix jenkins build link Signed-off-by: Artyom Bakhtin --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 43e94c3697..cf40ea2a28 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ [![CII Best Practices](https://bestpractices.coreinfrastructure.org/projects/960/badge)](https://bestpractices.coreinfrastructure.org/projects/960) [![codecov](https://codecov.io/gh/hyperledger/iroha/branch/master/graph/badge.svg)](https://codecov.io/gh/hyperledger/iroha) [![Snap Status](https://build.snapcraft.io/badge/hyperledger/iroha.svg)](https://build.snapcraft.io/user/hyperledger/iroha) -[![Build Status](https://jenkins.soramitsu.co.jp/buildStatus/icon?job=iroha/iroha-hyperledger/master)](https://jenkins.soramitsu.co.jp/job/iroha/iroha-hyperledger/master) +[![Build Status](https://jenkins.soramitsu.co.jp/buildStatus/icon?job=iroha/iroha-hyperledger/master)](https://jenkins.soramitsu.co.jp/job/iroha/job/iroha-hyperledger/job/master/) [![Throughput Graph](https://graphs.waffle.io/hyperledger/iroha/throughput.svg)](https://waffle.io/hyperledger/iroha/metrics/throughput) Blockchain platform Hyperledger Iroha is designed for simple creation and management of assets. This is a distributed ledger of transactions. From 058981389225381f42f197d46cd4453b7990e173 Mon Sep 17 00:00:00 2001 From: Akvinikym Date: Mon, 25 Jun 2018 10:51:21 +0300 Subject: [PATCH 02/97] Command validator error responses (#1486) Command validator now has error messages Signed-off-by: Akvinikym --- .../ametsuchi/impl/mutable_storage_impl.cpp | 2 +- irohad/ametsuchi/impl/temporary_wsv_impl.cpp | 26 +- irohad/execution/command_executor.hpp | 364 ++++---- irohad/execution/impl/command_executor.cpp | 840 +++++++++++++----- .../command_validate_execute_test.cpp | 39 +- 5 files changed, 840 insertions(+), 431 deletions(-) diff --git a/irohad/ametsuchi/impl/mutable_storage_impl.cpp b/irohad/ametsuchi/impl/mutable_storage_impl.cpp index 0130d58849..1898653b09 100644 --- a/irohad/ametsuchi/impl/mutable_storage_impl.cpp +++ b/irohad/ametsuchi/impl/mutable_storage_impl.cpp @@ -57,7 +57,7 @@ namespace iroha { auto execute_command = [this](auto &command) { auto result = boost::apply_visitor(*command_executor_, command.get()); return result.match([](expected::Value &v) { return true; }, - [&](expected::Error &e) { + [&](expected::Error &e) { log_->error(e.error.toString()); return false; }); diff --git a/irohad/ametsuchi/impl/temporary_wsv_impl.cpp b/irohad/ametsuchi/impl/temporary_wsv_impl.cpp index 9e69b70e3c..ea5637ece1 100644 --- a/irohad/ametsuchi/impl/temporary_wsv_impl.cpp +++ b/irohad/ametsuchi/impl/temporary_wsv_impl.cpp @@ -47,15 +47,27 @@ namespace iroha { command_validator_->setCreatorAccountId(tx_creator); auto execute_command = [this, &tx_creator](auto &command) { auto account = wsv_->getAccount(tx_creator).value(); - if (not boost::apply_visitor(*command_validator_, command.get())) { + + // Temporary variant: going to be a chain of results in future pull + // requests + auto validation_result = + boost::apply_visitor(*command_validator_, command.get()) + .match([](expected::Value &) { return true; }, + [this](expected::Error &e) { + log_->error(e.error.toString()); + return false; + }); + if (not validation_result) { return false; } - auto result = boost::apply_visitor(*command_executor_, command.get()); - return result.match([](expected::Value &v) { return true; }, - [this](expected::Error &e) { - log_->error(e.error.toString()); - return false; - }); + auto execution_result = + boost::apply_visitor(*command_executor_, command.get()); + return execution_result.match( + [](expected::Value &v) { return true; }, + [this](expected::Error &e) { + log_->error(e.error.toString()); + return false; + }); }; transaction_->exec("SAVEPOINT savepoint_;"); diff --git a/irohad/execution/command_executor.hpp b/irohad/execution/command_executor.hpp index 045f92b7a5..08e39351df 100644 --- a/irohad/execution/command_executor.hpp +++ b/irohad/execution/command_executor.hpp @@ -45,10 +45,10 @@ namespace iroha { /** - * Error for command execution. + * Error for command execution or validation * Contains command name, as well as an error message */ - struct ExecutionError { + struct CommandError { std::string command_name; std::string error_message; @@ -58,68 +58,68 @@ namespace iroha { }; /** - * ExecutionResult is a return type of all execute functions. - * If execute is successful, result will not contain anything (void value), - * because execute does not return any value. - * If execution is not successful, ExecutionResult will contain Execution - * error with explanation + * CommandResult is a return type of all execute and validate functions. + * If execute or validate is successful, result will not contain anything + * (void value), because execute and validate does not return any value. If + * execution or validation is not successful, CommandResult will contain + * Execution error with explanation * * Result is used because it allows to clear distinction between two states * - value and error. If we just returned error, it would be confusing, what - * value of an error to consider as a successful state of execution + * value of an error to consider as a successful state of execution or + * validation */ - using ExecutionResult = iroha::expected::Result; + using CommandResult = iroha::expected::Result; - class CommandExecutor : public boost::static_visitor { + class CommandExecutor : public boost::static_visitor { public: CommandExecutor(std::shared_ptr queries, std::shared_ptr commands); - ExecutionResult operator()( + CommandResult operator()( const shared_model::interface::AddAssetQuantity &command); - ExecutionResult operator()(const shared_model::interface::AddPeer &command); + CommandResult operator()(const shared_model::interface::AddPeer &command); - ExecutionResult operator()( + CommandResult operator()( const shared_model::interface::AddSignatory &command); - ExecutionResult operator()( + CommandResult operator()( const shared_model::interface::AppendRole &command); - ExecutionResult operator()( + CommandResult operator()( const shared_model::interface::CreateAccount &command); - ExecutionResult operator()( + CommandResult operator()( const shared_model::interface::CreateAsset &command); - ExecutionResult operator()( + CommandResult operator()( const shared_model::interface::CreateDomain &command); - ExecutionResult operator()( + CommandResult operator()( const shared_model::interface::CreateRole &command); - ExecutionResult operator()( + CommandResult operator()( const shared_model::interface::DetachRole &command); - ExecutionResult operator()( + CommandResult operator()( const shared_model::interface::GrantPermission &command); - ExecutionResult operator()( + CommandResult operator()( const shared_model::interface::RemoveSignatory &command); - ExecutionResult operator()( + CommandResult operator()( const shared_model::interface::RevokePermission &command); - ExecutionResult operator()( + CommandResult operator()( const shared_model::interface::SetAccountDetail &command); - ExecutionResult operator()( - const shared_model::interface::SetQuorum &command); + CommandResult operator()(const shared_model::interface::SetQuorum &command); - ExecutionResult operator()( + CommandResult operator()( const shared_model::interface::SubtractAssetQuantity &command); - ExecutionResult operator()( + CommandResult operator()( const shared_model::interface::TransferAsset &command); void setCreatorAccountId(const shared_model::interface::types::AccountIdType @@ -137,183 +137,201 @@ namespace iroha { shared_model::builder::DefaultDomainBuilder domain_builder_; }; - class CommandValidator : public boost::static_visitor { + class CommandValidator : public boost::static_visitor { public: CommandValidator(std::shared_ptr queries); template - bool operator()(const CommandType &command) { - return hasPermissions(command, *queries, creator_account_id) - and isValid(command, *queries, creator_account_id); + CommandResult operator()(const CommandType &command) { + return hasPermissions(command, *queries, creator_account_id) | + [&] { return isValid(command, *queries, creator_account_id); }; } void setCreatorAccountId(const shared_model::interface::types::AccountIdType &creator_account_id); private: - bool hasPermissions( + CommandResult hasPermissions( const shared_model::interface::AddAssetQuantity &command, iroha::ametsuchi::WsvQuery &queries, const shared_model::interface::types::AccountIdType &creator_account_id); - bool hasPermissions(const shared_model::interface::AddPeer &command, - iroha::ametsuchi::WsvQuery &queries, - const shared_model::interface::types::AccountIdType - &creator_account_id); - - bool hasPermissions(const shared_model::interface::AddSignatory &command, - iroha::ametsuchi::WsvQuery &queries, - const shared_model::interface::types::AccountIdType - &creator_account_id); - - bool hasPermissions(const shared_model::interface::AppendRole &command, - iroha::ametsuchi::WsvQuery &queries, - const shared_model::interface::types::AccountIdType - &creator_account_id); - - bool hasPermissions(const shared_model::interface::CreateAccount &command, - iroha::ametsuchi::WsvQuery &queries, - const shared_model::interface::types::AccountIdType - &creator_account_id); - - bool hasPermissions(const shared_model::interface::CreateAsset &command, - iroha::ametsuchi::WsvQuery &queries, - const shared_model::interface::types::AccountIdType - &creator_account_id); - - bool hasPermissions(const shared_model::interface::CreateDomain &command, - iroha::ametsuchi::WsvQuery &queries, - const shared_model::interface::types::AccountIdType - &creator_account_id); - - bool hasPermissions(const shared_model::interface::CreateRole &command, - iroha::ametsuchi::WsvQuery &queries, - const shared_model::interface::types::AccountIdType - &creator_account_id); - - bool hasPermissions(const shared_model::interface::DetachRole &command, - iroha::ametsuchi::WsvQuery &queries, - const shared_model::interface::types::AccountIdType - &creator_account_id); - - bool hasPermissions(const shared_model::interface::GrantPermission &command, - iroha::ametsuchi::WsvQuery &queries, - const shared_model::interface::types::AccountIdType - &creator_account_id); - - bool hasPermissions(const shared_model::interface::RemoveSignatory &command, - iroha::ametsuchi::WsvQuery &queries, - const shared_model::interface::types::AccountIdType - &creator_account_id); - - bool hasPermissions( + CommandResult hasPermissions( + const shared_model::interface::AddPeer &command, + iroha::ametsuchi::WsvQuery &queries, + const shared_model::interface::types::AccountIdType + &creator_account_id); + + CommandResult hasPermissions( + const shared_model::interface::AddSignatory &command, + iroha::ametsuchi::WsvQuery &queries, + const shared_model::interface::types::AccountIdType + &creator_account_id); + + CommandResult hasPermissions( + const shared_model::interface::AppendRole &command, + iroha::ametsuchi::WsvQuery &queries, + const shared_model::interface::types::AccountIdType + &creator_account_id); + + CommandResult hasPermissions( + const shared_model::interface::CreateAccount &command, + iroha::ametsuchi::WsvQuery &queries, + const shared_model::interface::types::AccountIdType + &creator_account_id); + + CommandResult hasPermissions( + const shared_model::interface::CreateAsset &command, + iroha::ametsuchi::WsvQuery &queries, + const shared_model::interface::types::AccountIdType + &creator_account_id); + + CommandResult hasPermissions( + const shared_model::interface::CreateDomain &command, + iroha::ametsuchi::WsvQuery &queries, + const shared_model::interface::types::AccountIdType + &creator_account_id); + + CommandResult hasPermissions( + const shared_model::interface::CreateRole &command, + iroha::ametsuchi::WsvQuery &queries, + const shared_model::interface::types::AccountIdType + &creator_account_id); + + CommandResult hasPermissions( + const shared_model::interface::DetachRole &command, + iroha::ametsuchi::WsvQuery &queries, + const shared_model::interface::types::AccountIdType + &creator_account_id); + + CommandResult hasPermissions( + const shared_model::interface::GrantPermission &command, + iroha::ametsuchi::WsvQuery &queries, + const shared_model::interface::types::AccountIdType + &creator_account_id); + + CommandResult hasPermissions( + const shared_model::interface::RemoveSignatory &command, + iroha::ametsuchi::WsvQuery &queries, + const shared_model::interface::types::AccountIdType + &creator_account_id); + + CommandResult hasPermissions( + const shared_model::interface::RevokePermission &command, + iroha::ametsuchi::WsvQuery &queries, + const shared_model::interface::types::AccountIdType + &creator_account_id); + + CommandResult hasPermissions( + const shared_model::interface::SetAccountDetail &command, + iroha::ametsuchi::WsvQuery &queries, + const shared_model::interface::types::AccountIdType + &creator_account_id); + + CommandResult hasPermissions( + const shared_model::interface::SetQuorum &command, + iroha::ametsuchi::WsvQuery &queries, + const shared_model::interface::types::AccountIdType + &creator_account_id); + + CommandResult hasPermissions( + const shared_model::interface::SubtractAssetQuantity &command, + iroha::ametsuchi::WsvQuery &queries, + const shared_model::interface::types::AccountIdType + &creator_account_id); + + CommandResult hasPermissions( + const shared_model::interface::TransferAsset &command, + iroha::ametsuchi::WsvQuery &queries, + const shared_model::interface::types::AccountIdType + &creator_account_id); + + CommandResult isValid( + const shared_model::interface::AddAssetQuantity &command, + iroha::ametsuchi::WsvQuery &queries, + const shared_model::interface::types::AccountIdType + &creator_account_id); + + CommandResult isValid(const shared_model::interface::AddPeer &command, + iroha::ametsuchi::WsvQuery &queries, + const shared_model::interface::types::AccountIdType + &creator_account_id); + + CommandResult isValid(const shared_model::interface::AddSignatory &command, + iroha::ametsuchi::WsvQuery &queries, + const shared_model::interface::types::AccountIdType + &creator_account_id); + + CommandResult isValid(const shared_model::interface::AppendRole &command, + iroha::ametsuchi::WsvQuery &queries, + const shared_model::interface::types::AccountIdType + &creator_account_id); + + CommandResult isValid(const shared_model::interface::CreateAccount &command, + iroha::ametsuchi::WsvQuery &queries, + const shared_model::interface::types::AccountIdType + &creator_account_id); + + CommandResult isValid(const shared_model::interface::CreateAsset &command, + iroha::ametsuchi::WsvQuery &queries, + const shared_model::interface::types::AccountIdType + &creator_account_id); + + CommandResult isValid(const shared_model::interface::CreateDomain &command, + iroha::ametsuchi::WsvQuery &queries, + const shared_model::interface::types::AccountIdType + &creator_account_id); + + CommandResult isValid(const shared_model::interface::CreateRole &command, + iroha::ametsuchi::WsvQuery &queries, + const shared_model::interface::types::AccountIdType + &creator_account_id); + + CommandResult isValid(const shared_model::interface::DetachRole &command, + iroha::ametsuchi::WsvQuery &queries, + const shared_model::interface::types::AccountIdType + &creator_account_id); + + CommandResult isValid( + const shared_model::interface::GrantPermission &command, + iroha::ametsuchi::WsvQuery &queries, + const shared_model::interface::types::AccountIdType + &creator_account_id); + + CommandResult isValid( + const shared_model::interface::RemoveSignatory &command, + iroha::ametsuchi::WsvQuery &queries, + const shared_model::interface::types::AccountIdType + &creator_account_id); + + CommandResult isValid( const shared_model::interface::RevokePermission &command, iroha::ametsuchi::WsvQuery &queries, const shared_model::interface::types::AccountIdType &creator_account_id); - bool hasPermissions( + CommandResult isValid( const shared_model::interface::SetAccountDetail &command, iroha::ametsuchi::WsvQuery &queries, const shared_model::interface::types::AccountIdType &creator_account_id); - bool hasPermissions(const shared_model::interface::SetQuorum &command, - iroha::ametsuchi::WsvQuery &queries, - const shared_model::interface::types::AccountIdType - &creator_account_id); + CommandResult isValid(const shared_model::interface::SetQuorum &command, + iroha::ametsuchi::WsvQuery &queries, + const shared_model::interface::types::AccountIdType + &creator_account_id); - bool hasPermissions( + CommandResult isValid( const shared_model::interface::SubtractAssetQuantity &command, iroha::ametsuchi::WsvQuery &queries, const shared_model::interface::types::AccountIdType &creator_account_id); - bool hasPermissions(const shared_model::interface::TransferAsset &command, - iroha::ametsuchi::WsvQuery &queries, - const shared_model::interface::types::AccountIdType - &creator_account_id); - - bool isValid(const shared_model::interface::AddAssetQuantity &command, - iroha::ametsuchi::WsvQuery &queries, - const shared_model::interface::types::AccountIdType - &creator_account_id); - - bool isValid(const shared_model::interface::AddPeer &command, - iroha::ametsuchi::WsvQuery &queries, - const shared_model::interface::types::AccountIdType - &creator_account_id); - - bool isValid(const shared_model::interface::AddSignatory &command, - iroha::ametsuchi::WsvQuery &queries, - const shared_model::interface::types::AccountIdType - &creator_account_id); - - bool isValid(const shared_model::interface::AppendRole &command, - iroha::ametsuchi::WsvQuery &queries, - const shared_model::interface::types::AccountIdType - &creator_account_id); - - bool isValid(const shared_model::interface::CreateAccount &command, - iroha::ametsuchi::WsvQuery &queries, - const shared_model::interface::types::AccountIdType - &creator_account_id); - - bool isValid(const shared_model::interface::CreateAsset &command, - iroha::ametsuchi::WsvQuery &queries, - const shared_model::interface::types::AccountIdType - &creator_account_id); - - bool isValid(const shared_model::interface::CreateDomain &command, - iroha::ametsuchi::WsvQuery &queries, - const shared_model::interface::types::AccountIdType - &creator_account_id); - - bool isValid(const shared_model::interface::CreateRole &command, - iroha::ametsuchi::WsvQuery &queries, - const shared_model::interface::types::AccountIdType - &creator_account_id); - - bool isValid(const shared_model::interface::DetachRole &command, - iroha::ametsuchi::WsvQuery &queries, - const shared_model::interface::types::AccountIdType - &creator_account_id); - - bool isValid(const shared_model::interface::GrantPermission &command, - iroha::ametsuchi::WsvQuery &queries, - const shared_model::interface::types::AccountIdType - &creator_account_id); - - bool isValid(const shared_model::interface::RemoveSignatory &command, - iroha::ametsuchi::WsvQuery &queries, - const shared_model::interface::types::AccountIdType - &creator_account_id); - - bool isValid(const shared_model::interface::RevokePermission &command, - iroha::ametsuchi::WsvQuery &queries, - const shared_model::interface::types::AccountIdType - &creator_account_id); - - bool isValid(const shared_model::interface::SetAccountDetail &command, - iroha::ametsuchi::WsvQuery &queries, - const shared_model::interface::types::AccountIdType - &creator_account_id); - - bool isValid(const shared_model::interface::SetQuorum &command, - iroha::ametsuchi::WsvQuery &queries, - const shared_model::interface::types::AccountIdType - &creator_account_id); - - bool isValid(const shared_model::interface::SubtractAssetQuantity &command, - iroha::ametsuchi::WsvQuery &queries, - const shared_model::interface::types::AccountIdType - &creator_account_id); - - bool isValid(const shared_model::interface::TransferAsset &command, - iroha::ametsuchi::WsvQuery &queries, - const shared_model::interface::types::AccountIdType - &creator_account_id); + CommandResult isValid(const shared_model::interface::TransferAsset &command, + iroha::ametsuchi::WsvQuery &queries, + const shared_model::interface::types::AccountIdType + &creator_account_id); std::shared_ptr queries; shared_model::interface::types::AccountIdType creator_account_id; diff --git a/irohad/execution/impl/command_executor.cpp b/irohad/execution/impl/command_executor.cpp index 5a48d0c143..ebd1bbb941 100644 --- a/irohad/execution/impl/command_executor.cpp +++ b/irohad/execution/impl/command_executor.cpp @@ -16,6 +16,8 @@ */ #include +#include +#include #include "execution/command_executor.hpp" @@ -28,22 +30,23 @@ using namespace shared_model::detail; using namespace shared_model::interface::permissions; using namespace shared_model::proto::permissions; +using namespace std::literals::string_literals; namespace iroha { - expected::Error makeExecutionError( + expected::Error makeCommandError( const std::string &error_message, const std::string command_name) noexcept { - return expected::makeError(ExecutionError{command_name, error_message}); + return expected::makeError(CommandError{command_name, error_message}); } - ExecutionResult makeExecutionResult(const ametsuchi::WsvCommandResult &result, - std::string command_name) noexcept { + CommandResult makeCommandResult(const ametsuchi::WsvCommandResult &result, + std::string command_name) noexcept { return result.match( - [](const expected::Value &v) -> ExecutionResult { return {}; }, + [](const expected::Value &v) -> CommandResult { return {}; }, [&command_name]( - const expected::Error &e) -> ExecutionResult { - return expected::makeError(ExecutionError{command_name, e.error}); + const expected::Error &e) -> CommandResult { + return expected::makeError(CommandError{command_name, e.error}); }); } @@ -57,19 +60,19 @@ namespace iroha { this->creator_account_id = creator_account_id; } - ExecutionResult CommandExecutor::operator()( + CommandResult CommandExecutor::operator()( const shared_model::interface::AddAssetQuantity &command) { std::string command_name = "AddAssetQuantity"; auto asset = queries->getAsset(command.assetId()); if (not asset) { - return makeExecutionError( + return makeCommandError( (boost::format("asset %s is absent") % command.assetId()).str(), command_name); } auto precision = asset.value()->precision(); if (command.amount().precision() > precision) { - return makeExecutionError( + return makeCommandError( (boost::format("command precision is greater than asset precision: " "expected %d, but got %d") % precision % command.amount().precision()) @@ -79,7 +82,7 @@ namespace iroha { auto command_amount = makeAmountWithPrecision(command.amount(), asset.value()->precision()); if (not queries->getAccount(command.accountId())) { - return makeExecutionError( + return makeCommandError( (boost::format("account %s is absent") % command.accountId()).str(), command_name); } @@ -93,7 +96,7 @@ namespace iroha { }; using AccountAssetResult = expected::Result, - iroha::ExecutionError>; + iroha::CommandError>; auto account_asset_new = new_balance.match( [this, &account_asset, &command_name, &command]( const expected::Value< @@ -123,51 +126,51 @@ namespace iroha { return expected::makeValue(new_account_asset_val.value); }, [&command_name](const auto &error) -> AccountAssetResult { - return makeExecutionError(*error.error, command_name); + return makeCommandError(*error.error, command_name); }); }, [&command_name](const auto &error) -> AccountAssetResult { - return makeExecutionError( + return makeCommandError( "amount builder failed. reason " + *error.error, command_name); }); return account_asset_new.match( [&](const expected::Value< std::shared_ptr> - &account_asset_new_val) -> ExecutionResult { - return makeExecutionResult( + &account_asset_new_val) -> CommandResult { + return makeCommandResult( commands->upsertAccountAsset(*account_asset_new_val.value), command_name); }, - [&command_name](const auto &account_asset_error) -> ExecutionResult { - return makeExecutionError("account asset builder failed. reason " - + account_asset_error.error.toString(), - command_name); + [&command_name](const auto &account_asset_error) -> CommandResult { + return makeCommandError("account asset builder failed. reason " + + account_asset_error.error.toString(), + command_name); }); } - ExecutionResult CommandExecutor::operator()( + CommandResult CommandExecutor::operator()( const shared_model::interface::AddPeer &command) { - return makeExecutionResult(commands->insertPeer(command.peer()), "AddPeer"); + return makeCommandResult(commands->insertPeer(command.peer()), "AddPeer"); } - ExecutionResult CommandExecutor::operator()( + CommandResult CommandExecutor::operator()( const shared_model::interface::AddSignatory &command) { auto result = commands->insertSignatory(command.pubkey()) | [&] { return commands->insertAccountSignatory(command.accountId(), command.pubkey()); }; - return makeExecutionResult(result, "AddSignatory"); + return makeCommandResult(result, "AddSignatory"); } - ExecutionResult CommandExecutor::operator()( + CommandResult CommandExecutor::operator()( const shared_model::interface::AppendRole &command) { - return makeExecutionResult( + return makeCommandResult( commands->insertAccountRole(command.accountId(), command.roleName()), "AppendRole"); } - ExecutionResult CommandExecutor::operator()( + CommandResult CommandExecutor::operator()( const shared_model::interface::CreateAccount &command) { std::string command_name = "CreateAccount"; auto account = @@ -180,10 +183,10 @@ namespace iroha { return account.match( [&](const expected::Value< std::shared_ptr> &account_val) - -> ExecutionResult { + -> CommandResult { auto domain = queries->getDomain(command.domainId()); if (not domain) { - return makeExecutionError( + return makeCommandError( (boost::format("Domain %s not found") % command.domainId()) .str(), command_name); @@ -199,15 +202,15 @@ namespace iroha { return commands->insertAccountRole((*account_val.value).accountId(), domain_default_role); }; - return makeExecutionResult(result, command_name); + return makeCommandResult(result, command_name); }, - [&command_name](const auto &error) -> ExecutionResult { - return makeExecutionError( + [&command_name](const auto &error) -> CommandResult { + return makeCommandError( "account builder failed. reason " + *error.error, command_name); }); } - ExecutionResult CommandExecutor::operator()( + CommandResult CommandExecutor::operator()( const shared_model::interface::CreateAsset &command) { std::string command_name = "CreateAsset"; auto new_asset = @@ -218,18 +221,18 @@ namespace iroha { return new_asset.match( [&](const expected::Value< std::shared_ptr> &new_asset_val) - -> ExecutionResult { + -> CommandResult { // The insert will fail if asset already exists - return makeExecutionResult( - commands->insertAsset(*new_asset_val.value), command_name); + return makeCommandResult(commands->insertAsset(*new_asset_val.value), + command_name); }, - [&command_name](const auto &error) -> ExecutionResult { - return makeExecutionError( + [&command_name](const auto &error) -> CommandResult { + return makeCommandError( "asset builder failed. reason " + *error.error, command_name); }); } - ExecutionResult CommandExecutor::operator()( + CommandResult CommandExecutor::operator()( const shared_model::interface::CreateDomain &command) { std::string command_name = "CreateDomain"; auto new_domain = domain_builder_.domainId(command.domainId()) @@ -238,43 +241,43 @@ namespace iroha { return new_domain.match( [&](const expected::Value< std::shared_ptr> &new_domain_val) - -> ExecutionResult { + -> CommandResult { // The insert will fail if domain already exist - return makeExecutionResult( + return makeCommandResult( commands->insertDomain(*new_domain_val.value), command_name); }, - [&command_name](const auto &error) -> ExecutionResult { - return makeExecutionError( + [&command_name](const auto &error) -> CommandResult { + return makeCommandError( "domain builder failed. reason " + *error.error, command_name); }); } - ExecutionResult CommandExecutor::operator()( + CommandResult CommandExecutor::operator()( const shared_model::interface::CreateRole &command) { std::string command_name = "CreateRole"; auto result = commands->insertRole(command.roleName()) | [&] { return commands->insertRolePermissions(command.roleName(), command.rolePermissions()); }; - return makeExecutionResult(result, command_name); + return makeCommandResult(result, command_name); } - ExecutionResult CommandExecutor::operator()( + CommandResult CommandExecutor::operator()( const shared_model::interface::DetachRole &command) { - return makeExecutionResult( + return makeCommandResult( commands->deleteAccountRole(command.accountId(), command.roleName()), "DetachRole"); } - ExecutionResult CommandExecutor::operator()( + CommandResult CommandExecutor::operator()( const shared_model::interface::GrantPermission &command) { - return makeExecutionResult( + return makeCommandResult( commands->insertAccountGrantablePermission( command.accountId(), creator_account_id, command.permissionName()), "GrantPermission"); } - ExecutionResult CommandExecutor::operator()( + CommandResult CommandExecutor::operator()( const shared_model::interface::RemoveSignatory &command) { std::string command_name = "RemoveSignatory"; @@ -282,37 +285,37 @@ namespace iroha { auto result = commands->deleteAccountSignatory(command.accountId(), command.pubkey()) | [&] { return commands->deleteSignatory(command.pubkey()); }; - return makeExecutionResult(result, command_name); + return makeCommandResult(result, command_name); } - ExecutionResult CommandExecutor::operator()( + CommandResult CommandExecutor::operator()( const shared_model::interface::RevokePermission &command) { - return makeExecutionResult( + return makeCommandResult( commands->deleteAccountGrantablePermission( command.accountId(), creator_account_id, command.permissionName()), "RevokePermission"); } - ExecutionResult CommandExecutor::operator()( + CommandResult CommandExecutor::operator()( const shared_model::interface::SetAccountDetail &command) { auto creator = creator_account_id; if (creator_account_id.empty()) { // When creator is not known, it is genesis block creator = "genesis"; } - return makeExecutionResult( + return makeCommandResult( commands->setAccountKV( command.accountId(), creator, command.key(), command.value()), "SetAccountDetail"); } - ExecutionResult CommandExecutor::operator()( + CommandResult CommandExecutor::operator()( const shared_model::interface::SetQuorum &command) { std::string command_name = "SetQuorum"; auto account = queries->getAccount(command.accountId()); if (not account) { - return makeExecutionError( + return makeCommandError( (boost::format("absent account %s") % command.accountId()).str(), command_name); } @@ -325,28 +328,28 @@ namespace iroha { return account_new.match( [&](const expected::Value< std::shared_ptr> &account_new_val) - -> ExecutionResult { - return makeExecutionResult( + -> CommandResult { + return makeCommandResult( commands->updateAccount(*account_new_val.value), command_name); }, - [&command_name](const auto &error) -> ExecutionResult { - return makeExecutionError( + [&command_name](const auto &error) -> CommandResult { + return makeCommandError( "account builder failed. reason " + *error.error, command_name); }); } - ExecutionResult CommandExecutor::operator()( + CommandResult CommandExecutor::operator()( const shared_model::interface::SubtractAssetQuantity &command) { std::string command_name = "SubtractAssetQuantity"; auto asset = queries->getAsset(command.assetId()); if (not asset) { - return makeExecutionError( + return makeCommandError( (boost::format("asset %s is absent") % command.assetId()).str(), command_name); } auto precision = asset.value()->precision(); if (command.amount().precision() > precision) { - return makeExecutionError( + return makeCommandError( (boost::format("command precision is greater than asset precision: " "expected %d, but got %d") % precision % command.amount().precision()) @@ -358,10 +361,10 @@ namespace iroha { auto account_asset = queries->getAccountAsset(command.accountId(), command.assetId()); if (not account_asset) { - return makeExecutionError((boost::format("%s do not have %s") - % command.accountId() % command.assetId()) - .str(), - command_name); + return makeCommandError((boost::format("%s do not have %s") + % command.accountId() % command.assetId()) + .str(), + command_name); } auto account_asset_new = command_amount | [&account_asset](const auto &amount) { @@ -377,42 +380,42 @@ namespace iroha { return account_asset_new.match( [&](const expected::Value< std::shared_ptr> - &account_asset_new_val) -> ExecutionResult { - return makeExecutionResult( + &account_asset_new_val) -> CommandResult { + return makeCommandResult( commands->upsertAccountAsset(*account_asset_new_val.value), command_name); }, - [&command_name](const auto &error) -> ExecutionResult { - return makeExecutionError( + [&command_name](const auto &error) -> CommandResult { + return makeCommandError( "account asset builder failed. reason " + *error.error, command_name); }); } - ExecutionResult CommandExecutor::operator()( + CommandResult CommandExecutor::operator()( const shared_model::interface::TransferAsset &command) { std::string command_name = "TransferAsset"; auto src_account_asset = queries->getAccountAsset(command.srcAccountId(), command.assetId()); if (not src_account_asset) { - return makeExecutionError((boost::format("asset %s is absent of %s") - % command.assetId() % command.srcAccountId()) - .str(), - command_name); + return makeCommandError((boost::format("asset %s is absent of %s") + % command.assetId() % command.srcAccountId()) + .str(), + command_name); } auto dest_account_asset = queries->getAccountAsset(command.destAccountId(), command.assetId()); auto asset = queries->getAsset(command.assetId()); if (not asset) { - return makeExecutionError((boost::format("asset %s is absent of %s") - % command.assetId() % command.destAccountId()) - .str(), - command_name); + return makeCommandError((boost::format("asset %s is absent of %s") + % command.assetId() % command.destAccountId()) + .str(), + command_name); } auto precision = asset.value()->precision(); if (command.amount().precision() > precision) { - return makeExecutionError( + return makeCommandError( (boost::format("command precision is greater than asset precision: " "expected %d, but got %d") % precision % command.amount().precision()) @@ -456,20 +459,19 @@ namespace iroha { }; auto map_error = [&command_name](const auto &t) { - return expected::map_error( - t, [&command_name](const auto &error) -> ExecutionError { - return {"account asset builder failed. reason " + *error, - command_name}; - }); + return expected::map_error< + CommandError>(t, [&command_name](const auto &error) -> CommandError { + return {"account asset builder failed. reason " + *error, command_name}; + }); }; return (map_error(src_account_asset_new) | [&](std::shared_ptr - src_amount) -> ExecutionResult { + src_amount) -> CommandResult { return map_error(dest_account_asset_new) | [&](std::shared_ptr - dst_amount) -> ExecutionResult { - return makeExecutionResult( + dst_amount) -> CommandResult { + return makeCommandResult( commands->upsertAccountAsset(*src_amount) | [&] { return commands->upsertAccountAsset(*dst_amount); }, command_name); @@ -488,220 +490,488 @@ namespace iroha { this->creator_account_id = creator_account_id; } - bool CommandValidator::hasPermissions( + CommandResult CommandValidator::hasPermissions( const shared_model::interface::AddAssetQuantity &command, ametsuchi::WsvQuery &queries, const shared_model::interface::types::AccountIdType &creator_account_id) { + auto command_name = "AddAssetQuantity"; // Check if creator has MoneyCreator permission. // One can only add to his/her account // TODO: 03.02.2018 grimadas IR-935, Separate asset creation for distinct // asset types, now: anyone having permission "can_add_asset_qty" can add // any asset - return creator_account_id == command.accountId() - and checkAccountRolePermission( - creator_account_id, queries, Role::kAddAssetQty); + if (creator_account_id != command.accountId()) { + return makeCommandError( + "has permission command validation failed: creator account " + + creator_account_id + " is not command account " + + command.accountId(), + command_name); + } + if (not checkAccountRolePermission( + creator_account_id, queries, Role::kAddAssetQty)) { + return makeCommandError( + "has permission command validation failed: account " + + creator_account_id + " does not have permission " + + toString(Role::kAddAssetQty), + command_name); + } + return {}; } - bool CommandValidator::hasPermissions( + CommandResult CommandValidator::hasPermissions( const shared_model::interface::AddPeer &command, ametsuchi::WsvQuery &queries, const shared_model::interface::types::AccountIdType &creator_account_id) { - return checkAccountRolePermission( - creator_account_id, queries, Role::kAddPeer); + auto command_name = "AddPeer"; + if (not checkAccountRolePermission( + creator_account_id, queries, Role::kAddPeer)) { + return makeCommandError( + "has permission command validation failed: account " + + creator_account_id + " does not have permission " + + toString(Role::kAddPeer), + command_name); + } + return {}; } - bool CommandValidator::hasPermissions( + CommandResult CommandValidator::hasPermissions( const shared_model::interface::AddSignatory &command, ametsuchi::WsvQuery &queries, const shared_model::interface::types::AccountIdType &creator_account_id) { - return - // Case 1. When command creator wants to add signatory to their - // account and he has permission CanAddSignatory - (command.accountId() == creator_account_id - and checkAccountRolePermission( - creator_account_id, queries, Role::kAddSignatory)) - or - // Case 2. Creator has granted permission for it - (queries.hasAccountGrantablePermission(creator_account_id, - command.accountId(), - Grantable::kAddMySignatory)); + auto command_name = "AddSignatory"; + + auto creator_adds_signatory_to_his_account = [&creator_account_id, + &command]() { + return creator_account_id == command.accountId(); + }; + auto creator_has_permission_on_own = [&creator_account_id, &queries]() { + return checkAccountRolePermission( + creator_account_id, queries, Role::kAddSignatory); + }; + auto creator_has_grantable_permission = [&creator_account_id, + &command, + &queries]() { + return queries.hasAccountGrantablePermission( + creator_account_id, command.accountId(), Grantable::kAddMySignatory); + }; + + if (creator_adds_signatory_to_his_account()) { + if (creator_has_permission_on_own()) { + // 1. Creator adds signatory to his account, and he has permission for + // it + return {}; + } else if (creator_has_grantable_permission()) { + // 2. Creator adds signatory to his account, and he has grantable + // permission for it + return {}; + } else { + return makeCommandError( + "has permission command validation failed: account " + + creator_account_id + " does not have permission " + + toString(Role::kAddSignatory) + " for his own account", + command_name); + } + } else if (creator_has_grantable_permission()) { + // 3. Creator adds signatory to another account, and he has grantable + // permission for it + return {}; + } else { + return makeCommandError( + "has permission command validation failed: account " + + creator_account_id + " does not have permission " + + toString(Grantable::kAddMySignatory) + " for account " + + command.accountId(), + command_name); + } } - bool CommandValidator::hasPermissions( + CommandResult CommandValidator::hasPermissions( const shared_model::interface::AppendRole &command, ametsuchi::WsvQuery &queries, const shared_model::interface::types::AccountIdType &creator_account_id) { - return checkAccountRolePermission( - creator_account_id, queries, Role::kAppendRole); + auto command_name = "AppendRole"; + if (not checkAccountRolePermission( + creator_account_id, queries, Role::kAppendRole)) { + return makeCommandError( + "has permission command validation failed: account " + + creator_account_id + " does not have permission " + + toString(Role::kAppendRole), + command_name); + } + return {}; } - bool CommandValidator::hasPermissions( + CommandResult CommandValidator::hasPermissions( const shared_model::interface::CreateAccount &command, ametsuchi::WsvQuery &queries, const shared_model::interface::types::AccountIdType &creator_account_id) { - return checkAccountRolePermission( - creator_account_id, queries, Role::kCreateAccount); + auto command_name = "CreateAccount"; + if (not checkAccountRolePermission( + creator_account_id, queries, Role::kCreateAccount)) { + return makeCommandError( + "has permission command validation failed: account " + + creator_account_id + " does not have permission " + + toString(Role::kCreateAccount), + command_name); + } + return {}; } - bool CommandValidator::hasPermissions( + CommandResult CommandValidator::hasPermissions( const shared_model::interface::CreateAsset &command, ametsuchi::WsvQuery &queries, const shared_model::interface::types::AccountIdType &creator_account_id) { - return checkAccountRolePermission( - creator_account_id, queries, Role::kCreateAsset); + auto command_name = "CreateAsset"; + if (not checkAccountRolePermission( + creator_account_id, queries, Role::kCreateAsset)) { + return makeCommandError( + "has permission command validation failed: account " + + creator_account_id + " does not have permission " + + toString(Role::kCreateAsset), + command_name); + } + return {}; } - bool CommandValidator::hasPermissions( + CommandResult CommandValidator::hasPermissions( const shared_model::interface::CreateDomain &command, ametsuchi::WsvQuery &queries, const shared_model::interface::types::AccountIdType &creator_account_id) { - return checkAccountRolePermission( - creator_account_id, queries, Role::kCreateDomain); + auto command_name = "CreateDomain"; + if (not checkAccountRolePermission( + creator_account_id, queries, Role::kCreateDomain)) { + return makeCommandError( + "has permission command validation failed: account " + + creator_account_id + " does not have permission " + + toString(Role::kCreateDomain), + command_name); + } + return {}; } - bool CommandValidator::hasPermissions( + CommandResult CommandValidator::hasPermissions( const shared_model::interface::CreateRole &command, ametsuchi::WsvQuery &queries, const shared_model::interface::types::AccountIdType &creator_account_id) { - return checkAccountRolePermission( - creator_account_id, queries, Role::kCreateRole); + auto command_name = "CreateRole"; + if (not checkAccountRolePermission( + creator_account_id, queries, Role::kCreateRole)) { + return makeCommandError( + "has permission command validation failed: account " + + creator_account_id + " does not have permission " + + toString(Role::kCreateRole), + command_name); + } + return {}; } - bool CommandValidator::hasPermissions( + CommandResult CommandValidator::hasPermissions( const shared_model::interface::DetachRole &command, ametsuchi::WsvQuery &queries, const shared_model::interface::types::AccountIdType &creator_account_id) { - return checkAccountRolePermission( - creator_account_id, queries, Role::kDetachRole); + auto command_name = "DetachRole"; + if (not checkAccountRolePermission( + creator_account_id, queries, Role::kDetachRole)) { + return makeCommandError( + "has permission command validation failed: account " + + creator_account_id + " does not have permission " + + toString(Role::kDetachRole), + command_name); + } + return {}; } - bool CommandValidator::hasPermissions( + CommandResult CommandValidator::hasPermissions( const shared_model::interface::GrantPermission &command, ametsuchi::WsvQuery &queries, const shared_model::interface::types::AccountIdType &creator_account_id) { - return checkAccountRolePermission( - creator_account_id, - queries, - shared_model::interface::permissions::permissionFor( - command.permissionName())); + auto command_name = "GrantPermission"; + if (not checkAccountRolePermission( + creator_account_id, + queries, + shared_model::interface::permissions::permissionFor( + command.permissionName()))) { + return makeCommandError( + "has permission command validation failed: account " + + creator_account_id + " does not have grantable permission " + + toString(command.permissionName()) + " to grant", + command_name); + } + return {}; } - bool CommandValidator::hasPermissions( + CommandResult CommandValidator::hasPermissions( const shared_model::interface::RemoveSignatory &command, ametsuchi::WsvQuery &queries, const shared_model::interface::types::AccountIdType &creator_account_id) { - return - // 1. Creator removes signatory from their account, and he must have - // permission on it - (creator_account_id == command.accountId() - and checkAccountRolePermission( - creator_account_id, queries, Role::kRemoveSignatory)) - // 2. Creator has granted permission on removal - or (queries.hasAccountGrantablePermission( - creator_account_id, - command.accountId(), - Grantable::kRemoveMySignatory)); + auto command_name = "RemoveSignatory"; + + auto creator_removes_signatory_from_his_account = [&creator_account_id, + &command]() { + return creator_account_id == command.accountId(); + }; + auto creator_has_permission_on_own = [&creator_account_id, &queries]() { + return checkAccountRolePermission( + creator_account_id, queries, Role::kRemoveSignatory); + }; + auto creator_has_grantable_permission = + [&creator_account_id, &command, &queries]() { + return queries.hasAccountGrantablePermission( + creator_account_id, + command.accountId(), + Grantable::kRemoveMySignatory); + }; + + if (creator_removes_signatory_from_his_account()) { + if (creator_has_permission_on_own()) { + // 1. Creator removes signatory from his account, and he has permission + // for it + return {}; + } else if (creator_has_grantable_permission()) { + // 2. Creator removes signatory from his account, and he has grantable + // permission for it + return {}; + } else { + return makeCommandError( + "has permission command validation failed: account " + + creator_account_id + " does not have permission " + + toString(Role::kRemoveSignatory) + " for his own account", + command_name); + } + } else if (creator_has_grantable_permission()) { + // 3. Creator removes signatory from another account, and he has grantable + // permission for it + return {}; + } else { + return makeCommandError( + "has permission command validation failed: account " + + creator_account_id + " does not have permission " + + toString(Grantable::kRemoveMySignatory) + " for account " + + command.accountId(), + command_name); + } } - bool CommandValidator::hasPermissions( + CommandResult CommandValidator::hasPermissions( const shared_model::interface::RevokePermission &command, ametsuchi::WsvQuery &queries, const shared_model::interface::types::AccountIdType &creator_account_id) { - return queries.hasAccountGrantablePermission( - command.accountId(), creator_account_id, command.permissionName()); + auto command_name = "RevokePermission"; + if (not queries.hasAccountGrantablePermission(command.accountId(), + creator_account_id, + command.permissionName())) { + return makeCommandError( + "has permission command validation failed: account " + + creator_account_id + " does not have grantable permission " + + toString(command.permissionName()) + " to revoke", + command_name); + } + return {}; } - bool CommandValidator::hasPermissions( + CommandResult CommandValidator::hasPermissions( const shared_model::interface::SetAccountDetail &command, ametsuchi::WsvQuery &queries, const shared_model::interface::types::AccountIdType &creator_account_id) { - return - // Case 1. Creator set details for his account + auto command_name = "SetAccountDetail"; + + if ( + // Case 1. Creator sets details for his account creator_account_id == command.accountId() + // Case 2. Creator has permission to set account key/value or checkAccountRolePermission( creator_account_id, queries, Role::kSetDetail) - or - // Case 2. Creator has grantable permission to set account key/value - queries.hasAccountGrantablePermission(creator_account_id, - command.accountId(), - Grantable::kSetMyAccountDetail); + // Case 3. Creator has grantable permission to set account key/value + or queries.hasAccountGrantablePermission( + creator_account_id, + command.accountId(), + Grantable::kSetMyAccountDetail)) { + return {}; + } + + return makeCommandError("has permission command validation failed: account " + + creator_account_id + + " tries to set details for account " + + command.accountId() + ", but has neither " + + toString(Role::kSetDetail) + " nor grantable " + + toString(Grantable::kSetMyAccountDetail), + command_name); } - bool CommandValidator::hasPermissions( + CommandResult CommandValidator::hasPermissions( const shared_model::interface::SetQuorum &command, ametsuchi::WsvQuery &queries, const shared_model::interface::types::AccountIdType &creator_account_id) { - return - // 1. Creator set quorum for his account -> must have permission - (creator_account_id == command.accountId() - and checkAccountRolePermission( - creator_account_id, queries, Role::kSetQuorum)) - // 2. Creator has granted permission on it - or (queries.hasAccountGrantablePermission(creator_account_id, - command.accountId(), - Grantable::kSetMyQuorum)); + auto command_name = "SetQuorum"; + + auto creator_sets_quorum_for_his_account = [&creator_account_id, + &command]() { + return creator_account_id == command.accountId(); + }; + auto creator_has_permission_on_own = [&creator_account_id, &queries]() { + return checkAccountRolePermission( + creator_account_id, queries, Role::kSetQuorum); + }; + auto creator_has_grantable_permission = + [&creator_account_id, &command, &queries]() { + return queries.hasAccountGrantablePermission( + creator_account_id, command.accountId(), Grantable::kSetMyQuorum); + }; + + if (creator_sets_quorum_for_his_account()) { + if (creator_has_permission_on_own()) { + // 1. Creator has permission quorum for his account + return {}; + } else if (creator_has_grantable_permission()) { + // 2. Creator has grantable permission for his account + return {}; + } else { + return makeCommandError( + "has permission command validation failed: account " + + creator_account_id + " does not have permission " + + toString(Role::kSetQuorum) + " for his own account", + command_name); + } + } else if (creator_has_grantable_permission()) { + // 3. Creator has grantable permission for another account + return {}; + } else { + return makeCommandError( + "has permission command validation failed: account " + + creator_account_id + " does not have permission " + + toString(Grantable::kSetMyQuorum) + " for account " + + command.accountId(), + command_name); + } } - bool CommandValidator::hasPermissions( + CommandResult CommandValidator::hasPermissions( const shared_model::interface::SubtractAssetQuantity &command, ametsuchi::WsvQuery &queries, const shared_model::interface::types::AccountIdType &creator_account_id) { - return creator_account_id == command.accountId() - and checkAccountRolePermission( - creator_account_id, queries, Role::kSubtractAssetQty); + auto command_name = "SubtractAssetQuantity"; + if (creator_account_id == command.accountId()) { + if (checkAccountRolePermission( + creator_account_id, queries, Role::kSubtractAssetQty)) { + return {}; + } else { + return makeCommandError( + "has permission command validation failed: account " + + creator_account_id + " does not have permission " + + toString(Role::kSubtractAssetQty) + " for his own account", + command_name); + } + } else { + return makeCommandError( + "has permission command validation failed: account " + + creator_account_id + + " cannot subtract asset quantity from account " + + command.accountId(), + command_name); + } } - bool CommandValidator::hasPermissions( + CommandResult CommandValidator::hasPermissions( const shared_model::interface::TransferAsset &command, ametsuchi::WsvQuery &queries, const shared_model::interface::types::AccountIdType &creator_account_id) { - return ( - // 1. Creator has granted permission on src_account_id - (creator_account_id != command.srcAccountId() - and queries.hasAccountGrantablePermission( - creator_account_id, - command.srcAccountId(), - Grantable::kTransferMyAssets)) - or - // 2. Creator transfer from their account - (creator_account_id == command.srcAccountId() - and checkAccountRolePermission( - creator_account_id, queries, Role::kTransfer))) - // For both cases, dest_account must have can_receive - and checkAccountRolePermission( - command.destAccountId(), queries, Role::kReceive); - } - - bool CommandValidator::isValid( + auto command_name = "TransferAsset"; + + auto creator_transfers_from_his_account = [&creator_account_id, + &command]() { + return creator_account_id == command.srcAccountId(); + }; + auto creator_has_permission_on_own = [&creator_account_id, &queries]() { + return checkAccountRolePermission( + creator_account_id, queries, Role::kTransfer); + }; + auto creator_has_grantable_permission = + [&creator_account_id, &command, &queries]() { + return queries.hasAccountGrantablePermission( + creator_account_id, + command.srcAccountId(), + Grantable::kTransferMyAssets); + }; + auto dest_can_receive = [&command, &queries]() { + return checkAccountRolePermission( + command.destAccountId(), queries, Role::kReceive); + }; + + if (dest_can_receive()) { + if (not creator_transfers_from_his_account()) { + if (creator_has_grantable_permission()) { + // 1. Creator has grantable permission on src_account_id + return {}; + } else { + return makeCommandError( + "has permission command validation failed: account " + + creator_account_id + " does not have " + + toString(Grantable::kTransferMyAssets) + " for account " + + command.srcAccountId(), + command_name); + } + } else { + if (creator_has_permission_on_own()) { + // 2. Creator transfers from their account + return {}; + } else { + return makeCommandError( + "has permission command validation failed: account " + + creator_account_id + " does not have " + + toString(Role::kTransfer) + " for his own account", + command_name); + } + } + } else { + // For both cases, dest_account must have can_receive + return makeCommandError( + "has permission command validation failed: destination account " + + command.destAccountId() + " does not have " + + toString(Role::kReceive), + command_name); + } + } + + CommandResult CommandValidator::isValid( const shared_model::interface::AddAssetQuantity &command, ametsuchi::WsvQuery &queries, const shared_model::interface::types::AccountIdType &creator_account_id) { - return true; + return {}; } - bool CommandValidator::isValid( + CommandResult CommandValidator::isValid( const shared_model::interface::AddPeer &command, ametsuchi::WsvQuery &queries, const shared_model::interface::types::AccountIdType &creator_account_id) { - return true; + return {}; } - bool CommandValidator::isValid( + CommandResult CommandValidator::isValid( const shared_model::interface::AddSignatory &command, ametsuchi::WsvQuery &queries, const shared_model::interface::types::AccountIdType &creator_account_id) { - return true; + return {}; } - bool CommandValidator::isValid( + CommandResult CommandValidator::isValid( const shared_model::interface::AppendRole &command, ametsuchi::WsvQuery &queries, const shared_model::interface::types::AccountIdType &creator_account_id) { + auto command_name = "AppendRole"; auto role_permissions = queries.getRolePermissions(command.roleName()); auto account_roles = queries.getAccountRoles(creator_account_id); - if (not role_permissions or not account_roles) { - return false; + if (not role_permissions) { + return makeCommandError( + "is valid command validation failed: no permissions in role " + + command.roleName(), + command_name); + } + if (not account_roles) { + return makeCommandError( + "is valid command validation failed: no roles in account " + + creator_account_id, + command_name); } shared_model::interface::RolePermissionSet account_permissions{}; @@ -712,133 +982,233 @@ namespace iroha { account_permissions |= *permissions; } - return role_permissions->isSubsetOf(account_permissions); + if (not role_permissions->isSubsetOf(account_permissions)) { + auto missing_permissions = ""s; + role_permissions.value().iterate( + [&account_permissions, &missing_permissions](const auto &perm) { + if (not account_permissions.test(perm)) { + missing_permissions += toString(perm) + ", "; + } + }); + + return makeCommandError( + "is valid command validation failed: account " + creator_account_id + + " does not have some of the permissions in a role " + + command.roleName() + + " he wants to append; such permissions are " + + missing_permissions, + command_name); + } + return {}; } - bool CommandValidator::isValid( + CommandResult CommandValidator::isValid( const shared_model::interface::CreateAccount &command, ametsuchi::WsvQuery &queries, const shared_model::interface::types::AccountIdType &creator_account_id) { - return true; + return {}; } - bool CommandValidator::isValid( + CommandResult CommandValidator::isValid( const shared_model::interface::CreateAsset &command, ametsuchi::WsvQuery &queries, const shared_model::interface::types::AccountIdType &creator_account_id) { - return true; + return {}; } - bool CommandValidator::isValid( + CommandResult CommandValidator::isValid( const shared_model::interface::CreateDomain &command, ametsuchi::WsvQuery &queries, const shared_model::interface::types::AccountIdType &creator_account_id) { - return true; + return {}; } - bool CommandValidator::isValid( + CommandResult CommandValidator::isValid( const shared_model::interface::CreateRole &command, ametsuchi::WsvQuery &queries, const shared_model::interface::types::AccountIdType &creator_account_id) { + auto command_name = "CreateRole"; auto set = command.rolePermissions(); + auto missing_permissions = ""s; for (size_t i = 0; i < set.size(); ++i) { auto perm = static_cast(i); if (set.test(perm) - && not checkAccountRolePermission( - creator_account_id, queries, perm)) { - return false; + and not checkAccountRolePermission( + creator_account_id, queries, perm)) { + missing_permissions += toString(perm) + ", "; } } - return true; + if (not missing_permissions.empty()) { + return makeCommandError( + "is valid command validation failed: account " + creator_account_id + + " does not have some of the permissions from a role " + + command.roleName() + + " he wants to create; such permissions are " + + missing_permissions, + command_name); + } + return {}; } - bool CommandValidator::isValid( + CommandResult CommandValidator::isValid( const shared_model::interface::DetachRole &command, ametsuchi::WsvQuery &queries, const shared_model::interface::types::AccountIdType &creator_account_id) { - return true; + return {}; } - bool CommandValidator::isValid( + CommandResult CommandValidator::isValid( const shared_model::interface::GrantPermission &command, ametsuchi::WsvQuery &queries, const shared_model::interface::types::AccountIdType &creator_account_id) { - return true; + return {}; } - bool CommandValidator::isValid( + CommandResult CommandValidator::isValid( const shared_model::interface::RemoveSignatory &command, ametsuchi::WsvQuery &queries, const shared_model::interface::types::AccountIdType &creator_account_id) { + auto command_name = "RemoveSignatory"; auto account = queries.getAccount(command.accountId()); auto signatories = queries.getSignatories(command.accountId()); - if (not(account and signatories)) { - // No account or signatories found - return false; + if (not account) { + return makeCommandError("is valid command validation failed: no account " + + command.accountId() + " found", + command_name); } + if (not signatories) { + return makeCommandError( + "is valid command validation failed: no signatories in account " + + command.accountId() + " found", + command_name); + } + auto newSignatoriesSize = signatories.value().size() - 1; // You can't remove if size of rest signatories less than the quorum - return newSignatoriesSize >= account.value()->quorum(); + if (newSignatoriesSize < account.value()->quorum()) { + return makeCommandError( + "is valid command validation failed: size of rest signatories " + "becomes less than the quorum; account id " + + command.accountId() + ", quorum " + + std::to_string(account.value()->quorum()) + + ", new desired size " + std::to_string(newSignatoriesSize), + command_name); + } + + return {}; } - bool CommandValidator::isValid( + CommandResult CommandValidator::isValid( const shared_model::interface::RevokePermission &command, ametsuchi::WsvQuery &queries, const shared_model::interface::types::AccountIdType &creator_account_id) { - return true; + return {}; } - bool CommandValidator::isValid( + CommandResult CommandValidator::isValid( const shared_model::interface::SetAccountDetail &command, ametsuchi::WsvQuery &queries, const shared_model::interface::types::AccountIdType &creator_account_id) { - return true; + return {}; } - bool CommandValidator::isValid( + CommandResult CommandValidator::isValid( const shared_model::interface::SetQuorum &command, ametsuchi::WsvQuery &queries, const shared_model::interface::types::AccountIdType &creator_account_id) { + auto command_name = "SetQuorum"; auto signatories = queries.getSignatories(command.accountId()); if (not(signatories)) { // No signatories of an account found - return false; + return makeCommandError( + "is valid command validation failed: no signatories of an account " + + command.accountId() + " found", + command_name); + } + if (command.newQuorum() <= 0 or command.newQuorum() >= 10) { + return makeCommandError( + "is valid command validation failed: account's " + + command.accountId() + + " new quorum size is " + "out of bounds; " + "value is " + std::to_string(command.newQuorum()), + command_name); } // You can't remove if size of rest signatories less than the quorum - return command.newQuorum() > 0 and command.newQuorum() < 10 - and signatories.value().size() >= command.newQuorum(); + if (signatories.value().size() < command.newQuorum()) { + return makeCommandError( + "is valid command validation failed: account's" + + command.accountId() + + " new quorum size " + "is greater than " + "the signatories amount; " + + std::to_string(command.newQuorum()) + + " vs " + std::to_string(signatories.value().size()), + command_name); + } + + return {}; } - bool CommandValidator::isValid( + CommandResult CommandValidator::isValid( const shared_model::interface::SubtractAssetQuantity &command, ametsuchi::WsvQuery &queries, const shared_model::interface::types::AccountIdType &creator_account_id) { - return true; + return {}; } - bool CommandValidator::isValid( + CommandResult CommandValidator::isValid( const shared_model::interface::TransferAsset &command, ametsuchi::WsvQuery &queries, const shared_model::interface::types::AccountIdType &creator_account_id) { + auto command_name = "TransferAsset"; auto asset = queries.getAsset(command.assetId()); if (not asset) { - return false; + return makeCommandError("is valid command validation failed: account " + + command.srcAccountId() + + ", no asset with such id " + + command.assetId(), + command_name); } // Amount is formed wrong if (command.amount().precision() > asset.value()->precision()) { - return false; + return makeCommandError( + + "is valid command validation failed: account " + + command.srcAccountId() + + ", precision of command's " + "asset " + "amount is greater than the actual asset's one; " + std::to_string(command.amount().precision()) + " vs " + std::to_string(asset.value()->precision()), + command_name); } auto account_asset = queries.getAccountAsset(command.srcAccountId(), command.assetId()); if (not account_asset) { - return false; + return makeCommandError( + "is valid command validation failed: asset " + command.assetId() + + " does not exist on account " + command.srcAccountId(), + command_name); } // Check if dest account exist - return queries.getAccount(command.destAccountId()) and - // Balance in your wallet should be at least amount of transfer - compareAmount(account_asset.value()->balance(), command.amount()) >= 0; + if (not queries.getAccount(command.destAccountId())) { + return makeCommandError( + "is valid command validation failed: destination account with id " + + command.destAccountId() + " does not exist", + command_name); + } + // Balance in your wallet should be at least amount of transfer + if (compareAmount(account_asset.value()->balance(), command.amount()) < 0) { + return makeCommandError( + "is valid command validation failed: not enough " + "balance on account " + + command.srcAccountId() + "; transfer amount " + + command.amount().toStringRepr() + ", balance " + + account_asset.value()->balance().toStringRepr(), + command_name); + } + return {}; } } // namespace iroha diff --git a/test/module/irohad/execution/command_validate_execute_test.cpp b/test/module/irohad/execution/command_validate_execute_test.cpp index efc0f77abc..0417c243b9 100644 --- a/test/module/irohad/execution/command_validate_execute_test.cpp +++ b/test/module/irohad/execution/command_validate_execute_test.cpp @@ -137,18 +137,22 @@ class CommandValidateExecuteTest : public ::testing::Test { }); } - iroha::ExecutionResult validateAndExecute( + iroha::CommandResult validateAndExecute( const std::unique_ptr &command) { validator->setCreatorAccountId(creator->accountId()); - if (boost::apply_visitor(*validator, command->get())) { - return execute(command); - } - return expected::makeError( - iroha::ExecutionError{"Validate", "validation of a command failed"}); + return boost::apply_visitor(*validator, command->get()) + .match( + [this, &command](expected::Value &) -> iroha::CommandResult { + return execute(command); + }, + [](const auto &) -> iroha::CommandResult { + return expected::makeError(iroha::CommandError{ + "Validate", "validation of a command failed"}); + }); } - iroha::ExecutionResult execute( + iroha::CommandResult execute( const std::unique_ptr &command) { executor->setCreatorAccountId(creator->accountId()); return boost::apply_visitor(*executor, command->get()); @@ -160,9 +164,9 @@ class CommandValidateExecuteTest : public ::testing::Test { } /// Returns error from result or throws error in case result contains value - iroha::ExecutionResult::ErrorType checkErrorCase( - const iroha::ExecutionResult &result) { - return boost::get(result); + iroha::CommandResult::ErrorType checkErrorCase( + const iroha::CommandResult &result) { + return boost::get(result); } const std::string kMaxAmountStr = @@ -1381,6 +1385,11 @@ TEST_F(TransferAssetTest, InvalidWhenNoPermissions) { EXPECT_CALL(*wsv_query, getAccountRoles(transfer_asset->srcAccountId())) .WillOnce(Return(boost::none)); + EXPECT_CALL(*wsv_query, getAccountRoles(transfer_asset->destAccountId())) + .WillOnce(Return(admin_roles)); + EXPECT_CALL(*wsv_query, getRolePermissions(kAdminRole)) + .WillOnce(Return(role_permissions)); + ASSERT_TRUE(err(validateAndExecute(command))); } @@ -1399,11 +1408,6 @@ TEST_F(TransferAssetTest, InvalidWhenNoDestAccount) { EXPECT_CALL(*wsv_query, getAccountRoles(transfer_asset->destAccountId())) .WillOnce(Return(boost::none)); - EXPECT_CALL(*wsv_query, getAccountRoles(transfer_asset->srcAccountId())) - .WillOnce(Return(admin_roles)); - EXPECT_CALL(*wsv_query, getRolePermissions(kAdminRole)) - .WillOnce(Return(role_permissions)); - ASSERT_TRUE(err(validateAndExecute(command))); } @@ -1669,6 +1673,11 @@ TEST_F(TransferAssetTest, InvalidWhenCreatorHasNoPermission) { auto transfer_asset = getConcreteCommand(command); + EXPECT_CALL(*wsv_query, getAccountRoles(transfer_asset->destAccountId())) + .WillOnce(Return(admin_roles)); + EXPECT_CALL(*wsv_query, getRolePermissions(kAdminRole)) + .WillOnce(Return(role_permissions)); + EXPECT_CALL(*wsv_query, hasAccountGrantablePermission( kAdminId, kAccountId, Grantable::kTransferMyAssets)) From 5513d2a61a7aaccfe2c388dc1299e196bfc0f44f Mon Sep 17 00:00:00 2001 From: Kitsu Date: Mon, 25 Jun 2018 15:28:44 +0300 Subject: [PATCH 03/97] Refactor TransferAsset acceptance test (#1482) - Add IntegrationTestFramework::sendTxAwait - Reuse AcceptanceFixture primitive in TA test - Move out primitive methods in fixture - Fix (and rename accordingly) WithOnlyCanTransferPerm/WithOnlyCanReceivePerm tests - Rename tx_counter in AcceptanceFixture Signed-off-by: Kitsu --- .../integration_test_framework.cpp | 7 + .../integration_test_framework.hpp | 11 + .../acceptance/acceptance_fixture.cpp | 6 +- .../acceptance/acceptance_fixture.hpp | 2 +- .../acceptance/transfer_asset_test.cpp | 456 ++++++------------ 5 files changed, 173 insertions(+), 309 deletions(-) diff --git a/test/framework/integration_framework/integration_test_framework.cpp b/test/framework/integration_framework/integration_test_framework.cpp index 8b2b1664d4..c89171a81a 100644 --- a/test/framework/integration_framework/integration_test_framework.cpp +++ b/test/framework/integration_framework/integration_test_framework.cpp @@ -195,6 +195,13 @@ namespace integration_framework { return *this; } + IntegrationTestFramework &IntegrationTestFramework::sendTxAwait( + const shared_model::proto::Transaction &tx, + std::function check) { + sendTx(tx).skipProposal().checkBlock(check); + return *this; + } + IntegrationTestFramework &IntegrationTestFramework::sendQuery( const shared_model::proto::Query &qry, std::function diff --git a/test/framework/integration_framework/integration_test_framework.hpp b/test/framework/integration_framework/integration_test_framework.hpp index 26d35a9cea..6f230b37ac 100644 --- a/test/framework/integration_framework/integration_test_framework.hpp +++ b/test/framework/integration_framework/integration_test_framework.hpp @@ -139,6 +139,17 @@ namespace integration_framework { IntegrationTestFramework &sendTx( const shared_model::proto::Transaction &tx); + /** + * Send transaction to Iroha with awaiting proposal + * and without status validation + * @param tx - transaction for sending + * @param check - callback for checking committed block + * @return this + */ + IntegrationTestFramework &sendTxAwait( + const shared_model::proto::Transaction &tx, + std::function check); + /** * Check current status of transaction * @param hash - hash of transaction to check diff --git a/test/integration/acceptance/acceptance_fixture.cpp b/test/integration/acceptance/acceptance_fixture.cpp index c15505f390..dd165ffd29 100644 --- a/test/integration/acceptance/acceptance_fixture.cpp +++ b/test/integration/acceptance/acceptance_fixture.cpp @@ -29,7 +29,7 @@ AcceptanceFixture::AcceptanceFixture() status.get())); }), initial_time(iroha::time::now()), - tx_counter(0){} + nonce_counter(0) {} TestUnsignedTransactionBuilder AcceptanceFixture::createUser( const std::string &user, const shared_model::crypto::PublicKey &key) { @@ -94,7 +94,7 @@ auto AcceptanceFixture::baseTx() auto AcceptanceFixture::baseQry() -> decltype(base(TestUnsignedQueryBuilder())) { - return base(TestUnsignedQueryBuilder()); + return base(TestUnsignedQueryBuilder()).queryCounter(nonce_counter); } template @@ -119,5 +119,5 @@ template auto AcceptanceFixture::complete( .finish()); iroha::time::time_t AcceptanceFixture::getUniqueTime() { - return initial_time + tx_counter++; + return initial_time + nonce_counter++; } diff --git a/test/integration/acceptance/acceptance_fixture.hpp b/test/integration/acceptance/acceptance_fixture.hpp index 16698008d6..59e23860a3 100644 --- a/test/integration/acceptance/acceptance_fixture.hpp +++ b/test/integration/acceptance/acceptance_fixture.hpp @@ -122,7 +122,7 @@ class AcceptanceFixture : public ::testing::Test { private: iroha::time::time_t initial_time; /// number of created transactions, used to provide unique time - int tx_counter; + int nonce_counter; }; #endif // IROHA_ACCEPTANCE_FIXTURE_HPP diff --git a/test/integration/acceptance/transfer_asset_test.cpp b/test/integration/acceptance/transfer_asset_test.cpp index 196e97426d..9f57f8270b 100644 --- a/test/integration/acceptance/transfer_asset_test.cpp +++ b/test/integration/acceptance/transfer_asset_test.cpp @@ -19,159 +19,105 @@ using namespace shared_model; class TransferAsset : public AcceptanceFixture { public: /** - * Creates the transaction with the user creation commands + * Creates the transaction with the first user creation commands * @param perms are the permissions of the user - * @return built tx and a hash of its payload + * @return built tx */ - auto makeUserWithPerms(const std::string &user, - const crypto::Keypair &key, - const interface::RolePermissionSet &perms, - const std::string &role) { - return createUserWithPerms(user, key.publicKey(), role, perms) + auto makeFirstUser(const interface::RolePermissionSet &perms = { + interface::permissions::Role::kTransfer}) { + auto new_perms = perms; + new_perms.set(interface::permissions::Role::kAddAssetQty); + const std::string kRole1 = "roleone"; + return AcceptanceFixture::makeUserWithPerms(kRole1, new_perms); + } + + /** + * Creates the transaction with the second user creation commands + * @param perms are the permissions of the user + * @return built tx + */ + auto makeSecondUser(const interface::RolePermissionSet &perms = { + interface::permissions::Role::kReceive}) { + return createUserWithPerms(kUser2, kUser2Keypair.publicKey(), kRole2, perms) .build() .signAndAddSignature(kAdminKeypair) .finish(); } - proto::Transaction addAssets(const std::string &user, - const crypto::Keypair &key) { - return addAssets(user, key, kAmount); + proto::Transaction addAssets() { + return addAssets(kAmount); } - proto::Transaction addAssets(const std::string &user, - const crypto::Keypair &key, - const std::string &amount) { - const std::string kUserId = user + "@test"; - return proto::TransactionBuilder() - .creatorAccountId(kUserId) - .createdTime(getUniqueTime()) - .addAssetQuantity(kUserId, kAsset, amount) - .quorum(1) - .build() - .signAndAddSignature(key) - .finish(); + proto::Transaction addAssets(const std::string &amount) { + return complete(baseTx().addAssetQuantity(kUserId, kAsset, amount)); } - /** - * Create valid base pre-build transaction - * @return pre-build tx - */ - auto baseTx() { - return TestUnsignedTransactionBuilder() - .creatorAccountId(kUser1 + "@test") - .createdTime(getUniqueTime()) - .quorum(1); + proto::Transaction makeTransfer(const std::string &amount) { + return complete( + baseTx().transferAsset(kUserId, kUser2Id, kAsset, kDesc, amount)); } - /** - * Completes pre-build transaction - * @param builder is a pre-built tx - * @return built tx - */ - template - auto completeTx(TestTransactionBuilder builder) { - return builder.build().signAndAddSignature(kUser1Keypair).finish(); + proto::Transaction makeTransfer() { + return makeTransfer(kAmount); } const std::string kAmount = "1.0"; const std::string kDesc = "description"; - const std::string kUser1 = "userone"; - const std::string kUser2 = "usertwo"; - const std::string kRole1 = "roleone"; const std::string kRole2 = "roletwo"; - const std::string kUser1Id = kUser1 + "@test"; + const std::string kUser2 = "usertwo"; const std::string kUser2Id = kUser2 + "@test"; - const crypto::Keypair kUser1Keypair = - crypto::DefaultCryptoAlgorithmType::generateKeypair(); const crypto::Keypair kUser2Keypair = crypto::DefaultCryptoAlgorithmType::generateKeypair(); - const interface::RolePermissionSet kPerms{ - interface::permissions::Role::kAddAssetQty, - interface::permissions::Role::kTransfer, - interface::permissions::Role::kReceive}; }; +#define check(i) [](auto &block) { ASSERT_EQ(block->transactions().size(), i); } + /** - * @given some user with all required permissions + * @given pair of users with all required permissions * @when execute tx with TransferAsset command * @then there is the tx in proposal */ TEST_F(TransferAsset, Basic) { IntegrationTestFramework(1) .setInitialState(kAdminKeypair) - .sendTx(makeUserWithPerms(kUser1, kUser1Keypair, kPerms, kRole1)) - .skipProposal() - .skipBlock() - .sendTx(makeUserWithPerms(kUser2, kUser2Keypair, kPerms, kRole2)) - .skipProposal() - .skipBlock() - .sendTx(addAssets(kUser1, kUser1Keypair)) - .skipProposal() - .skipBlock() - .sendTx(completeTx( - baseTx().transferAsset(kUser1Id, kUser2Id, kAsset, kDesc, kAmount))) - .checkBlock( - [](auto &block) { ASSERT_EQ(block->transactions().size(), 1); }) + .sendTxAwait(makeFirstUser(), check(1)) + .sendTxAwait(makeSecondUser(), check(1)) + .sendTxAwait(addAssets(), check(1)) + .sendTxAwait(makeTransfer(), check(1)) .done(); } /** - * @given some user with only can_transfer permission + * @given pair of users + * AND the first user without can_transfer permission * @when execute tx with TransferAsset command * @then there is an empty proposal */ -TEST_F(TransferAsset, WithOnlyCanTransferPerm) { +TEST_F(TransferAsset, WithoutCanTransfer) { IntegrationTestFramework(1) .setInitialState(kAdminKeypair) - .sendTx(makeUserWithPerms(kUser1, - kUser1Keypair, - {interface::permissions::Role::kTransfer}, - kRole1)) - .skipProposal() - .skipBlock() - .sendTx(makeUserWithPerms(kUser2, kUser2Keypair, kPerms, kRole2)) - .skipProposal() - .skipBlock() - .sendTx(addAssets(kUser1, kUser1Keypair)) - .skipProposal() - .skipBlock() - .sendTx(baseTx() - .transferAsset(kUser1Id, kUser2Id, kAsset, kDesc, kAmount) - .build() - .signAndAddSignature(kUser1Keypair) - .finish()) - .checkBlock( - [](auto &block) { ASSERT_EQ(block->transactions().size(), 0); }) + .sendTxAwait(makeFirstUser({}), check(1)) + .sendTxAwait(makeSecondUser(), check(1)) + .sendTxAwait(addAssets(), check(1)) + .sendTxAwait(makeTransfer(), check(0)) .done(); } /** - * @given some user with only can_receive permission + * @given pair of users + * AND the second user without can_receive permission * @when execute tx with TransferAsset command * @then there is an empty proposal */ -TEST_F(TransferAsset, WithOnlyCanReceivePerm) { +TEST_F(TransferAsset, WithoutCanReceive) { IntegrationTestFramework(1) .setInitialState(kAdminKeypair) - .sendTx(makeUserWithPerms(kUser1, - kUser1Keypair, - {interface::permissions::Role::kReceive}, - kRole1)) - .skipProposal() - .skipBlock() - .sendTx(makeUserWithPerms(kUser2, kUser2Keypair, kPerms, kRole2)) - .skipProposal() - .skipBlock() - .sendTx(addAssets(kUser1, kUser1Keypair)) - .skipProposal() - .skipBlock() - .sendTx(baseTx() - .transferAsset(kUser1Id, kUser2Id, kAsset, kDesc, kAmount) - .build() - .signAndAddSignature(kUser1Keypair) - .finish()) - .checkBlock( - [](auto &block) { ASSERT_EQ(block->transactions().size(), 0); }) + .sendTxAwait(makeFirstUser(), check(1)) + // TODO(@l4l) 23/06/18: remove permission with IR-1367 + .sendTxAwait(makeSecondUser({interface::permissions::Role::kAddPeer}), + check(1)) + .sendTxAwait(addAssets(), check(1)) + .sendTxAwait(makeTransfer(), check(0)) .done(); } @@ -184,19 +130,11 @@ TEST_F(TransferAsset, NonexistentDest) { std::string nonexistent = "inexist@test"; IntegrationTestFramework(1) .setInitialState(kAdminKeypair) - .sendTx(makeUserWithPerms(kUser1, kUser1Keypair, kPerms, kRole1)) - .skipProposal() - .skipBlock() - .sendTx(addAssets(kUser1, kUser1Keypair)) - .skipProposal() - .skipBlock() - .sendTx(baseTx() - .transferAsset(kUser1Id, nonexistent, kAsset, kDesc, kAmount) - .build() - .signAndAddSignature(kUser1Keypair) - .finish()) - .checkBlock( - [](auto &block) { ASSERT_EQ(block->transactions().size(), 0); }) + .sendTxAwait(makeFirstUser(), check(1)) + .sendTxAwait(addAssets(), check(1)) + .sendTxAwait(complete(baseTx().transferAsset( + kUserId, nonexistent, kAsset, kDesc, kAmount)), + check(0)) .done(); } @@ -209,23 +147,12 @@ TEST_F(TransferAsset, NonexistentAsset) { std::string nonexistent = "inexist#test"; IntegrationTestFramework(1) .setInitialState(kAdminKeypair) - .sendTx(makeUserWithPerms(kUser1, kUser1Keypair, kPerms, kRole1)) - .skipProposal() - .skipBlock() - .sendTx(makeUserWithPerms(kUser2, kUser2Keypair, kPerms, kRole2)) - .skipProposal() - .skipBlock() - .sendTx(addAssets(kUser1, kUser1Keypair)) - .skipProposal() - .skipBlock() - .sendTx( - baseTx() - .transferAsset(kUser1Id, kUser2Id, nonexistent, kDesc, kAmount) - .build() - .signAndAddSignature(kUser1Keypair) - .finish()) - .checkBlock( - [](auto &block) { ASSERT_EQ(block->transactions().size(), 0); }) + .sendTxAwait(makeFirstUser(), check(1)) + .sendTxAwait(makeSecondUser(), check(1)) + .sendTxAwait(addAssets(), check(1)) + .sendTxAwait(complete(baseTx().transferAsset( + kUserId, kUser2Id, nonexistent, kDesc, kAmount)), + check(0)) .done(); } @@ -238,21 +165,11 @@ TEST_F(TransferAsset, NonexistentAsset) { TEST_F(TransferAsset, NegativeAmount) { IntegrationTestFramework(1) .setInitialState(kAdminKeypair) - .sendTx(makeUserWithPerms(kUser1, kUser1Keypair, kPerms, kRole1)) - .skipProposal() - .skipBlock() - .sendTx(makeUserWithPerms(kUser2, kUser2Keypair, kPerms, kRole2)) - .skipProposal() - .skipBlock() - .sendTx(addAssets(kUser1, kUser1Keypair)) - .skipProposal() - .skipBlock() - .sendTx(baseTx() - .transferAsset(kUser1Id, kUser2Id, kAsset, kDesc, "-1.0") - .build() - .signAndAddSignature(kUser1Keypair) - .finish(), - checkStatelessInvalid); + .sendTxAwait(makeFirstUser(), check(1)) + .sendTxAwait(makeSecondUser(), check(1)) + .sendTxAwait(addAssets(), check(1)) + .sendTx(makeTransfer("-1.0"), checkStatelessInvalid) + .done(); } /** @@ -262,19 +179,13 @@ TEST_F(TransferAsset, NegativeAmount) { * (aka skipProposal throws) */ TEST_F(TransferAsset, ZeroAmount) { - IntegrationTestFramework(3) + IntegrationTestFramework(1) .setInitialState(kAdminKeypair) - .sendTx(makeUserWithPerms(kUser1, kUser1Keypair, kPerms, kRole1)) - .sendTx(makeUserWithPerms(kUser2, kUser2Keypair, kPerms, kRole2)) - .sendTx(addAssets(kUser1, kUser1Keypair)) - .skipProposal() - .skipBlock() - .sendTx(baseTx() - .transferAsset(kUser1Id, kUser2Id, kAsset, kDesc, "0.0") - .build() - .signAndAddSignature(kUser1Keypair) - .finish(), - checkStatelessInvalid); + .sendTxAwait(makeFirstUser(), check(1)) + .sendTxAwait(makeSecondUser(), check(1)) + .sendTxAwait(addAssets(), check(1)) + .sendTx(makeTransfer("0.0"), checkStatelessInvalid) + .done(); } /** @@ -283,15 +194,14 @@ TEST_F(TransferAsset, ZeroAmount) { * @then it passed to the proposal */ TEST_F(TransferAsset, EmptyDesc) { - IntegrationTestFramework(4) + IntegrationTestFramework(1) .setInitialState(kAdminKeypair) - .sendTx(makeUserWithPerms(kUser1, kUser1Keypair, kPerms, kRole1)) - .sendTx(makeUserWithPerms(kUser2, kUser2Keypair, kPerms, kRole2)) - .sendTx(addAssets(kUser1, kUser1Keypair)) - .sendTx(completeTx( - baseTx().transferAsset(kUser1Id, kUser2Id, kAsset, "", kAmount))) - .checkBlock( - [](auto &block) { ASSERT_EQ(block->transactions().size(), 4); }) + .sendTxAwait(makeFirstUser(), check(1)) + .sendTxAwait(makeSecondUser(), check(1)) + .sendTxAwait(addAssets(), check(1)) + .sendTxAwait(complete(baseTx().transferAsset( + kUserId, kUser2Id, kAsset, "", kAmount)), + check(1)) .done(); } @@ -302,17 +212,15 @@ TEST_F(TransferAsset, EmptyDesc) { * (aka skipProposal throws) */ TEST_F(TransferAsset, LongDesc) { - std::string long_desc(100000, 'a'); - auto invalid_tx = completeTx( - baseTx().transferAsset(kUser1Id, kUser2Id, kAsset, long_desc, kAmount)); - IntegrationTestFramework(3) + IntegrationTestFramework(1) .setInitialState(kAdminKeypair) - .sendTx(makeUserWithPerms(kUser1, kUser1Keypair, kPerms, kRole1)) - .sendTx(makeUserWithPerms(kUser2, kUser2Keypair, kPerms, kRole2)) - .sendTx(addAssets(kUser1, kUser1Keypair)) - .skipProposal() - .skipBlock() - .sendTx(invalid_tx, checkStatelessInvalid) + .sendTxAwait(makeFirstUser(), check(1)) + .sendTxAwait(makeSecondUser(), check(1)) + .sendTxAwait(addAssets(), check(1)) + .sendTx( + complete(baseTx().transferAsset( + kUserId, kUser2Id, kAsset, std::string(100000, 'a'), kAmount)), + checkStatelessInvalid) .done(); } @@ -324,20 +232,10 @@ TEST_F(TransferAsset, LongDesc) { TEST_F(TransferAsset, MoreThanHas) { IntegrationTestFramework(1) .setInitialState(kAdminKeypair) - .sendTx(makeUserWithPerms(kUser1, kUser1Keypair, kPerms, kRole1)) - .skipProposal() - .skipBlock() - .sendTx(makeUserWithPerms(kUser2, kUser2Keypair, kPerms, kRole2)) - .skipProposal() - .skipBlock() - .sendTx(addAssets(kUser1, kUser1Keypair, "50.0")) - .skipProposal() - .skipBlock() - .sendTx(completeTx( - baseTx().transferAsset(kUser1Id, kUser2Id, kAsset, kDesc, "100.0"))) - .skipProposal() - .checkBlock( - [](auto &block) { ASSERT_EQ(block->transactions().size(), 0); }) + .sendTxAwait(makeFirstUser(), check(1)) + .sendTxAwait(makeSecondUser(), check(1)) + .sendTxAwait(addAssets("50.0"), check(1)) + .sendTxAwait(makeTransfer("100.0"), check(0)) .done(); } @@ -355,32 +253,15 @@ TEST_F(TransferAsset, Uint256DestOverflow) { "2495.0"; // 2**252 - 1 IntegrationTestFramework(1) .setInitialState(kAdminKeypair) - .sendTx(makeUserWithPerms(kUser1, kUser1Keypair, kPerms, kRole1)) - .skipProposal() - .skipBlock() - .sendTx(makeUserWithPerms(kUser2, kUser2Keypair, kPerms, kRole2)) - .skipProposal() - .skipBlock() - .sendTx(addAssets(kUser1, kUser1Keypair, uint256_halfmax)) - .skipProposal() - .skipBlock() + .sendTxAwait(makeFirstUser(), check(1)) + .sendTxAwait(makeSecondUser(), check(1)) + .sendTxAwait(addAssets(uint256_halfmax), check(1)) // Send first half of the maximum - .sendTx(completeTx(baseTx().transferAsset( - kUser1Id, kUser2Id, kAsset, kDesc, uint256_halfmax))) - .skipProposal() - .checkBlock( - [](auto &block) { ASSERT_EQ(block->transactions().size(), 1); }) + .sendTxAwait(makeTransfer(uint256_halfmax), check(1)) // Restore self balance - .sendTx(addAssets(kUser1, kUser1Keypair, uint256_halfmax)) - .skipProposal() - .checkBlock( - [](auto &block) { ASSERT_EQ(block->transactions().size(), 1); }) + .sendTxAwait(addAssets(uint256_halfmax), check(1)) // Send second half of the maximum - .sendTx(completeTx(baseTx().transferAsset( - kUser1Id, kUser2Id, kAsset, kDesc, uint256_halfmax))) - .skipProposal() - .checkBlock( - [](auto &block) { ASSERT_EQ(block->transactions().size(), 0); }) + .sendTxAwait(makeTransfer(uint256_halfmax), check(0)) .done(); } @@ -392,14 +273,12 @@ TEST_F(TransferAsset, Uint256DestOverflow) { * (aka skipProposal throws) */ TEST_F(TransferAsset, SourceIsDest) { - IntegrationTestFramework(2) + IntegrationTestFramework(1) .setInitialState(kAdminKeypair) - .sendTx(makeUserWithPerms(kUser1, kUser1Keypair, kPerms, kRole1)) - .sendTx(addAssets(kUser1, kUser1Keypair)) - .skipProposal() - .skipBlock() - .sendTx(completeTx(baseTx().transferAsset( - kUser1Id, kUser1Id, kAsset, kDesc, kAmount)), + .sendTxAwait(makeFirstUser(), check(1)) + .sendTxAwait(addAssets(), check(1)) + .sendTx(complete(baseTx().transferAsset( + kUserId, kUserId, kAsset, kDesc, kAmount)), checkStatelessInvalid); } @@ -410,40 +289,32 @@ TEST_F(TransferAsset, SourceIsDest) { * @then the tx is commited */ TEST_F(TransferAsset, InterDomain) { - const auto kNewRole = "newrl"; - const auto kNewDomain = "newdom"; - const auto kUser2Id = kUser2 + "@" + kNewDomain; + const std::string kNewDomain = "newdom"; + const std::string kUser2Id = kUser2 + "@" + kNewDomain; + const std::string kNewAssetId = + IntegrationTestFramework::kAssetName + "#" + kNewDomain; + + auto make_second_user = + baseTx() + .creatorAccountId(IntegrationTestFramework::kAdminId) + .createRole(kRole2, {interface::permissions::Role::kReceive}) + .createDomain(kNewDomain, kRole2) + .createAccount(kUser2, kNewDomain, kUser2Keypair.publicKey()) + .createAsset(IntegrationTestFramework::kAssetName, kNewDomain, 1) + .build() + .signAndAddSignature(kAdminKeypair) + .finish(); + auto add_assets = + complete(baseTx().addAssetQuantity(kUserId, kNewAssetId, kAmount)); + auto make_transfer = complete( + baseTx().transferAsset(kUserId, kUser2Id, kNewAssetId, kDesc, kAmount)); + IntegrationTestFramework(1) .setInitialState(kAdminKeypair) - .sendTx(makeUserWithPerms(kUser1, kUser1Keypair, kPerms, kRole1)) - .skipProposal() - .skipBlock() - .sendTx( - shared_model::proto::TransactionBuilder() - .creatorAccountId( - integration_framework::IntegrationTestFramework::kAdminId) - .createdTime(getUniqueTime()) - .createRole(kNewRole, {interface::permissions::Role::kReceive}) - .createDomain(kNewDomain, kNewRole) - .createAccount( - kUser2, - kNewDomain, - crypto::DefaultCryptoAlgorithmType::generateKeypair() - .publicKey()) - .createAsset(IntegrationTestFramework::kAssetName, kNewDomain, 1) - .quorum(1) - .build() - .signAndAddSignature(kAdminKeypair) - .finish()) - .skipProposal() - .skipBlock() - .sendTx(addAssets(kUser1, kUser1Keypair, kAmount)) - .skipProposal() - .skipBlock() - .sendTx(completeTx( - baseTx().transferAsset(kUser1Id, kUser2Id, kAsset, kDesc, kAmount))) - .checkBlock( - [](auto &block) { ASSERT_EQ(block->transactions().size(), 1); }) + .sendTxAwait(makeFirstUser(), check(1)) + .sendTxAwait(make_second_user, check(1)) + .sendTxAwait(add_assets, check(1)) + .sendTxAwait(make_transfer, check(1)) .done(); } @@ -457,14 +328,28 @@ TEST_F(TransferAsset, BigPrecision) { const std::string kNewAsset = IntegrationTestFramework::kAssetName + "a"; const std::string kNewAssetId = kNewAsset + "#" + IntegrationTestFramework::kDefaultDomain; - const auto precision = 5; + const auto kPrecision = 5; const std::string kInitial = "500"; const std::string kForTransfer = "1"; const std::string kLeft = "499"; + auto create_asset = + baseTx() + .creatorAccountId( + integration_framework::IntegrationTestFramework::kAdminId) + .createAsset( + kNewAsset, IntegrationTestFramework::kDefaultDomain, kPrecision) + .build() + .signAndAddSignature(kAdminKeypair) + .finish(); + auto add_assets = + complete(baseTx().addAssetQuantity(kUserId, kNewAssetId, kInitial)); + auto make_transfer = complete(baseTx().transferAsset( + kUserId, kUser2Id, kNewAssetId, kDesc, kForTransfer)); + auto check_balance = [](std::string account_id, std::string val) { return [a = std::move(account_id), - v = val + "." + std::string(precision, '0')](auto &resp) { + v = val + "." + std::string(kPrecision, '0')](auto &resp) { auto &acc_ast = boost::apply_visitor( framework::SpecifiedVisitor(), resp.get()); @@ -475,12 +360,11 @@ TEST_F(TransferAsset, BigPrecision) { } }; }; + auto make_query = [this](std::string account_id) { - return proto::QueryBuilder() + return baseQry() .creatorAccountId(IntegrationTestFramework::kAdminId) - .createdTime(getUniqueTime()) .getAccountAssets(account_id) - .queryCounter(1) .build() .signAndAddSignature(kAdminKeypair) .finish(); @@ -488,50 +372,12 @@ TEST_F(TransferAsset, BigPrecision) { IntegrationTestFramework(1) .setInitialState(kAdminKeypair) - .sendTx(makeUserWithPerms(kUser1, kUser1Keypair, kPerms, kRole1)) - .skipProposal() - .skipBlock() - .sendTx(makeUserWithPerms(kUser2, kUser2Keypair, kPerms, kRole2)) - .skipProposal() - .skipBlock() - .sendTx(proto::TransactionBuilder() - .creatorAccountId( - integration_framework::IntegrationTestFramework::kAdminId) - .createdTime(getUniqueTime()) - .quorum(1) - .createAsset(kNewAsset, - IntegrationTestFramework::kDefaultDomain, - precision) - .build() - .signAndAddSignature(kAdminKeypair) - .finish()) - .skipProposal() - .checkBlock([](auto &block) { - ASSERT_EQ(block->transactions().size(), 1) << "Cannot create asset"; - }) - .sendTx(proto::TransactionBuilder() - .creatorAccountId(kUser1Id) - .createdTime(getUniqueTime()) - .quorum(1) - .addAssetQuantity(kUser1Id, kNewAssetId, kInitial) - .build() - .signAndAddSignature(kUser1Keypair) - .finish()) - .checkBlock([](auto &block) { - ASSERT_EQ(block->transactions().size(), 1) << "Cannot add assets"; - }) - .sendTx(proto::TransactionBuilder() - .creatorAccountId(kUser1Id) - .createdTime(getUniqueTime()) - .quorum(1) - .transferAsset( - kUser1Id, kUser2Id, kNewAssetId, kDesc, kForTransfer) - .build() - .signAndAddSignature(kUser1Keypair) - .finish()) - .checkBlock( - [](auto &block) { ASSERT_EQ(block->transactions().size(), 1); }) - .sendQuery(make_query(kUser1Id), check_balance(kUser1Id, kLeft)) + .sendTxAwait(makeFirstUser(), check(1)) + .sendTxAwait(makeSecondUser(), check(1)) + .sendTxAwait(create_asset, check(1)) + .sendTxAwait(add_assets, check(1)) + .sendTxAwait(make_transfer, check(1)) + .sendQuery(make_query(kUserId), check_balance(kUserId, kLeft)) .sendQuery(make_query(kUser2Id), check_balance(kUser2Id, kForTransfer)) .done(); } From c64d9f2f8fde2ced282e270c77658a92f29a8864 Mon Sep 17 00:00:00 2001 From: Kitsu Date: Tue, 26 Jun 2018 10:53:56 +0300 Subject: [PATCH 04/97] Remove hasAccountGrantablePermissions in qry exec (#1500) Signed-off-by: Kitsu --- irohad/execution/impl/query_execution.cpp | 44 ++-- shared_model/interfaces/impl/permissions.cpp | 17 -- shared_model/interfaces/permissions.hpp | 2 - test/module/iroha-cli/client_test.cpp | 24 +- test/module/irohad/execution/CMakeLists.txt | 7 + .../query_execution_test.cpp} | 215 ------------------ .../irohad/torii/torii_queries_test.cpp | 22 +- test/module/irohad/validation/CMakeLists.txt | 7 - 8 files changed, 35 insertions(+), 303 deletions(-) rename test/module/irohad/{validation/query_execution.cpp => execution/query_execution_test.cpp} (85%) diff --git a/irohad/execution/impl/query_execution.cpp b/irohad/execution/impl/query_execution.cpp index 98fe5e629c..a807ec7cde 100644 --- a/irohad/execution/impl/query_execution.cpp +++ b/irohad/execution/impl/query_execution.cpp @@ -59,32 +59,28 @@ shared_model::proto::TemplateQueryResponseBuilder<1> statefulFailed() { return buildError(); } -bool hasQueryPermission(const std::string &creator, - const std::string &target_account, - WsvQuery &wsv_query, - Role indiv_permission_id, - Role all_permission_id, - Role domain_permission_id) { +static bool hasQueryPermission(const std::string &creator, + const std::string &target_account, + WsvQuery &wsv_query, + Role indiv_permission_id, + Role all_permission_id, + Role domain_permission_id) { auto perms_set = iroha::getAccountPermissions(creator, wsv_query); - auto grantable = permissionOf(indiv_permission_id); - return - // 1. Creator has grant permission from other user - (creator != target_account - and wsv_query.hasAccountGrantablePermission( - creator, target_account, grantable)) - or // ----- Creator has role permission --------- - (perms_set - and ( - // 2. Creator want to query his account, must have role - // permission - (creator == target_account - and perms_set.value().test(indiv_permission_id)) - or // 3. Creator has global permission to get any account - perms_set.value().test(all_permission_id) - or // 4. Creator has domain permission - (getDomainFromName(creator) == getDomainFromName(target_account) - and perms_set.value().test(domain_permission_id)))); + if (not perms_set) { + return false; + } + + auto &set = perms_set.value(); + // Creator want to query his account, must have role + // permission + return (creator == target_account and set.test(indiv_permission_id)) or + // Creator has global permission to get any account + set.test(all_permission_id) or + // Creator has domain permission + (getDomainFromName(creator) == getDomainFromName(target_account) + and set.test(domain_permission_id)); } + bool QueryProcessingFactory::validate( const shared_model::interface::BlocksQuery &query) { return checkAccountRolePermission( diff --git a/shared_model/interfaces/impl/permissions.cpp b/shared_model/interfaces/impl/permissions.cpp index 4bbe142075..1322a9f9d8 100644 --- a/shared_model/interfaces/impl/permissions.cpp +++ b/shared_model/interfaces/impl/permissions.cpp @@ -28,23 +28,6 @@ namespace shared_model { return Role::COUNT; } - Grantable permissionOf(Role g) { - switch (g) { - case Role::kAddMySignatory: - return Grantable::kAddMySignatory; - case Role::kRemoveMySignatory: - return Grantable::kRemoveMySignatory; - case Role::kSetMyQuorum: - return Grantable::kSetMyQuorum; - case Role::kSetMyAccountDetail: - return Grantable::kSetMyAccountDetail; - case Role::kTransferMyAssets: - return Grantable::kTransferMyAssets; - default:; - } - return Grantable::COUNT; - } - bool isValid(Role perm) noexcept { auto p = static_cast(perm); return p < static_cast(Role::COUNT); diff --git a/shared_model/interfaces/permissions.hpp b/shared_model/interfaces/permissions.hpp index 484ad39538..fd83374c49 100644 --- a/shared_model/interfaces/permissions.hpp +++ b/shared_model/interfaces/permissions.hpp @@ -73,8 +73,6 @@ namespace shared_model { }; Role permissionFor(Grantable); - // TODO(@l4l) 19/06/18: Remove with IR-1452 - Grantable permissionOf(Role); /** * @param perm protocol object for checking diff --git a/test/module/iroha-cli/client_test.cpp b/test/module/iroha-cli/client_test.cpp index 7b51079630..2f43a186ca 100644 --- a/test/module/iroha-cli/client_test.cpp +++ b/test/module/iroha-cli/client_test.cpp @@ -237,20 +237,15 @@ TEST_F(ClientServerTest, SendQueryWhenValid) { EXPECT_CALL(*wsv_query, getSignatories("admin@test")) .WillRepeatedly(Return(signatories)); - EXPECT_CALL( - *wsv_query, - hasAccountGrantablePermission( - "admin@test", - "test@test", - shared_model::interface::permissions::permissionOf( - shared_model::interface::permissions::Role::kGetMyAccDetail))) - .WillOnce(Return(true)); - EXPECT_CALL(*wsv_query, getAccountDetail("test@test")) .WillOnce(Return(boost::make_optional(std::string("value")))); + const std::vector kRole{"role"}; EXPECT_CALL(*wsv_query, getAccountRoles("admin@test")) - .WillOnce(Return(boost::none)); + .WillOnce(Return(boost::make_optional(kRole))); + EXPECT_CALL(*wsv_query, getRolePermissions(kRole[0])) + .WillOnce(Return(shared_model::interface::RolePermissionSet{ + shared_model::interface::permissions::Role::kGetAllAccDetail})); auto query = QueryBuilder() .createdTime(iroha::time::now()) @@ -271,15 +266,6 @@ TEST_F(ClientServerTest, SendQueryWhenStatefulInvalid) { EXPECT_CALL(*wsv_query, getSignatories("admin@test")) .WillRepeatedly(Return(signatories)); - EXPECT_CALL( - *wsv_query, - hasAccountGrantablePermission( - "admin@test", - "test@test", - shared_model::interface::permissions::permissionOf( - shared_model::interface::permissions::Role::kGetMyAccDetail))) - .WillOnce(Return(false)); - EXPECT_CALL(*wsv_query, getAccountRoles("admin@test")) .WillOnce(Return(boost::none)); diff --git a/test/module/irohad/execution/CMakeLists.txt b/test/module/irohad/execution/CMakeLists.txt index 3068bc81a7..9b524bd031 100644 --- a/test/module/irohad/execution/CMakeLists.txt +++ b/test/module/irohad/execution/CMakeLists.txt @@ -17,3 +17,10 @@ target_link_libraries(command_validate_execute_test command_execution shared_model_stateless_validation ) + +addtest(query_execution_test query_execution_test.cpp) +target_link_libraries(query_execution_test + query_execution + shared_model_interfaces + shared_model_stateless_validation + ) diff --git a/test/module/irohad/validation/query_execution.cpp b/test/module/irohad/execution/query_execution_test.cpp similarity index 85% rename from test/module/irohad/validation/query_execution.cpp rename to test/module/irohad/execution/query_execution_test.cpp index 351448d862..5c91a22e73 100644 --- a/test/module/irohad/validation/query_execution.cpp +++ b/test/module/irohad/execution/query_execution_test.cpp @@ -55,9 +55,6 @@ class QueryValidateExecuteTest : public ::testing::Test { block_query = std::make_shared>(); factory = std::make_shared(wsv_query, block_query); - EXPECT_CALL(*wsv_query, hasAccountGrantablePermission(_, _, _)) - .WillRepeatedly(Return(false)); - creator = clone(shared_model::proto::AccountBuilder() .accountId(admin_id) .domainId(domain_id) @@ -214,40 +211,6 @@ TEST_F(GetAccountTest, DomainAccountValidCase) { }); } -/** - * @given initialized storage, granted permission - * @when get account information about other user - * @then Return users account - */ -TEST_F(GetAccountTest, GrantAccountValidCase) { - auto query = TestQueryBuilder() - .creatorAccountId(admin_id) - .getAccount(account_id) - .build(); - - role_permissions = {}; - - EXPECT_CALL(*wsv_query, getAccountRoles(admin_id)) - .WillOnce(Return(admin_roles)); - EXPECT_CALL(*wsv_query, getRolePermissions(admin_role)) - .WillOnce(Return(role_permissions)); - EXPECT_CALL(*wsv_query, - hasAccountGrantablePermission( - admin_id, account_id, permissionOf(Role::kGetMyAccount))) - .WillOnce(Return(true)); - - EXPECT_CALL(*wsv_query, getAccount(account_id)).WillOnce(Return(account)); - EXPECT_CALL(*wsv_query, getAccountRoles(account_id)) - .WillOnce(Return(admin_roles)); - auto response = validateAndExecute(query); - ASSERT_NO_THROW({ - const auto &cast_resp = boost::apply_visitor( - framework::SpecifiedVisitor(), - response->get()); - ASSERT_EQ(cast_resp.account().accountId(), account_id); - }); -} - /** * @given initialized storage, domain permission * @when get account information about other user in the other domain @@ -265,10 +228,6 @@ TEST_F(GetAccountTest, DifferentDomainAccountInValidCase) { .WillOnce(Return(admin_roles)); EXPECT_CALL(*wsv_query, getRolePermissions(admin_role)) .WillOnce(Return(role_permissions)); - EXPECT_CALL(*wsv_query, - hasAccountGrantablePermission( - admin_id, "test@test2", permissionOf(Role::kGetMyAccount))) - .WillOnce(Return(false)); auto response = validateAndExecute(query); @@ -470,50 +429,6 @@ TEST_F(GetAccountAssetsTest, DomainAccountValidCase) { }); } -/** - * @given initialized storage, granted permission - * @when get account information about other user - * @then Return account assets - */ -TEST_F(GetAccountAssetsTest, GrantAccountValidCase) { - auto query = TestQueryBuilder() - .creatorAccountId(admin_id) - .getAccountAssets(account_id) - .build(); - - accountAsset = clone(shared_model::proto::AccountAssetBuilder() - .assetId(accountAsset->assetId()) - .accountId(account_id) - .balance(accountAsset->balance()) - .build()); - role_permissions = {}; - - EXPECT_CALL(*wsv_query, getAccountRoles(admin_id)) - .WillOnce(Return(admin_roles)); - EXPECT_CALL(*wsv_query, getRolePermissions(admin_role)) - .WillOnce(Return(role_permissions)); - EXPECT_CALL(*wsv_query, - hasAccountGrantablePermission( - admin_id, account_id, permissionOf(Role::kGetMyAccAst))) - .WillOnce(Return(true)); - - EXPECT_CALL(*wsv_query, getAccountAssets(account_id)) - .WillOnce(Return( - std::vector>( - {accountAsset}))); - - auto response = validateAndExecute(query); - ASSERT_NO_THROW({ - const auto &cast_resp = boost::apply_visitor( - framework::SpecifiedVisitor< - shared_model::interface::AccountAssetResponse>(), - response->get()); - - ASSERT_EQ(cast_resp.accountAssets()[0].accountId(), account_id); - ASSERT_EQ(cast_resp.accountAssets()[0].assetId(), asset_id); - }); -} - /** * @given initialized storage, domain permission * @when get account information about other user in the other domain @@ -544,10 +459,6 @@ TEST_F(GetAccountAssetsTest, DifferentDomainAccountInValidCase) { .WillOnce(Return(admin_roles)); EXPECT_CALL(*wsv_query, getRolePermissions(admin_role)) .WillOnce(Return(role_permissions)); - EXPECT_CALL(*wsv_query, - hasAccountGrantablePermission( - admin_id, "test@test2", permissionOf(Role::kGetMyAccAst))) - .WillOnce(Return(false)); auto response = validateAndExecute(query); @@ -698,41 +609,6 @@ TEST_F(GetSignatoriesTest, DomainAccountValidCase) { }); } -/** - * @given initialized storage, granted permission - * @when get account information about other user - * @then Return signatories - */ -TEST_F(GetSignatoriesTest, GrantAccountValidCase) { - auto query = TestQueryBuilder() - .creatorAccountId(admin_id) - .getSignatories(account_id) - .build(); - - role_permissions = {}; - - EXPECT_CALL(*wsv_query, getAccountRoles(admin_id)) - .WillOnce(Return(admin_roles)); - EXPECT_CALL(*wsv_query, getRolePermissions(admin_role)) - .WillOnce(Return(role_permissions)); - EXPECT_CALL(*wsv_query, - hasAccountGrantablePermission( - admin_id, account_id, permissionOf(Role::kGetMySignatories))) - .WillOnce(Return(true)); - - EXPECT_CALL(*wsv_query, getSignatories(account_id)).WillOnce(Return(signs)); - - auto response = validateAndExecute(query); - ASSERT_NO_THROW({ - const auto &cast_resp = boost::apply_visitor( - framework::SpecifiedVisitor< - shared_model::interface::SignatoriesResponse>(), - response->get()); - - ASSERT_EQ(cast_resp.keys().size(), 1); - }); -} - /** * @given initialized storage, domain permission * @when get account information about other user in the other domain @@ -750,11 +626,6 @@ TEST_F(GetSignatoriesTest, DifferentDomainAccountInValidCase) { .WillOnce(Return(admin_roles)); EXPECT_CALL(*wsv_query, getRolePermissions(admin_role)) .WillOnce(Return(role_permissions)); - EXPECT_CALL( - *wsv_query, - hasAccountGrantablePermission( - admin_id, "test@test2", permissionOf(Role::kGetMySignatories))) - .WillOnce(Return(false)); auto response = validateAndExecute(query); @@ -910,45 +781,6 @@ TEST_F(GetAccountTransactionsTest, DomainAccountValidCase) { }); } -/** - * @given initialized storage, granted permission - * @when get account information about other user - * @then Return error - */ -TEST_F(GetAccountTransactionsTest, GrantAccountValidCase) { - auto query = TestQueryBuilder() - .creatorAccountId(admin_id) - .getAccountTransactions(account_id) - .build(); - - role_permissions = {}; - - EXPECT_CALL(*wsv_query, getAccountRoles(admin_id)) - .WillOnce(Return(admin_roles)); - EXPECT_CALL(*wsv_query, getRolePermissions(admin_role)) - .WillOnce(Return(role_permissions)); - EXPECT_CALL(*wsv_query, - hasAccountGrantablePermission( - admin_id, account_id, permissionOf(Role::kGetMyAccTxs))) - .WillOnce(Return(true)); - - EXPECT_CALL(*block_query, getAccountTransactions(account_id)) - .WillOnce(Return(txs_observable)); - - auto response = validateAndExecute(query); - ASSERT_NO_THROW({ - const auto &cast_resp = boost::apply_visitor( - framework::SpecifiedVisitor< - shared_model::interface::TransactionsResponse>(), - response->get()); - - ASSERT_EQ(cast_resp.transactions().size(), N); - for (const auto &tx : cast_resp.transactions()) { - EXPECT_EQ(account_id, tx.creatorAccountId()); - } - }); -} - /** * @given initialized storage, domain permission * @when get account information about other user in the other domain @@ -966,10 +798,6 @@ TEST_F(GetAccountTransactionsTest, DifferentDomainAccountInValidCase) { .WillOnce(Return(admin_roles)); EXPECT_CALL(*wsv_query, getRolePermissions(admin_role)) .WillOnce(Return(role_permissions)); - EXPECT_CALL(*wsv_query, - hasAccountGrantablePermission( - admin_id, "test@test2", permissionOf(Role::kGetMyAccTxs))) - .WillOnce(Return(false)); auto response = validateAndExecute(query); @@ -1125,45 +953,6 @@ TEST_F(GetAccountAssetsTransactionsTest, DomainAccountValidCase) { }); } -/** - * @given initialized storage, granted permission - * @when get account information about other user - * @then Return error - */ -TEST_F(GetAccountAssetsTransactionsTest, GrantAccountValidCase) { - auto query = TestQueryBuilder() - .creatorAccountId(admin_id) - .getAccountAssetTransactions(account_id, asset_id) - .build(); - - role_permissions = {}; - - EXPECT_CALL(*wsv_query, getAccountRoles(admin_id)) - .WillOnce(Return(admin_roles)); - EXPECT_CALL(*wsv_query, getRolePermissions(admin_role)) - .WillOnce(Return(role_permissions)); - EXPECT_CALL(*wsv_query, - hasAccountGrantablePermission( - admin_id, account_id, permissionOf(Role::kGetMyAccAstTxs))) - .WillOnce(Return(true)); - - EXPECT_CALL(*block_query, getAccountAssetTransactions(account_id, asset_id)) - .WillOnce(Return(txs_observable)); - - auto response = validateAndExecute(query); - ASSERT_NO_THROW({ - const auto &cast_resp = boost::apply_visitor( - framework::SpecifiedVisitor< - shared_model::interface::TransactionsResponse>(), - response->get()); - - ASSERT_EQ(cast_resp.transactions().size(), N); - for (const auto &tx : cast_resp.transactions()) { - EXPECT_EQ(account_id, tx.creatorAccountId()); - } - }); -} - /** * @given initialized storage, domain permission * @when get account information about other user in the other domain @@ -1181,10 +970,6 @@ TEST_F(GetAccountAssetsTransactionsTest, DifferentDomainAccountInValidCase) { .WillOnce(Return(admin_roles)); EXPECT_CALL(*wsv_query, getRolePermissions(admin_role)) .WillOnce(Return(role_permissions)); - EXPECT_CALL(*wsv_query, - hasAccountGrantablePermission( - admin_id, "test@test2", permissionOf(Role::kGetMyAccAstTxs))) - .WillOnce(Return(false)); auto response = validateAndExecute(query); diff --git a/test/module/irohad/torii/torii_queries_test.cpp b/test/module/irohad/torii/torii_queries_test.cpp index dfbe3a1f29..f74f9d4d98 100644 --- a/test/module/irohad/torii/torii_queries_test.cpp +++ b/test/module/irohad/torii/torii_queries_test.cpp @@ -154,12 +154,6 @@ TEST_F(ToriiQueriesTest, FindAccountWhenNoGrantPermissions) { shared_model::proto::AccountBuilder().accountId("b@domain").build(); auto creator = "a@domain"; - EXPECT_CALL( - *wsv_query, - hasAccountGrantablePermission( - creator, account.accountId(), permissionOf(Role::kGetMyAccount))) - .WillOnce(Return(false)); - EXPECT_CALL(*wsv_query, getSignatories(creator)) .WillRepeatedly(Return(signatories)); EXPECT_CALL(*wsv_query, getAccountRoles(creator)) @@ -199,11 +193,6 @@ TEST_F(ToriiQueriesTest, FindAccountWhenHasReadPermissions) { EXPECT_CALL(*wsv_query, getSignatories(creator)) .WillRepeatedly(Return(signatories)); - EXPECT_CALL( - *wsv_query, - hasAccountGrantablePermission( - creator, accountB->accountId(), permissionOf(Role::kGetMyAccount))) - .WillOnce(Return(true)); // Should be called once, after successful stateful validation EXPECT_CALL(*wsv_query, getAccount(accountB->accountId())) @@ -211,6 +200,9 @@ TEST_F(ToriiQueriesTest, FindAccountWhenHasReadPermissions) { std::vector roles = {"user"}; EXPECT_CALL(*wsv_query, getAccountRoles(_)).WillRepeatedly(Return(roles)); + EXPECT_CALL(*wsv_query, getRolePermissions(_)) + .WillOnce(Return(shared_model::interface::RolePermissionSet( + {shared_model::interface::permissions::Role::kGetAllAccounts}))); iroha::protocol::QueryResponse response; @@ -296,10 +288,6 @@ TEST_F(ToriiQueriesTest, FindAccountAssetWhenNoGrantPermissions) { EXPECT_CALL(*wsv_query, getSignatories(creator)) .WillRepeatedly(Return(signatories)); - EXPECT_CALL(*wsv_query, - hasAccountGrantablePermission( - creator, accountb_id, permissionOf(Role::kGetMyAccAst))) - .WillOnce(Return(false)); EXPECT_CALL(*wsv_query, getAccountRoles(creator)) .WillOnce(Return(boost::none)); @@ -415,10 +403,6 @@ TEST_F(ToriiQueriesTest, FindSignatoriesWhenNoGrantPermissions) { auto creator = "a@domain"; EXPECT_CALL(*wsv_query, getSignatories(creator)) .WillRepeatedly(Return(signatories)); - EXPECT_CALL(*wsv_query, - hasAccountGrantablePermission( - creator, "b@domain", permissionOf(Role::kGetMySignatories))) - .WillOnce(Return(false)); EXPECT_CALL(*wsv_query, getAccountRoles(creator)) .WillOnce(Return(boost::none)); diff --git a/test/module/irohad/validation/CMakeLists.txt b/test/module/irohad/validation/CMakeLists.txt index 3f964e39d8..363f33ba28 100644 --- a/test/module/irohad/validation/CMakeLists.txt +++ b/test/module/irohad/validation/CMakeLists.txt @@ -15,13 +15,6 @@ # limitations under the License. # -addtest(query_execution_test query_execution.cpp) -target_link_libraries(query_execution_test - query_execution - shared_model_interfaces - shared_model_stateless_validation - ) - addtest(chain_validation_test chain_validation_test.cpp) target_link_libraries(chain_validation_test chain_validator From 49397a4354e0d6eab5f5bfa8f9d419e70f672f6f Mon Sep 17 00:00:00 2001 From: Uditha Atukorala Date: Tue, 26 Jun 2018 10:41:56 +0100 Subject: [PATCH 05/97] Cache CLI input values (#1496) Signed-off-by: Uditha Atukorala --- .../impl/interactive_common_cli.cpp | 58 +++++++++---- .../impl/interactive_query_cli.cpp | 43 +++++----- .../impl/interactive_status_cli.cpp | 5 +- .../impl/interactive_transaction_cli.cpp | 83 ++++++++++--------- .../interactive/interactive_common_cli.hpp | 50 +++++++++-- .../interactive/interactive_query_cli.hpp | 4 +- .../interactive_transaction_cli.hpp | 4 +- 7 files changed, 154 insertions(+), 93 deletions(-) diff --git a/iroha-cli/interactive/impl/interactive_common_cli.cpp b/iroha-cli/interactive/impl/interactive_common_cli.cpp index 5b5c8bbd49..e2d0df4d47 100644 --- a/iroha-cli/interactive/impl/interactive_common_cli.cpp +++ b/iroha-cli/interactive/impl/interactive_common_cli.cpp @@ -14,8 +14,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +#include #include +#include "common/types.hpp" #include "interactive/interactive_common_cli.hpp" #include "parser/parser.hpp" @@ -35,14 +37,25 @@ namespace iroha_cli { int default_port) { return { // commonParamsMap - {SAVE_CODE, {"Path to save json file"}}, + {SAVE_CODE, makeParamsDescription({"Path to save json file"})}, {SEND_CODE, - {"Peer address (" + default_ip + ")", - "Peer port (" + std::to_string(default_port) + ")"}} + {ParamData({"Peer address", default_ip}), + ParamData({"Peer port", std::to_string(default_port)})}} // commonParamsMap }; } + ParamsDescription makeParamsDescription( + const std::vector ¶ms) { + return std::accumulate(params.begin(), + params.end(), + ParamsDescription{}, + [](auto &&acc, auto &el) { + acc.push_back(ParamData({el, {}})); + return std::forward(acc); + }); + } + void handleEmptyCommand() { std::cout << "Put not empty command" << std::endl; } @@ -57,16 +70,15 @@ namespace iroha_cli { bool isBackOption(std::string line) { auto command = parser::parseFirstCommand(std::move(line)); - return command - and (*command == "0" or *command == BACK_CODE); + return command and (*command == "0" or *command == BACK_CODE); } void printCommandParameters(std::string &command, - std::vector parameters) { + const ParamsDescription ¶meters) { std::cout << "Run " << command << " with following parameters: " << std::endl; std::for_each(parameters.begin(), parameters.end(), [](auto el) { - std::cout << " " << el << std::endl; + std::cout << " " << el.message << std::endl; }); } @@ -87,12 +99,20 @@ namespace iroha_cli { return line; } + boost::optional promptString(const ParamData ¶m) { + std::string message = param.message; + if (not param.cache.empty()) { + message += " (" + param.cache + ")"; + } + return promptString(message); + } + void printEnd() { std::cout << "--------------------" << std::endl; } boost::optional> parseIrohaPeerParams( - ParamsDescription params, + std::vector params, const std::string &default_ip, int default_port) { const auto &address = params[0].empty() ? default_ip : params[0]; @@ -107,9 +127,8 @@ namespace iroha_cli { } boost::optional> parseParams( - std::string line, std::string command_name, ParamsMap params_map) { - auto params_description = - findInHandlerMap(command_name, std::move(params_map)); + std::string line, std::string command_name, ParamsMap ¶ms_map) { + auto params_description = findInHandlerMap(command_name, params_map); if (not params_description) { // Report no params where found for this command std::cout << "Command params not found" << std::endl; @@ -122,11 +141,18 @@ namespace iroha_cli { std::vector params; std::for_each(params_description.value().begin(), params_description.value().end(), - [¶ms](auto param) { - auto val = promptString(param); - if (val and not val.value().empty()) { - params.push_back(val.value()); - } + [¶ms](auto ¶m) { + using namespace iroha; + promptString(param) | [&](auto &val) { + if (not val.empty()) { + // Update input cache + param.cache = val; + params.push_back(val); + } else if (not param.cache.empty()) { + // Input cache is not empty, use cached value + params.push_back(param.cache); + } + }; }); if (params.size() != params_description.value().size()) { // Wrong params passed diff --git a/iroha-cli/interactive/impl/interactive_query_cli.cpp b/iroha-cli/interactive/impl/interactive_query_cli.cpp index 95fdcaa3e3..dedad966b0 100644 --- a/iroha-cli/interactive/impl/interactive_query_cli.cpp +++ b/iroha-cli/interactive/impl/interactive_query_cli.cpp @@ -58,23 +58,24 @@ namespace iroha_cli { const auto role_id = "Requested role name"; const auto tx_hashes = "Requested tx hashes"; - query_params_descriptions_ = { - {GET_ACC, {acc_id}}, - {GET_ACC_AST, {acc_id, ast_id}}, - {GET_ACC_AST_TX, {acc_id, ast_id}}, - {GET_ACC_TX, {acc_id}}, - {GET_TX, {tx_hashes}}, - {GET_ACC_SIGN, {acc_id}}, - {GET_ROLES, {}}, - {GET_AST_INFO, {ast_id}}, - {GET_ROLE_PERM, {role_id}} - // query_params_descriptions_ + query_params_map_ = { + {GET_ACC, makeParamsDescription({acc_id})}, + {GET_ACC_AST, makeParamsDescription({acc_id, ast_id})}, + {GET_ACC_AST_TX, makeParamsDescription({acc_id, ast_id})}, + {GET_ACC_TX, makeParamsDescription({acc_id})}, + {GET_TX, makeParamsDescription({tx_hashes})}, + {GET_ACC_SIGN, makeParamsDescription({acc_id})}, + {GET_ROLES, makeParamsDescription({})}, + {GET_AST_INFO, makeParamsDescription({ast_id})}, + {GET_ROLE_PERM, makeParamsDescription({role_id})} + // query_params_map_ }; query_handlers_ = { {GET_ACC, &InteractiveQueryCli::parseGetAccount}, {GET_ACC_AST, &InteractiveQueryCli::parseGetAccountAssets}, - {GET_ACC_AST_TX, &InteractiveQueryCli::parseGetAccountAssetTransactions}, + {GET_ACC_AST_TX, + &InteractiveQueryCli::parseGetAccountAssetTransactions}, {GET_ACC_TX, &InteractiveQueryCli::parseGetAccountTransactions}, {GET_TX, &InteractiveQueryCli::parseGetTransactions}, {GET_ACC_SIGN, &InteractiveQueryCli::parseGetSignatories}, @@ -84,8 +85,8 @@ namespace iroha_cli { // query_handlers_ }; - menu_points_ = formMenu( - query_handlers_, query_params_descriptions_, description_map_); + menu_points_ = + formMenu(query_handlers_, query_params_map_, description_map_); // Add "go back" option addBackOption(menu_points_); } @@ -93,12 +94,10 @@ namespace iroha_cli { void InteractiveQueryCli::create_result_menu() { result_handlers_ = {{SAVE_CODE, &InteractiveQueryCli::parseSaveFile}, {SEND_CODE, &InteractiveQueryCli::parseSendToIroha}}; - result_params_descriptions_ = - getCommonParamsMap(default_peer_ip_, default_port_); + result_params_map_ = getCommonParamsMap(default_peer_ip_, default_port_); - result_points_ = formMenu(result_handlers_, - result_params_descriptions_, - getCommonDescriptionMap()); + result_points_ = formMenu( + result_handlers_, result_params_map_, getCommonDescriptionMap()); addBackOption(result_points_); } @@ -159,7 +158,7 @@ namespace iroha_cli { } auto res = handleParse>( - this, line, query_handlers_, query_params_descriptions_); + this, line, query_handlers_, query_params_map_); if (not res) { // Continue parsing return true; @@ -259,8 +258,8 @@ namespace iroha_cli { return true; } - auto res = handleParse( - this, line, result_handlers_, result_params_descriptions_); + auto res = + handleParse(this, line, result_handlers_, result_params_map_); return res.get_value_or(true); } diff --git a/iroha-cli/interactive/impl/interactive_status_cli.cpp b/iroha-cli/interactive/impl/interactive_status_cli.cpp index 9ec0c91771..6aedf781b4 100644 --- a/iroha-cli/interactive/impl/interactive_status_cli.cpp +++ b/iroha-cli/interactive/impl/interactive_status_cli.cpp @@ -50,7 +50,8 @@ namespace iroha_cli { descriptionMap_ = {{GET_TX_INFO, "Get status of transaction"}}; const auto tx_id = "Requested tx hash"; - requestParamsDescriptions_ = {{GET_TX_INFO, {tx_id}}}; + requestParamsDescriptions_ = { + {GET_TX_INFO, makeParamsDescription({tx_id})}}; actionHandlers_ = {{GET_TX_INFO, &InteractiveStatusCli::parseGetHash}}; menuPoints_ = formMenu( @@ -76,7 +77,7 @@ namespace iroha_cli { printMenu("Choose action: ", menuPoints_); while (isParsing) { auto line = promptString("> "); - if (not line){ + if (not line) { // line has terminating symbol isParsing = false; break; diff --git a/iroha-cli/interactive/impl/interactive_transaction_cli.cpp b/iroha-cli/interactive/impl/interactive_transaction_cli.cpp index 02b095a6e1..8fe7798c11 100644 --- a/iroha-cli/interactive/impl/interactive_transaction_cli.cpp +++ b/iroha-cli/interactive/impl/interactive_transaction_cli.cpp @@ -86,37 +86,40 @@ namespace iroha_cli { const auto can_asset_creator = "Can create/add new assets"; const auto can_roles = "Can create/append roles"; - command_params_descriptions_ = { - {ADD_ASSET_QTY, {acc_id, ast_id, amount_a, amount_b}}, - {ADD_PEER, {peer_id, pub_key}}, - {ADD_SIGN, {acc_id, pub_key}}, - {CREATE_ACC, {acc_name, dom_id, pub_key}}, - {CREATE_DOMAIN, {dom_id, std::string("Default ") + role}}, - {CREATE_ASSET, {ast_name, dom_id, ast_precision}}, - {REMOVE_SIGN, {acc_id, pub_key}}, - {SET_QUO, {acc_id, quorum}}, - {SUB_ASSET_QTY, {}}, + command_params_map_ = { + {ADD_ASSET_QTY, + makeParamsDescription({acc_id, ast_id, amount_a, amount_b})}, + {ADD_PEER, makeParamsDescription({peer_id, pub_key})}, + {ADD_SIGN, makeParamsDescription({acc_id, pub_key})}, + {CREATE_ACC, makeParamsDescription({acc_name, dom_id, pub_key})}, + {CREATE_DOMAIN, + makeParamsDescription({dom_id, std::string("Default ") + role})}, + {CREATE_ASSET, + makeParamsDescription({ast_name, dom_id, ast_precision})}, + {REMOVE_SIGN, makeParamsDescription({acc_id, pub_key})}, + {SET_QUO, makeParamsDescription({acc_id, quorum})}, + {SUB_ASSET_QTY, makeParamsDescription({})}, {TRAN_ASSET, - {std::string("Src") + acc_id, - std::string("Dest") + acc_id, - ast_id, - amount_a, - amount_b}}, + makeParamsDescription({std::string("Src") + acc_id, + std::string("Dest") + acc_id, + ast_id, + amount_a, + amount_b})}, {CREATE_ROLE, - {role, - can_read_self, - can_edit_self, - can_read_all, - can_transfer_receive, - can_asset_creator, - can_create_domain, - can_roles, - can_create_account}}, - {APPEND_ROLE, {acc_id, role}}, - {DETACH_ROLE, {acc_id, role}}, - {GRANT_PERM, {acc_id, perm}}, - {REVOKE_PERM, {acc_id, perm}}, - {SET_ACC_KV, {acc_id, "key", "value"}} + makeParamsDescription({role, + can_read_self, + can_edit_self, + can_read_all, + can_transfer_receive, + can_asset_creator, + can_create_domain, + can_roles, + can_create_account})}, + {APPEND_ROLE, makeParamsDescription({acc_id, role})}, + {DETACH_ROLE, makeParamsDescription({acc_id, role})}, + {GRANT_PERM, makeParamsDescription({acc_id, perm})}, + {REVOKE_PERM, makeParamsDescription({acc_id, perm})}, + {SET_ACC_KV, makeParamsDescription({acc_id, "key", "value"})} // command parameters descriptions }; @@ -141,9 +144,8 @@ namespace iroha_cli { // Command parsers }; - commands_menu_ = formMenu(command_handlers_, - command_params_descriptions_, - commands_description_map_); + commands_menu_ = formMenu( + command_handlers_, command_params_map_, commands_description_map_); // Add "go back" option addBackOption(commands_menu_); } @@ -159,11 +161,10 @@ namespace iroha_cli { result_desciption.insert( {BACK_CODE, "Go back and start a new transaction"}); - result_params_descriptions = - getCommonParamsMap(default_peer_ip_, default_port_); + result_params_map = getCommonParamsMap(default_peer_ip_, default_port_); - result_params_descriptions.insert({ADD_CMD, {}}); - result_params_descriptions.insert({BACK_CODE, {}}); + result_params_map.insert({ADD_CMD, {}}); + result_params_map.insert({BACK_CODE, {}}); result_handlers_ = { {SAVE_CODE, &InteractiveTransactionCli::parseSaveFile}, @@ -173,8 +174,8 @@ namespace iroha_cli { // Parsers for result }; - result_menu_ = formMenu( - result_handlers_, result_params_descriptions, result_desciption); + result_menu_ = + formMenu(result_handlers_, result_params_map, result_desciption); } InteractiveTransactionCli::InteractiveTransactionCli( @@ -227,7 +228,7 @@ namespace iroha_cli { } auto res = handleParse>( - this, line, command_handlers_, command_params_descriptions_); + this, line, command_handlers_, command_params_map_); if (not res) { // Continue parsing @@ -457,8 +458,8 @@ namespace iroha_cli { bool InteractiveTransactionCli::parseResult(std::string line) { // Find in result handler map - auto res = handleParse( - this, line, result_handlers_, result_params_descriptions); + auto res = + handleParse(this, line, result_handlers_, result_params_map); return res.get_value_or(true); } diff --git a/iroha-cli/interactive/interactive_common_cli.hpp b/iroha-cli/interactive/interactive_common_cli.hpp index 27d3bc89dd..958d1fb9fc 100644 --- a/iroha-cli/interactive/interactive_common_cli.hpp +++ b/iroha-cli/interactive/interactive_common_cli.hpp @@ -19,8 +19,9 @@ #define IROHA_CLI_INTERACTIVE_COMMON_CLI_HPP #include -#include #include +#include +#include #include #include #include @@ -46,8 +47,23 @@ namespace iroha_cli { RESULT }; + /** + * Data structure for parameter data + */ + struct ParamData { + /** + * Message to display when prompting for user input + */ + std::string message; + + /** + * Cached user input for the parameter or the default value + */ + std::string cache; + }; + // Description of parameters - using ParamsDescription = std::vector; + using ParamsDescription = std::vector; // map for command - command description relationship using DescriptionMap = std::unordered_map; // Points in a menu @@ -68,11 +84,22 @@ namespace iroha_cli { /** * Return mapping of Command_name to parameters descriptions - * @return Map with parameters of common commands + * @param default_ip - default hostname or IP to be used when connecting to + * irohad + * @param default_port - default port to be used when connecting to irohad + * @return ParamsMap with parameters of common commands */ ParamsMap getCommonParamsMap(const std::string &default_ip, int default_port); + /** + * Creates parameters descriptions with empty default/cache values + * @param params - parameters as a vector of prompt messages + * @return ParamsDescription with parameter data + */ + ParamsDescription makeParamsDescription( + const std::vector ¶ms); + /** * Handle error with empty command */ @@ -108,7 +135,7 @@ namespace iroha_cli { * @param parameters needed to run the command */ void printCommandParameters(std::string &command, - std::vector parameters); + const ParamsDescription ¶meters); /** * Pretty Print of menu @@ -124,6 +151,13 @@ namespace iroha_cli { */ boost::optional promptString(const std::string &message); + /** + * Construct a prompt and get a string input from user + * @param param Parameter to collect the input for + * @return nullopt if termintaing symbol, else user's input + */ + boost::optional promptString(const ParamData ¶m); + /** * Parse parameters in interactive and shortcuted mode. * Function run interactive mode if in line only the command name is passed. @@ -136,7 +170,7 @@ namespace iroha_cli { * @return vector with needed parameters */ boost::optional> parseParams( - std::string line, std::string command_name, ParamsMap params_map); + std::string line, std::string command_name, ParamsMap ¶ms_map); /** * Add menu point to vector menu @@ -171,8 +205,8 @@ namespace iroha_cli { * @return nullopt if key not found, value if found */ template - boost::optional findInHandlerMap(K command_name, - std::unordered_map params_map) { + boost::optional findInHandlerMap( + K command_name, std::unordered_map ¶ms_map) { auto it = params_map.find(command_name); if (it == params_map.end()) { // Command not found, report error @@ -211,7 +245,7 @@ namespace iroha_cli { C class_pointer, std::string &line, std::unordered_map &parsers_map, - ParamsMap params_map) { + ParamsMap ¶ms_map) { auto raw_command = parser::parseFirstCommand(line); if (not raw_command) { handleEmptyCommand(); diff --git a/iroha-cli/interactive/interactive_query_cli.hpp b/iroha-cli/interactive/interactive_query_cli.hpp index a21003587b..ac1e2963f6 100644 --- a/iroha-cli/interactive/interactive_query_cli.hpp +++ b/iroha-cli/interactive/interactive_query_cli.hpp @@ -95,7 +95,7 @@ namespace iroha_cli { DescriptionMap description_map_; // Parameters descriptions of queries - ParamsMap query_params_descriptions_; + ParamsMap query_params_map_; /** * Parse line for query @@ -127,7 +127,7 @@ namespace iroha_cli { std::unordered_map result_handlers_; // Parameters descriptions of result commands - ParamsMap result_params_descriptions_; + ParamsMap result_params_map_; /** * Parse line for result diff --git a/iroha-cli/interactive/interactive_transaction_cli.hpp b/iroha-cli/interactive/interactive_transaction_cli.hpp index 5e8f2a9597..ee8a61eb75 100644 --- a/iroha-cli/interactive/interactive_transaction_cli.hpp +++ b/iroha-cli/interactive/interactive_transaction_cli.hpp @@ -99,7 +99,7 @@ namespace iroha_cli { std::unordered_map command_handlers_; // Descriptions for commands parameters - ParamsMap command_params_descriptions_; + ParamsMap command_params_map_; // Descriptions for commands DescriptionMap commands_description_map_; @@ -151,7 +151,7 @@ namespace iroha_cli { std::unordered_map result_handlers_; // Description for result command - ParamsMap result_params_descriptions; + ParamsMap result_params_map; /** * Parse line for result From bfbebc3b49dad46398c9271511657d3fa78c9b00 Mon Sep 17 00:00:00 2001 From: Kitsu Date: Thu, 28 Jun 2018 14:11:44 +0300 Subject: [PATCH 06/97] Migrate WSV to postgres bitstring perms (#1499) Signed-off-by: Kitsu --- .../ametsuchi/impl/postgres_wsv_command.cpp | 88 ++++++++++--------- irohad/ametsuchi/impl/postgres_wsv_query.cpp | 31 +++---- irohad/ametsuchi/impl/storage_impl.cpp | 38 +++----- shared_model/backend/protobuf/from_old.hpp | 35 -------- .../impl/proto_role_permissions_response.cpp | 1 - shared_model/interfaces/impl/permissions.cpp | 18 +++- shared_model/interfaces/permissions.hpp | 8 +- 7 files changed, 94 insertions(+), 125 deletions(-) delete mode 100644 shared_model/backend/protobuf/from_old.hpp diff --git a/irohad/ametsuchi/impl/postgres_wsv_command.cpp b/irohad/ametsuchi/impl/postgres_wsv_command.cpp index 0d80986cba..9e89b6ff3a 100644 --- a/irohad/ametsuchi/impl/postgres_wsv_command.cpp +++ b/irohad/ametsuchi/impl/postgres_wsv_command.cpp @@ -77,34 +77,21 @@ namespace iroha { WsvCommandResult PostgresWsvCommand::insertRolePermissions( const shared_model::interface::types::RoleIdType &role_id, const shared_model::interface::RolePermissionSet &permissions) { - auto entry = [this, &role_id](auto permission) { - return "(" + transaction_.quote(role_id) + ", " - + transaction_.quote(permission) + ")"; - }; - - // generate string with all permissions, - // applying transform_func to each permission - auto generate_perm_string = [&permissions](auto transform_func) { - std::string s; - permissions.iterate([&](auto perm) { - s += transform_func(shared_model::proto::permissions::toString(perm)) - + ','; - }); - if (s.size() > 0) { - // remove last comma - s.resize(s.size() - 1); - } - return s; - }; - + auto perm_str = permissions.toBitstring(); auto result = execute_( - "INSERT INTO role_has_permissions(role_id, permission) VALUES " - + generate_perm_string(entry) + ";"); + "INSERT INTO role_has_permissions(role_id, permission) VALUES (" + + transaction_.quote(role_id) + ", " + transaction_.quote(perm_str) + + " );"); auto message_gen = [&] { + // TODO(@l4l) 26/06/18 need to be simplified at IR-1479 + const auto &str = + shared_model::proto::permissions::toString(permissions); + const auto perm_debug_str = + std::accumulate(str.begin(), str.end(), std::string()); return (boost::format("failed to insert role permissions, role " "id: '%s', permissions: [%s]") - % role_id % generate_perm_string([](auto a) { return a; })) + % role_id % perm_debug_str) .str(); }; @@ -116,22 +103,30 @@ namespace iroha { &permittee_account_id, const shared_model::interface::types::AccountIdType &account_id, shared_model::interface::permissions::Grantable permission) { - auto result = execute_( - "INSERT INTO " - "account_has_grantable_permissions(permittee_account_id, " - "account_id, permission) VALUES (" - + transaction_.quote(permittee_account_id) + ", " - + transaction_.quote(account_id) + ", " - + transaction_.quote( - shared_model::proto::permissions::toString(permission)) - + ");"); + const auto perm_str = + shared_model::interface::GrantablePermissionSet({permission}) + .toBitstring(); + auto query = + (boost::format( + "INSERT INTO account_has_grantable_permissions as " + "has_perm(permittee_account_id, account_id, permission) VALUES " + "(%1%, %2%, %3%) ON CONFLICT (permittee_account_id, account_id) " + // SELECT will end up with a error, if the permission exists + "DO UPDATE SET permission=(SELECT has_perm.permission | %3% " + "WHERE (has_perm.permission & %3%) <> %3%);") + % transaction_.quote(permittee_account_id) + % transaction_.quote(account_id) % transaction_.quote(perm_str)) + .str(); + auto result = execute_(query); auto message_gen = [&] { return (boost::format("failed to insert account grantable permission, " "permittee account id: '%s', " "account id: '%s', " "permission: '%s'") - % permittee_account_id % account_id + % permittee_account_id + % account_id + // TODO(@l4l) 26/06/18 need to be simplified at IR-1479 % shared_model::proto::permissions::toString(permission)) .str(); }; @@ -144,21 +139,30 @@ namespace iroha { &permittee_account_id, const shared_model::interface::types::AccountIdType &account_id, shared_model::interface::permissions::Grantable permission) { - auto result = execute_( - "DELETE FROM public.account_has_grantable_permissions WHERE " - "permittee_account_id=" - + transaction_.quote(permittee_account_id) + " AND account_id=" - + transaction_.quote(account_id) + " AND permission=" - + transaction_.quote( - shared_model::proto::permissions::toString(permission)) - + " ;"); + const auto perm_str = shared_model::interface::GrantablePermissionSet() + .set() + .unset(permission) + .toBitstring(); + auto query = + (boost::format("UPDATE account_has_grantable_permissions as has_perm " + // SELECT will end up with a error, if the permission + // doesn't exists + "SET permission=(SELECT has_perm.permission & %3% " + "WHERE has_perm.permission & %3% = %3%) WHERE " + "permittee_account_id=%1% AND account_id=%2%;") + % transaction_.quote(permittee_account_id) + % transaction_.quote(account_id) % transaction_.quote(perm_str)) + .str(); + auto result = execute_(query); auto message_gen = [&] { return (boost::format("failed to delete account grantable permission, " "permittee account id: '%s', " "account id: '%s', " "permission id: '%s'") - % permittee_account_id % account_id + % permittee_account_id + % account_id + // TODO(@l4l) 26/06/18 need to be simplified at IR-1479 % shared_model::proto::permissions::toString(permission)) .str(); }; diff --git a/irohad/ametsuchi/impl/postgres_wsv_query.cpp b/irohad/ametsuchi/impl/postgres_wsv_query.cpp index c63f90d60f..47b1eda0cf 100644 --- a/irohad/ametsuchi/impl/postgres_wsv_query.cpp +++ b/irohad/ametsuchi/impl/postgres_wsv_query.cpp @@ -16,7 +16,6 @@ */ #include "ametsuchi/impl/postgres_wsv_query.hpp" -#include "backend/protobuf/from_old.hpp" #include "backend/protobuf/permissions.hpp" namespace iroha { @@ -54,15 +53,16 @@ namespace iroha { const AccountIdType &permitee_account_id, const AccountIdType &account_id, shared_model::interface::permissions::Grantable permission) { + const auto perm_str = + shared_model::interface::GrantablePermissionSet({permission}) + .toBitstring(); return execute_( "SELECT * FROM account_has_grantable_permissions WHERE " "permittee_account_id = " + transaction_.quote(permitee_account_id) + " AND account_id = " + transaction_.quote(account_id) - + " AND permission = " - + transaction_.quote( - shared_model::proto::permissions::toString(permission)) - + ";") + + " AND permission & " + transaction_.quote(perm_str) + " = " + + transaction_.quote(perm_str) + ";") | [](const auto &result) { return result.size() == 1; }; } @@ -81,17 +81,18 @@ namespace iroha { boost::optional PostgresWsvQuery::getRolePermissions(const RoleIdType &role_name) { return execute_( - "SELECT permission FROM role_has_permissions WHERE role_id " - "= " + "SELECT permission FROM role_has_permissions WHERE role_id = " + transaction_.quote(role_name) + ";") - | [&](const auto &result) { - shared_model::interface::RolePermissionSet set; - for (const auto &r : result) { - set.set(shared_model::interface::permissions::fromOldR( - r.at("permission").c_str())); - } - return set; - }; + | [&](const auto &result) + -> boost::optional< + shared_model::interface::RolePermissionSet> { + // TODO(@l4l) 26/06/18 remove with IR-1480 + if (result.empty()) { + return shared_model::interface::RolePermissionSet(); + } + return shared_model::interface::RolePermissionSet( + std::string(result.at(0).at("permission").c_str())); + }; } boost::optional> PostgresWsvQuery::getRoles() { diff --git a/irohad/ametsuchi/impl/storage_impl.cpp b/irohad/ametsuchi/impl/storage_impl.cpp index 89546016f6..566f008d05 100644 --- a/irohad/ametsuchi/impl/storage_impl.cpp +++ b/irohad/ametsuchi/impl/storage_impl.cpp @@ -302,31 +302,8 @@ DROP TABLE IF EXISTS index_by_id_height_asset; return notifier_.get_observable(); } - template - static const std::string createPermissionTypes( - const std::string &type_name) { - std::string s = - "DO $$\nBEGIN\nIF NOT EXISTS (SELECT 1 FROM pg_type " - "WHERE typname='" + type_name + "')" - " THEN CREATE TYPE " + type_name + " AS ENUM ("; - const auto count = static_cast(Perm::COUNT); - for (size_t i = 0; i < count; ++i) { - s += "'" - + shared_model::proto::permissions::toString(static_cast(i)) - + "'"; - if (i != count - 1) { - s += ','; - } - } - return s + ");\nEND IF;\nEND $$;"; - } - const std::string &StorageImpl::init_ = - createPermissionTypes( - "role_perm") - + createPermissionTypes< - shared_model::interface::permissions::Grantable>("grantable_perm") - + R"( + R"( CREATE TABLE IF NOT EXISTS role ( role_id character varying(32), PRIMARY KEY (role_id) @@ -372,8 +349,10 @@ CREATE TABLE IF NOT EXISTS account_has_asset ( ); CREATE TABLE IF NOT EXISTS role_has_permissions ( role_id character varying(32) NOT NULL REFERENCES role, - permission role_perm, - PRIMARY KEY (role_id, permission) + permission bit()" + + std::to_string(shared_model::interface::RolePermissionSet::size()) + + R"() NOT NULL, + PRIMARY KEY (role_id) ); CREATE TABLE IF NOT EXISTS account_has_roles ( account_id character varying(288) NOT NULL REFERENCES account, @@ -383,8 +362,11 @@ CREATE TABLE IF NOT EXISTS account_has_roles ( CREATE TABLE IF NOT EXISTS account_has_grantable_permissions ( permittee_account_id character varying(288) NOT NULL REFERENCES account, account_id character varying(288) NOT NULL REFERENCES account, - permission grantable_perm, - PRIMARY KEY (permittee_account_id, account_id, permission) + permission bit()" + + std::to_string( + shared_model::interface::GrantablePermissionSet::size()) + + R"() NOT NULL, + PRIMARY KEY (permittee_account_id, account_id) ); CREATE TABLE IF NOT EXISTS height_by_hash ( hash bytea, diff --git a/shared_model/backend/protobuf/from_old.hpp b/shared_model/backend/protobuf/from_old.hpp deleted file mode 100644 index 9407c1e1a9..0000000000 --- a/shared_model/backend/protobuf/from_old.hpp +++ /dev/null @@ -1,35 +0,0 @@ -/** - * Copyright Soramitsu Co., Ltd. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -#ifndef SHARED_MODEL_PROTOBUF_FROM_OLD -#define SHARED_MODEL_PROTOBUF_FROM_OLD - -#include -#include -#include "backend/protobuf/permissions.hpp" - -namespace shared_model { - namespace interface { - namespace permissions { - static inline Role fromOldR(const std::string &s) { - iroha::protocol::RolePermission role; - if (not RolePermission_Parse(s, &role)) { - throw std::invalid_argument(s); - } - return proto::permissions::fromTransport(role); - } - - static inline RolePermissionSet fromOldR(const std::set &s) { - RolePermissionSet set{}; - for (auto &el : s) { - set.set(fromOldR(el)); - } - return set; - } - } // namespace permissions - } // namespace interface -} // namespace shared_model - -#endif // SHARED_MODEL_PROTOBUF_FROM_OLD diff --git a/shared_model/backend/protobuf/query_responses/impl/proto_role_permissions_response.cpp b/shared_model/backend/protobuf/query_responses/impl/proto_role_permissions_response.cpp index 32c68bb1c2..3cd47c5941 100644 --- a/shared_model/backend/protobuf/query_responses/impl/proto_role_permissions_response.cpp +++ b/shared_model/backend/protobuf/query_responses/impl/proto_role_permissions_response.cpp @@ -5,7 +5,6 @@ #include "backend/protobuf/query_responses/proto_role_permissions_response.hpp" #include -#include "backend/protobuf/from_old.hpp" #include "backend/protobuf/permissions.hpp" #include "utils/string_builder.hpp" diff --git a/shared_model/interfaces/impl/permissions.cpp b/shared_model/interfaces/impl/permissions.cpp index 1322a9f9d8..78626682ca 100644 --- a/shared_model/interfaces/impl/permissions.cpp +++ b/shared_model/interfaces/impl/permissions.cpp @@ -57,8 +57,16 @@ PermissionSet::PermissionSet(std::initializer_list list) { } template -size_t PermissionSet::size() const { - return Parent::size(); +PermissionSet::PermissionSet(const std::string &bitstring) : Parent(bitstring) {} + +template +std::string PermissionSet::toBitstring() const { + return Parent::to_string(); +} + +template +size_t PermissionSet::size() { + return bit(Perm::COUNT); } template @@ -67,6 +75,12 @@ PermissionSet &PermissionSet::reset() { return *this; } +template +PermissionSet &PermissionSet::set() { + Parent::set(); + return *this; +} + template PermissionSet &PermissionSet::set(Perm p) { Parent::set(bit(p), true); diff --git a/shared_model/interfaces/permissions.hpp b/shared_model/interfaces/permissions.hpp index fd83374c49..75165c91b4 100644 --- a/shared_model/interfaces/permissions.hpp +++ b/shared_model/interfaces/permissions.hpp @@ -9,7 +9,7 @@ #include #include #include -#include +#include namespace shared_model { namespace interface { @@ -96,9 +96,13 @@ namespace shared_model { public: PermissionSet(); PermissionSet(std::initializer_list list); + explicit PermissionSet(const std::string &bitstring); - size_t size() const; + std::string toBitstring() const; + + static size_t size(); PermissionSet &reset(); + PermissionSet &set(); PermissionSet &set(Perm p); PermissionSet &unset(Perm p); From 6741843b52be7aa44880e948f0b68fb03ef5c30b Mon Sep 17 00:00:00 2001 From: Kitsu Date: Fri, 29 Jun 2018 12:42:42 +0300 Subject: [PATCH 07/97] Change PermissionSet constraints (#1510) * Remove PermissionSet constraints for emptiness * Add CreateRole permissions validation per element * Add InvalidCreateRolePermission validator test Signed-off-by: Kitsu --- shared_model/validators/field_validator.cpp | 18 ------------ shared_model/validators/field_validator.hpp | 8 ------ .../validators/transaction_validator.hpp | 11 ++++++-- .../acceptance/create_role_test.cpp | 4 ++- .../validators/transaction_validator_test.cpp | 28 +++++++++++++++---- 5 files changed, 34 insertions(+), 35 deletions(-) diff --git a/shared_model/validators/field_validator.cpp b/shared_model/validators/field_validator.cpp index 9f11e2684d..0ec42c4c7a 100644 --- a/shared_model/validators/field_validator.cpp +++ b/shared_model/validators/field_validator.cpp @@ -250,24 +250,6 @@ namespace shared_model { } } - void FieldValidator::validateRolePermissions( - ReasonsGroupType &reason, - const interface::RolePermissionSet &permissions) const { - if (permissions.none()) { - reason.second.push_back( - "Permission set should contain at least one permission"); - } - } - - void FieldValidator::validateGrantablePermissions( - ReasonsGroupType &reason, - const interface::GrantablePermissionSet &permissions) const { - if (permissions.none()) { - reason.second.push_back( - "Permission set should contain at least one permission"); - } - } - void FieldValidator::validateQuorum( ReasonsGroupType &reason, const interface::types::QuorumType &quorum) const { diff --git a/shared_model/validators/field_validator.hpp b/shared_model/validators/field_validator.hpp index 24cc829948..02a2bade9f 100644 --- a/shared_model/validators/field_validator.hpp +++ b/shared_model/validators/field_validator.hpp @@ -116,14 +116,6 @@ namespace shared_model { ReasonsGroupType &reason, const interface::permissions::Grantable &permission) const; - void validateRolePermissions( - ReasonsGroupType &reason, - const interface::RolePermissionSet &permissions) const; - - void validateGrantablePermissions( - ReasonsGroupType &reason, - const interface::GrantablePermissionSet &permissions) const; - void validateQuorum(ReasonsGroupType &reason, const interface::types::QuorumType &quorum) const; diff --git a/shared_model/validators/transaction_validator.hpp b/shared_model/validators/transaction_validator.hpp index 8fd3f257fb..c364d383cd 100644 --- a/shared_model/validators/transaction_validator.hpp +++ b/shared_model/validators/transaction_validator.hpp @@ -22,7 +22,7 @@ #include #include "backend/protobuf/permissions.hpp" -#include "interfaces/transaction.hpp" +#include "backend/protobuf/transaction.hpp" #include "validators/answer.hpp" namespace shared_model { @@ -118,7 +118,14 @@ namespace shared_model { addInvalidCommand(reason, "CreateRole"); validator_.validateRoleId(reason, cr.roleName()); - validator_.validateRolePermissions(reason, cr.rolePermissions()); + for (auto i : static_cast(cr) + .getTransport() + .create_role() + .permissions()) { + validator_.validateRolePermission( + reason, + static_cast(i)); + } return reason; } diff --git a/test/integration/acceptance/create_role_test.cpp b/test/integration/acceptance/create_role_test.cpp index f6c9fb0478..e9534cd925 100644 --- a/test/integration/acceptance/create_role_test.cpp +++ b/test/integration/acceptance/create_role_test.cpp @@ -98,7 +98,9 @@ TEST_F(CreateRole, EmptyPerms) { .sendTx(makeUserWithPerms()) .skipProposal() .skipBlock() - .sendTx(complete(baseTx({})), checkStatelessInvalid); + .sendTxAwait(complete(baseTx({})), [](auto &block) { + ASSERT_EQ(block->transactions().size(), 1); + }); } /** diff --git a/test/module/shared_model/validators/transaction_validator_test.cpp b/test/module/shared_model/validators/transaction_validator_test.cpp index cbf2d1b0c4..b421bafb7b 100644 --- a/test/module/shared_model/validators/transaction_validator_test.cpp +++ b/test/module/shared_model/validators/transaction_validator_test.cpp @@ -51,8 +51,26 @@ TEST_F(TransactionValidatorTest, EmptyTransactionTest) { tx.mutable_payload()->set_created_time(created_time); shared_model::validation::DefaultTransactionValidator transaction_validator; auto result = proto::Transaction(iroha::protocol::Transaction(tx)); - auto answer = - transaction_validator.validate(result); + auto answer = transaction_validator.validate(result); + ASSERT_EQ(answer.getReasonsMap().size(), 1); +} + +/** + * @given transaction without any commands + * @when commands validator is invoked + * @then answer has error about empty transaction + */ +TEST_F(TransactionValidatorTest, InvalidCreateRolePermission) { + auto tx = generateEmptyTransaction(); + tx.mutable_payload()->set_created_time(created_time); + iroha::protocol::Command cmd; + cmd.mutable_create_role()->set_role_name("role"); + cmd.mutable_create_role()->add_permissions( + static_cast(-1)); + *tx.mutable_payload()->add_commands() = std::move(cmd); + shared_model::validation::DefaultTransactionValidator transaction_validator; + auto result = proto::Transaction(iroha::protocol::Transaction(tx)); + auto answer = transaction_validator.validate(result); ASSERT_EQ(answer.getReasonsMap().size(), 1); } @@ -85,8 +103,7 @@ TEST_F(TransactionValidatorTest, StatelessValidTest) { shared_model::validation::DefaultTransactionValidator transaction_validator; auto result = proto::Transaction(iroha::protocol::Transaction(tx)); - auto answer = - transaction_validator.validate(result); + auto answer = transaction_validator.validate(result); ASSERT_FALSE(answer.hasErrors()) << answer.reason(); } @@ -121,8 +138,7 @@ TEST_F(TransactionValidatorTest, StatelessInvalidTest) { shared_model::validation::DefaultTransactionValidator transaction_validator; auto result = proto::Transaction(iroha::protocol::Transaction(tx)); - auto answer = - transaction_validator.validate(result); + auto answer = transaction_validator.validate(result); // in total there should be number_of_commands + 1 reasons of bad answer: // number_of_commands for each command + 1 for transaction metadata From 6779ae0714e8775caaad8c985a4a20df0714fcac Mon Sep 17 00:00:00 2001 From: Kitsu Date: Fri, 29 Jun 2018 13:53:45 +0300 Subject: [PATCH 08/97] Fix error message for Peer's address (#1513) Signed-off-by: Kitsu --- shared_model/validators/field_validator.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/shared_model/validators/field_validator.cpp b/shared_model/validators/field_validator.cpp index 0ec42c4c7a..a7d44813f8 100644 --- a/shared_model/validators/field_validator.cpp +++ b/shared_model/validators/field_validator.cpp @@ -132,9 +132,10 @@ namespace shared_model { const interface::types::AddressType &address) const { if (not std::regex_match(address, peer_address_regex_)) { auto message = - (boost::format("Wrongly formed peer address, passed value: '%s'. " - "Field should have valid IPv4 format or be a valid " - "hostname following RFC1123 specification") + (boost::format( + "Wrongly formed peer address, passed value: '%s'. " + "Field should have 'host:port' format where host is IPv4 or a " + "hostname following RFC1035, RFC1123 specifications") % address) .str(); reason.second.push_back(std::move(message)); From aff0e217645a20f94934a0320a562648e97ecfe2 Mon Sep 17 00:00:00 2001 From: Andrei Lebedev Date: Wed, 27 Jun 2018 13:54:39 +0300 Subject: [PATCH 09/97] Fix type in keys manager std::max (#1503) Signed-off-by: Andrei Lebedev --- libs/crypto/keys_manager_impl.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/libs/crypto/keys_manager_impl.cpp b/libs/crypto/keys_manager_impl.cpp index f9d7d968f8..b557e5f447 100644 --- a/libs/crypto/keys_manager_impl.cpp +++ b/libs/crypto/keys_manager_impl.cpp @@ -37,11 +37,12 @@ namespace iroha { template static std::string encrypt(const T &key, const std::string &pass_phrase) { std::string ciphertext(key.size(), '\0'); + const size_t min_pass_size = 1; // pass_size will always be > 0 - const auto pass_size = std::max(1ul, pass_phrase.size()); + const auto pass_size = std::max(min_pass_size, pass_phrase.size()); // When pass_phrase is empty it, pass_phrase[0] is "\0", so no out_of_range // exception is possible - for (auto i = 0u; i < key.size(); i++) { + for (size_t i = 0; i < key.size(); i++) { ciphertext[i] = key[i] ^ pass_phrase[i % pass_size]; } return ciphertext; From f2a98ea37db34227a5a458d0147f5e6a0e75c5d6 Mon Sep 17 00:00:00 2001 From: Dumitru Date: Fri, 29 Jun 2018 13:40:56 +0200 Subject: [PATCH 10/97] Fix error message (#1514) Signed-off-by: Dumitru --- shared_model/validators/field_validator.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/shared_model/validators/field_validator.cpp b/shared_model/validators/field_validator.cpp index a7d44813f8..34b34e9112 100644 --- a/shared_model/validators/field_validator.cpp +++ b/shared_model/validators/field_validator.cpp @@ -132,10 +132,10 @@ namespace shared_model { const interface::types::AddressType &address) const { if (not std::regex_match(address, peer_address_regex_)) { auto message = - (boost::format( - "Wrongly formed peer address, passed value: '%s'. " - "Field should have 'host:port' format where host is IPv4 or a " - "hostname following RFC1035, RFC1123 specifications") + (boost::format("Wrongly formed peer address, passed value: '%s'. " + "Field should have a valid 'host:port' format where " + "host is IPv4 or a " + "hostname following RFC1035, RFC1123 specifications") % address) .str(); reason.second.push_back(std::move(message)); From 89d74d06a3be655469e84442849a08a40a26493c Mon Sep 17 00:00:00 2001 From: Artyom Bakhtin Date: Thu, 28 Jun 2018 11:17:10 +0300 Subject: [PATCH 11/97] Add java package name for bindings Signed-off-by: Artyom Bakhtin --- .jenkinsci/bindings.groovy | 7 +++++-- Jenkinsfile | 5 +++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/.jenkinsci/bindings.groovy b/.jenkinsci/bindings.groovy index ae49bc57db..26212c96d7 100644 --- a/.jenkinsci/bindings.groovy +++ b/.jenkinsci/bindings.groovy @@ -1,6 +1,6 @@ #!/usr/bin/env groovy -def doJavaBindings(os, buildType=Release) { +def doJavaBindings(os, packageName, buildType=Release) { def currentPath = sh(script: "pwd", returnStdout: true).trim() def commit = env.GIT_COMMIT def artifactsPath = sprintf('%1$s/java-bindings-%2$s-%3$s-%4$s-%5$s.zip', @@ -20,12 +20,15 @@ def doJavaBindings(os, buildType=Release) { -Bbuild \ -DCMAKE_BUILD_TYPE=$buildType \ -DSWIG_JAVA=ON \ + -DSWIG_JAVA_PKG="$packageName" \ ${cmakeOptions} """ def parallelismParam = (os == 'windows') ? '' : "-j${params.PARALLELISM}" sh "cmake --build build --target irohajava -- ${parallelismParam}" // TODO 29.05.18 @bakhtin Java tests never finishes on Windows Server 2016. IR-1380 - sh "zip -j $artifactsPath build/bindings/*.java build/bindings/*.dll build/bindings/libirohajava.so" + sh "pushd build/bindings; \ + zip -r $artifactsPath *.dll *.lib *.manifest *.exp libirohajava.so \$(echo ${packageName} | cut -d '.' -f1); \ + popd" if (os == 'windows') { sh "cp $artifactsPath /tmp/${env.GIT_COMMIT}/bindings-artifact" } diff --git a/Jenkinsfile b/Jenkinsfile index 2fd2412580..14b2b4de7f 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -9,6 +9,7 @@ properties([parameters([ choice(choices: 'Debug\nRelease', description: 'Iroha build type', name: 'build_type'), booleanParam(defaultValue: false, description: 'Build Java bindings', name: 'JavaBindings'), choice(choices: 'Release\nDebug', description: 'Java bindings build type', name: 'JBBuildType'), + string(defaultValue: 'jp.co.soramitsu.iroha', description: 'Java bindings package name', name: 'JBPackageName'), booleanParam(defaultValue: false, description: 'Build Python bindings', name: 'PythonBindings'), choice(choices: 'Release\nDebug', description: 'Python bindings build type', name: 'PBBuildType'), choice(choices: 'python3\npython2', description: 'Python bindings version', name: 'PBVersion'), @@ -410,7 +411,7 @@ pipeline { ['PARALLELISM': params.PARALLELISM]) if (params.JavaBindings) { iC.inside("-v /tmp/${env.GIT_COMMIT}/bindings-artifact:/tmp/bindings-artifact") { - bindings.doJavaBindings('linux', params.JBBuildType) + bindings.doJavaBindings('linux', params.JBPackageName, params.JBBuildType) } } if (params.PythonBindings) { @@ -469,7 +470,7 @@ pipeline { script { def bindings = load ".jenkinsci/bindings.groovy" if (params.JavaBindings) { - bindings.doJavaBindings('windows', params.JBBuildType) + bindings.doJavaBindings('windows', params.JBPackageName, params.JBBuildType) } if (params.PythonBindings) { bindings.doPythonBindings('windows', params.PBBuildType) From 1037191a34deb19b4ac0529e20b5ba472fdbcab2 Mon Sep 17 00:00:00 2001 From: Anatoly Date: Fri, 29 Jun 2018 17:24:09 +0300 Subject: [PATCH 12/97] New CI pipeline (#1392) Signed-off-by: Anatoly --- .jenkinsci/build-coverage.groovy | 15 + .jenkinsci/debug-build.groovy | 160 +++--- .jenkinsci/docker-cleanup.groovy | 26 +- .jenkinsci/docker-pull-or-build.groovy | 44 +- .jenkinsci/enums.groovy | 12 + .jenkinsci/github-api.groovy | 137 +++++ .jenkinsci/linux-post-step.groovy | 20 - .jenkinsci/mac-debug-build.groovy | 44 ++ .jenkinsci/mac-release-build.groovy | 8 +- .jenkinsci/notifications.groovy | 61 +++ .jenkinsci/post-step.groovy | 40 ++ .jenkinsci/pre-build.groovy | 14 + .jenkinsci/release-build.groovy | 13 +- .jenkinsci/selected-branches-coverage.groovy | 13 - .jenkinsci/set-parallelism.groovy | 19 + .jenkinsci/test-launcher.groovy | 41 ++ Jenkinsfile | 517 +++++++++++-------- 17 files changed, 838 insertions(+), 346 deletions(-) create mode 100644 .jenkinsci/build-coverage.groovy create mode 100644 .jenkinsci/enums.groovy create mode 100644 .jenkinsci/github-api.groovy delete mode 100644 .jenkinsci/linux-post-step.groovy create mode 100644 .jenkinsci/mac-debug-build.groovy create mode 100644 .jenkinsci/notifications.groovy create mode 100644 .jenkinsci/post-step.groovy create mode 100644 .jenkinsci/pre-build.groovy delete mode 100644 .jenkinsci/selected-branches-coverage.groovy create mode 100644 .jenkinsci/set-parallelism.groovy create mode 100644 .jenkinsci/test-launcher.groovy diff --git a/.jenkinsci/build-coverage.groovy b/.jenkinsci/build-coverage.groovy new file mode 100644 index 0000000000..2e9a050694 --- /dev/null +++ b/.jenkinsci/build-coverage.groovy @@ -0,0 +1,15 @@ +#!/usr/bin/env groovy + +def checkCoverageConditions() { + // trigger coverage if branch is master, or it is a open PR commit, or a merge request + def branch_coverage = ['master'] + + if ( params.coverage ) { + return true + } + else { + return env.GIT_LOCAL_BRANCH in branch_coverage || INITIAL_COMMIT_PR == "true" || MERGE_CONDITIONS_SATISFIED == "true" + } +} + +return this diff --git a/.jenkinsci/debug-build.groovy b/.jenkinsci/debug-build.groovy index 47087c07eb..52ad96d383 100644 --- a/.jenkinsci/debug-build.groovy +++ b/.jenkinsci/debug-build.groovy @@ -4,26 +4,20 @@ def doDebugBuild(coverageEnabled=false) { def dPullOrBuild = load ".jenkinsci/docker-pull-or-build.groovy" def manifest = load ".jenkinsci/docker-manifest.groovy" def pCommit = load ".jenkinsci/previous-commit.groovy" - def parallelism = params.PARALLELISM + def setter = load ".jenkinsci/set-parallelism.groovy" + def parallelism = setter.setParallelism(params.PARALLELISM) def platform = sh(script: 'uname -m', returnStdout: true).trim() - def previousCommit = pCommit.previousCommitOrCurrent() + def prevCommit = pCommit.previousCommitOrCurrent() // params are always null unless job is started // this is the case for the FIRST build only. // So just set this to same value as default. // This is a known bug. See https://issues.jenkins-ci.org/browse/JENKINS-41929 - if (!parallelism) { - parallelism = 4 - } - if (env.NODE_NAME.contains('arm7')) { - parallelism = 1 - } - sh "docker network create ${env.IROHA_NETWORK}" + def iC = dPullOrBuild.dockerPullOrUpdate("${platform}-develop-build", "${env.GIT_RAW_BASE_URL}/${env.GIT_COMMIT}/docker/develop/Dockerfile", - "${env.GIT_RAW_BASE_URL}/${previousCommit}/docker/develop/Dockerfile", + "${env.GIT_RAW_BASE_URL}/${prevCommit}/docker/develop/Dockerfile", "${env.GIT_RAW_BASE_URL}/develop/docker/develop/Dockerfile", ['PARALLELISM': parallelism]) - if (GIT_LOCAL_BRANCH == 'develop' && manifest.manifestSupportEnabled()) { manifest.manifestCreate("${DOCKER_REGISTRY_BASENAME}:develop-build", ["${DOCKER_REGISTRY_BASENAME}:x86_64-develop-build", @@ -42,6 +36,61 @@ def doDebugBuild(coverageEnabled=false) { manifest.manifestPush("${DOCKER_REGISTRY_BASENAME}:develop-build", login, password) } } + + iC.inside("" + + " -e IROHA_POSTGRES_HOST=${env.IROHA_POSTGRES_HOST}" + + " -e IROHA_POSTGRES_PORT=${env.IROHA_POSTGRES_PORT}" + + " -e IROHA_POSTGRES_USER=${env.IROHA_POSTGRES_USER}" + + " -e IROHA_POSTGRES_PASSWORD=${env.IROHA_POSTGRES_PASSWORD}" + + " -v ${CCACHE_DIR}:${CCACHE_DIR}") { + def scmVars = checkout scm + def cmakeOptions = "" + + if ( coverageEnabled ) { + cmakeOptions = " -DCOVERAGE=ON " + } + env.IROHA_VERSION = "0x${scmVars.GIT_COMMIT}" + env.IROHA_HOME = "/opt/iroha" + env.IROHA_BUILD = "${env.IROHA_HOME}/build" + + sh """ + ccache --version + ccache --show-stats + ccache --zero-stats + ccache --max-size=5G + """ + sh """ + cmake \ + -DTESTING=ON \ + -H. \ + -Bbuild \ + -DCMAKE_BUILD_TYPE=${params.build_type} \ + -DIROHA_VERSION=${env.IROHA_VERSION} \ + ${cmakeOptions} + """ + sh "cmake --build build -- -j${parallelism}" + sh "ccache --show-stats" + } +} + +// do not implement any checks as coverage runs only on x86_64 +def doPreCoverageStep() { + sh "docker load -i ${JENKINS_DOCKER_IMAGE_DIR}/${DOCKER_IMAGE_FILE}" + def iC = docker.image("${DOCKER_AGENT_IMAGE}") + iC.inside() { + sh "cmake --build build --target coverage.init.info" + } +} + +// run specified test categories +def doTestStep(testList) { + if (env.NODE_NAME.contains('x86_64')) { + sh "docker load -i ${JENKINS_DOCKER_IMAGE_DIR}/${DOCKER_IMAGE_FILE}" + } + + def iC = docker.image("${DOCKER_AGENT_IMAGE}") + sh "docker network create ${env.IROHA_NETWORK}" + docker.image('postgres:9.5').withRun("" + " -e POSTGRES_USER=${env.IROHA_POSTGRES_USER}" + " -e POSTGRES_PASSWORD=${env.IROHA_POSTGRES_PASSWORD}" @@ -52,63 +101,44 @@ def doDebugBuild(coverageEnabled=false) { + " -e IROHA_POSTGRES_PORT=${env.IROHA_POSTGRES_PORT}" + " -e IROHA_POSTGRES_USER=${env.IROHA_POSTGRES_USER}" + " -e IROHA_POSTGRES_PASSWORD=${env.IROHA_POSTGRES_PASSWORD}" - + " --network=${env.IROHA_NETWORK}" - + " -v /var/jenkins/ccache:${CCACHE_DIR}" - + " -v /tmp/${GIT_COMMIT}-${BUILD_NUMBER}:/tmp/${GIT_COMMIT}") { - - def scmVars = checkout scm - def cmakeOptions = "" - if ( coverageEnabled ) { - cmakeOptions = " -DCOVERAGE=ON " - } - env.IROHA_VERSION = "0x${scmVars.GIT_COMMIT}" - env.IROHA_HOME = "/opt/iroha" - env.IROHA_BUILD = "${env.IROHA_HOME}/build" - - sh """ - ccache --version - ccache --show-stats - ccache --zero-stats - ccache --max-size=5G - """ - sh """ - cmake \ - -DTESTING=ON \ - -H. \ - -Bbuild \ - -DCMAKE_BUILD_TYPE=Debug \ - -DIROHA_VERSION=${env.IROHA_VERSION} \ - ${cmakeOptions} - """ - sh "cmake --build build -- -j${parallelism}" - sh "ccache --show-stats" - if ( coverageEnabled ) { - sh "cmake --build build --target coverage.init.info" - } - def testExitCode = sh(script: """cd build && ctest --output-on-failure""", returnStatus: true) - if (testExitCode != 0) { - currentBuild.result = "UNSTABLE" - } - if ( coverageEnabled ) { - sh "cmake --build build --target cppcheck" - // Sonar - if (env.CHANGE_ID != null) { - sh """ - sonar-scanner \ - -Dsonar.github.disableInlineComments \ - -Dsonar.github.repository='${DOCKER_REGISTRY_BASENAME}' \ - -Dsonar.analysis.mode=preview \ - -Dsonar.login=${SONAR_TOKEN} \ - -Dsonar.projectVersion=${BUILD_TAG} \ - -Dsonar.github.oauth=${SORABOT_TOKEN} \ - -Dsonar.github.pullRequest=${CHANGE_ID} - """ + + " --network=${env.IROHA_NETWORK}") { + def testExitCode = sh(script: """cd build && ctest --output-on-failure -R '${testList}' """, returnStatus: true) + if (testExitCode != 0) { + currentBuild.result = "UNSTABLE" } - sh "cmake --build build --target coverage.info" - sh "python /tmp/lcov_cobertura.py build/reports/coverage.info -o build/reports/coverage.xml" - cobertura autoUpdateHealth: false, autoUpdateStability: false, coberturaReportFile: '**/build/reports/coverage.xml', conditionalCoverageTargets: '75, 50, 0', failUnhealthy: false, failUnstable: false, lineCoverageTargets: '75, 50, 0', maxNumberOfBuilds: 50, methodCoverageTargets: '75, 50, 0', onlyStable: false, zoomCoverageChart: false } } +} + +// do cobertura analysis +// same as for doPreCoverageStep() +def doPostCoverageCoberturaStep() { + sh "docker load -i ${JENKINS_DOCKER_IMAGE_DIR}/${DOCKER_IMAGE_FILE}" + def iC = docker.image("${DOCKER_AGENT_IMAGE}") + iC.inside() { + sh "cmake --build build --target coverage.info" + sh "python /tmp/lcov_cobertura.py build/reports/coverage.info -o build/reports/coverage.xml" + cobertura autoUpdateHealth: false, autoUpdateStability: false, coberturaReportFile: '**/build/reports/coverage.xml', conditionalCoverageTargets: '75, 50, 0', failUnhealthy: false, failUnstable: false, lineCoverageTargets: '75, 50, 0', maxNumberOfBuilds: 50, methodCoverageTargets: '75, 50, 0', onlyStable: false, zoomCoverageChart: false + } +} + +// do cppcheck analysis +// same as for doPreCoverageStep() +def doPostCoverageSonarStep() { + sh "docker load -i ${JENKINS_DOCKER_IMAGE_DIR}/${DOCKER_IMAGE_FILE}" + def iC = docker.image("${DOCKER_AGENT_IMAGE}") + iC.inside() { + sh "cmake --build build --target cppcheck" + sh """ + sonar-scanner \ + -Dsonar.github.disableInlineComments \ + -Dsonar.github.repository='hyperledger/iroha' \ + -Dsonar.analysis.mode=preview \ + -Dsonar.login=${SONAR_TOKEN} \ + -Dsonar.projectVersion=${BUILD_TAG} \ + -Dsonar.github.oauth=${SORABOT_TOKEN} \ + -Dsonar.github.pullRequest=${CHANGE_ID} + """ } } diff --git a/.jenkinsci/docker-cleanup.groovy b/.jenkinsci/docker-cleanup.groovy index c789383b28..b4eda7a04c 100644 --- a/.jenkinsci/docker-cleanup.groovy +++ b/.jenkinsci/docker-cleanup.groovy @@ -1,17 +1,23 @@ #!/usr/bin/env groovy +// remove docker network and stale images def doDockerCleanup() { + sh """ + # Check whether the image is the last-standing man + # i.e., no other tags exist for this image + docker rmi \$(docker images --no-trunc --format '{{.Repository}}:{{.Tag}}\\t{{.ID}}' | grep \$(docker images --no-trunc --format '{{.ID}}' ${iC.id}) | head -n -1 | cut -f 1) || true + """ +} + +// cleanup docker network created for the test stage +def doDockerNetworkCleanup() { + sh "docker network rm ${env.IROHA_NETWORK}" +} - sh """ - # Check whether the image is the last-standing man - # i.e., no other tags exist for this image - docker rmi \$(docker images --no-trunc --format '{{.Repository}}:{{.Tag}}\\t{{.ID}}' | grep \$(docker images --no-trunc --format '{{.ID}}' ${iC.id}) | head -n -1 | cut -f 1) || true - sleep 5 - docker network rm $IROHA_NETWORK || true - #remove folder with iroha.deb package and Dockerfiles - rm -rf /tmp/${env.GIT_COMMIT}-${BUILD_NUMBER} - rm -rf /tmp/${env.GIT_COMMIT} - """ +// cleanup docker images which weren't used for more that 20 days and image for this PR in case of successful PR +def doStaleDockerImagesCleanup() { + sh "find ${JENKINS_DOCKER_IMAGE_DIR} -type f -mtime +20 -exec rm -f {} \\;" + sh "rm -f ${JENKINS_DOCKER_IMAGE_DIR}/${DOCKER_IMAGE_FILE}" } return this diff --git a/.jenkinsci/docker-pull-or-build.groovy b/.jenkinsci/docker-pull-or-build.groovy index d699ac40bb..9d74931db8 100644 --- a/.jenkinsci/docker-pull-or-build.groovy +++ b/.jenkinsci/docker-pull-or-build.groovy @@ -19,31 +19,52 @@ def buildOptionsString(options) { def dockerPullOrUpdate(imageName, currentDockerfileURL, previousDockerfileURL, referenceDockerfileURL, buildOptions=null) { buildOptions = buildOptionsString(buildOptions) + def uploadExitCode = 0 + // GIT_PREVIOUS_COMMIT is null for first PR build def commit = sh(script: "echo ${GIT_LOCAL_BRANCH} | md5sum | cut -c 1-8", returnStdout: true).trim() + DOCKER_IMAGE_FILE = commit + if (remoteFilesDiffer(currentDockerfileURL, previousDockerfileURL)) { // Dockerfile has been changed compared to the previous commit // Worst case scenario. We cannot count on the local cache // because Dockerfile may contain apt-get entries that would try to update // from invalid (stale) addresses iC = docker.build("${DOCKER_REGISTRY_BASENAME}:${commit}-${BUILD_NUMBER}", "${buildOptions} --no-cache -f /tmp/${env.GIT_COMMIT}/f1 /tmp/${env.GIT_COMMIT}") + if (env.NODE_NAME.contains('x86_64')) { + sh "docker save -o ${JENKINS_DOCKER_IMAGE_DIR}/${DOCKER_IMAGE_FILE} ${DOCKER_REGISTRY_BASENAME}:${commit}-${BUILD_NUMBER}" + } } else { + // upload reference image (hyperledger/iroha:develop-build) (required in all use-cases in this execution branch) + if (env.NODE_NAME.contains('x86_64')) { + uploadExitCode = sh(script: "docker load -i ${JENKINS_DOCKER_IMAGE_DIR}/${imageName}", returnStatus: true) + if (uploadExitCode != 0) { + sh "echo 'Reference image ${DOCKER_REGISTRY_BASENAME}:${imageName} doesn't exist on the EFS" + } + } + def pullExitCode = sh(script: "docker pull ${DOCKER_REGISTRY_BASENAME}:${imageName}", returnStatus: true) + if (pullExitCode != 0) { + sh "echo 'Reference image ${DOCKER_REGISTRY_BASENAME}:${imageName} doesn't exist on the dockerhub" + } + else { + // save reference docker image into the file when it is doesn't exist on the EFS + if (uploadExitCode != 0) { + sh "docker save -o ${JENKINS_DOCKER_IMAGE_DIR}/${imageName} ${DOCKER_REGISTRY_BASENAME}:${imageName}" + } + } // first commit in this branch or Dockerfile modified if (remoteFilesDiffer(currentDockerfileURL, referenceDockerfileURL)) { // if we're lucky to build on the same agent, image will be built using cache - iC = docker.build("${DOCKER_REGISTRY_BASENAME}:${commit}-${BUILD_NUMBER}", "$buildOptions -f /tmp/${env.GIT_COMMIT}/f1 /tmp/${env.GIT_COMMIT}") + // for x86 download reference image and start build + iC = docker.build("${DOCKER_REGISTRY_BASENAME}:${commit}-${BUILD_NUMBER}", "${buildOptions} -f /tmp/${env.GIT_COMMIT}/f1 /tmp/${env.GIT_COMMIT}") + if (env.NODE_NAME.contains('x86_64')) { + sh "docker save -o /tmp/docker/${imageName} ${DOCKER_REGISTRY_BASENAME}:${commit}-${BUILD_NUMBER}" + } } else { - // try pulling image from Dockerhub, probably image is already there - def testExitCode = sh(script: "docker pull ${DOCKER_REGISTRY_BASENAME}:${imageName}", returnStatus: true) - if (testExitCode != 0) { - // image does not (yet) exist on Dockerhub. Build it - iC = docker.build("${DOCKER_REGISTRY_BASENAME}:${commit}-${BUILD_NUMBER}", "$buildOptions --no-cache -f /tmp/${env.GIT_COMMIT}/f1 /tmp/${env.GIT_COMMIT}") - } - else { - // no difference found compared to both previous and reference Dockerfile - iC = docker.image("${DOCKER_REGISTRY_BASENAME}:${imageName}") - } + // now we get reference image from the file (pull from dockerhub) + iC = docker.image("${DOCKER_REGISTRY_BASENAME}:${imageName}") + DOCKER_IMAGE_FILE = imageName } } if (GIT_LOCAL_BRANCH ==~ /develop|master/) { @@ -51,6 +72,7 @@ def dockerPullOrUpdate(imageName, currentDockerfileURL, previousDockerfileURL, r iC.push(imageName) } } + DOCKER_AGENT_IMAGE = iC.imageName() return iC } diff --git a/.jenkinsci/enums.groovy b/.jenkinsci/enums.groovy new file mode 100644 index 0000000000..32e383c6e6 --- /dev/null +++ b/.jenkinsci/enums.groovy @@ -0,0 +1,12 @@ +// types of tests provided by the developers (can be found at the CMakeLists.txt files) +enum TestTypes { + module(0), integration(1), system(2), cmake(3), regression(4), benchmark(5), framework(6) + TestTypes(int order) { + this.order = order + } + private final int order + int getOrder() { + order + } +} +return this diff --git a/.jenkinsci/github-api.groovy b/.jenkinsci/github-api.groovy new file mode 100644 index 0000000000..9441daf68f --- /dev/null +++ b/.jenkinsci/github-api.groovy @@ -0,0 +1,137 @@ +#!/usr/bin/env groovy + +// list of the pull requests reviews status on github +enum GithubPRStatus { + APPROVED, CHANGES_REQUESTED, REVIEW_REQUESTED +} +// list of the possible merge strategies +enum MergeTarget { + merge, squash +} +// list of supportable target branches for automated merge (by CI) +enum ChangeTarget { + master, develop, trunk +} + +// map with user:review_status +pullRequestReviewers = [:] + +// merges pull request using GitHub API in case it meets the merging criteria +def mergePullRequest() { + if ( ! ( checkMergeAcceptance() ) ) { return false } + withCredentials([string(credentialsId: 'jenkins-integration-test', variable: 'sorabot')]) { + def slurper = new groovy.json.JsonSlurperClassic() + def commitTitle = "" + def commitMessage = "" + def mergeMethod = getMergeMethod() + def jsonResponseMerge = sh(script: """ + curl -H "Authorization: token ${sorabot}" \ + -H "Accept: application/vnd.github.v3+json" \ + -X PUT --data '{"commit_title":"${commitTitle}","commit_message":"${commitMessage}","sha":"${env.GIT_COMMIT}","merge_method":"${mergeMethod}"}' \ + -w "%{http_code}" https://api.github.com/repos/hyperledger/iroha/pulls/${CHANGE_ID}/merge""", returnStdout: true) + def githubResponse = sh(script:"""set +x; printf '%s\n' "${jsonResponseMerge}" | tail -n 1; set -x""", returnStdout: true).trim() + jsonResponseMerge = slurper.parseText(jsonResponseMerge) + if (jsonResponseMerge.merged != "true" || !(githubResponse ==~ "200")) { + echo jsonResponseMerge.message + return false + } + return true + } +} + +// check merge acceptance by: +// - at least 2 "approved and NO "changes_requested" in reviews +// - e-mail of the commit does not match Jenkins user who launched this build +def checkMergeAcceptance() { + def approvalsRequired = 0 + // fill the map of user:review_status + getPullRequestReviewers() + pullRequestReviewers.each{ user, review_status -> + if (review_status == GithubPRStatus.APPROVED.toString()) { + approvalsRequired += 1 + } + else if (review_status == GithubPRStatus.CHANGES_REQUESTED.toString()) { + return false + } + } + if (approvalsRequired < 2) { + sh "echo 'Merge failed. Get more PR approvals before merging'" + return false + } + return true +} + +// returns merge method based on target branch (squash&merge vs merge) +def getMergeMethod() { + if (env.CHANGE_TARGET == ChangeTarget.master.toString()) { + return MergeTarget.merge.toString() + } + else { + return MergeTarget.squash.toString() + } +} + +// fill the pullRequestReviews map with user:review status +def getPullRequestReviewers() { + def slurper = new groovy.json.JsonSlurperClassic() + // if there more than 1 page of "reviews" in PR (it happens due to huge amount of comments) + def reviewPaging = sh(script: """curl -I https://api.github.com/repos/hyperledger/iroha/pulls/1392/reviews | grep -E "^Link:" | wc -l""", returnStdout: true) + def reviewPagesCount = "1" + if (reviewPaging.toInteger()) { + reviewPagesCount = sh(script: """ curl -I https://api.github.com/repos/hyperledger/iroha/pulls/1392/reviews | grep -E "^Link:" | awk 'BEGIN { FS = "page" } { print \$NF }' | awk -F"=" '{print \$2}' | awk -F">" '{print \$1}' """, returnStdout: true) + } + // start the loop to request pages sequentially + for(pageID in (1..reviewPagesCount.toInteger())) { + def jsonResponseReview = sh(script: """ + curl https://api.github.com/repos/hyperledger/iroha/pulls/${CHANGE_ID}/reviews?page=${pageID} + """, returnStdout: true).trim() + // process returned reviews. add/update user:review_status to the map + jsonResponseReview = slurper.parseText(jsonResponseReview) + if (jsonResponseReview.size() > 0) { + jsonResponseReview.each { + if (it.state.toString() in [GithubPRStatus.APPROVED.toString(), GithubPRStatus.CHANGES_REQUESTED.toString()]) { + pullRequestReviewers[it.user.login.toString()] = it.state.toString() + } + } + } + } + // get requested reviewers (those who did not review this PR yet) + def jsonResponseReviewers = sh(script: """ + curl https://api.github.com/repos/hyperledger/iroha/pulls/${CHANGE_ID}/requested_reviewers + """, returnStdout: true).trim() + jsonResponseReviewers = slurper.parseText(jsonResponseReviewers) + if (jsonResponseReviewers.size() > 0) { + jsonResponseReviewers.users.each { + pullRequestReviewers[it.login] = GithubPRStatus.REVIEW_REQUESTED.toString() + } + } +} + +// returns PR reviewers in the form of "@reviewer1 @reviewer2 ... @reviewerN" to mention PR reviewers about build result +def getUsersMentionList() { + getPullRequestReviewers() + def ghUsersList = '' + pullRequestReviewers.each{ user, review_status -> ghUsersList = ["@${user}", ghUsersList].join(' ') } + return ghUsersList +} + +// post a comment on PR via GitHub API +def writePullRequestComment() { + def ghUsersList = getUsersMentionList() + withCredentials([string(credentialsId: 'jenkins-integration-test', variable: 'sorabot')]) { + def slurper = new groovy.json.JsonSlurperClassic() + def jsonResponseComment = sh(script: """ + curl -H "Authorization: token ${sorabot}" \ + -H "Accept: application/vnd.github.v3+json" \ + -X POST --data '{"body":"${ghUsersList} commit ${env.GIT_COMMIT} build status: ${currentBuild.currentResult}. build URL: ${BUILD_URL}"}' \ + -w "%{http_code}" https://api.github.com/repos/hyperledger/iroha/issues/${CHANGE_ID}/comments + """, returnStdout: true).trim() + def githubResponse = sh(script:"""set +x; printf '%s\n' "${jsonResponseComment}" | tail -n 1 ; set -x""", returnStdout: true).trim() + if (githubResponse ==~ "201") { + return true + } + } + return false +} + +return this diff --git a/.jenkinsci/linux-post-step.groovy b/.jenkinsci/linux-post-step.groovy deleted file mode 100644 index c28f6f6022..0000000000 --- a/.jenkinsci/linux-post-step.groovy +++ /dev/null @@ -1,20 +0,0 @@ -def linuxPostStep() { - timeout(time: 600, unit: "SECONDS") { - try { - if (currentBuild.currentResult == "SUCCESS" && GIT_LOCAL_BRANCH ==~ /(master|develop)/) { - def artifacts = load ".jenkinsci/artifacts.groovy" - def commit = env.GIT_COMMIT - def platform = sh(script: 'uname -m', returnStdout: true).trim() - filePaths = [ '/tmp/${GIT_COMMIT}-${BUILD_NUMBER}/*' ] - artifacts.uploadArtifacts(filePaths, sprintf('/iroha/linux/%4$s/%1$s-%2$s-%3$s', [GIT_LOCAL_BRANCH, sh(script: 'date "+%Y%m%d"', returnStdout: true).trim(), commit.substring(0,6), platform])) - } - } - finally { - def cleanup = load ".jenkinsci/docker-cleanup.groovy" - cleanup.doDockerCleanup() - cleanWs() - } - } -} - -return this diff --git a/.jenkinsci/mac-debug-build.groovy b/.jenkinsci/mac-debug-build.groovy new file mode 100644 index 0000000000..7eb118758b --- /dev/null +++ b/.jenkinsci/mac-debug-build.groovy @@ -0,0 +1,44 @@ +#!/usr/bin/env groovy + +def doDebugBuild() { + def setter = load ".jenkinsci/set-parallelism.groovy" + def parallelism = setter.setParallelism(params.PARALLELISM) + def scmVars = checkout scm + env.IROHA_VERSION = "0x${scmVars.GIT_COMMIT}" + env.IROHA_HOME = "/opt/iroha" + env.IROHA_BUILD = "${env.IROHA_HOME}/build" + + sh """ + ccache --version + ccache --show-stats + ccache --zero-stats + ccache --max-size=5G + """ + sh """ + cmake \ + -DTESTING=ON \ + -H. \ + -Bbuild \ + -DCMAKE_BUILD_TYPE=${params.build_type} \ + -DIROHA_VERSION=${env.IROHA_VERSION} + """ + sh "cmake --build build -- -j${parallelism}" + sh "ccache --show-stats" +} + +def doTestStep(testList) { + sh """ + export IROHA_POSTGRES_PASSWORD=${IROHA_POSTGRES_PASSWORD}; \ + export IROHA_POSTGRES_USER=${IROHA_POSTGRES_USER}; \ + mkdir -p /var/jenkins/${GIT_COMMIT}-${BUILD_NUMBER}; \ + initdb -D /var/jenkins/${GIT_COMMIT}-${BUILD_NUMBER}/ -U ${IROHA_POSTGRES_USER} --pwfile=<(echo ${IROHA_POSTGRES_PASSWORD}); \ + pg_ctl -D /var/jenkins/${GIT_COMMIT}-${BUILD_NUMBER}/ -o '-p 5433' -l /var/jenkins/${GIT_COMMIT}-${BUILD_NUMBER}/events.log start; \ + psql -h localhost -d postgres -p 5433 -U ${IROHA_POSTGRES_USER} --file=<(echo create database ${IROHA_POSTGRES_USER};) + """ + def testExitCode = sh(script: """cd build && IROHA_POSTGRES_HOST=localhost IROHA_POSTGRES_PORT=5433 ctest --output-on-failure -R '${testList}' """, returnStatus: true) + if (testExitCode != 0) { + currentBuild.result = "UNSTABLE" + } +} + +return this diff --git a/.jenkinsci/mac-release-build.groovy b/.jenkinsci/mac-release-build.groovy index 4ff20c6a95..26552f4ed4 100644 --- a/.jenkinsci/mac-release-build.groovy +++ b/.jenkinsci/mac-release-build.groovy @@ -1,6 +1,8 @@ #!/usr/bin/env groovy def doReleaseBuild(coverageEnabled=false) { + def setter = load ".jenkinsci/set-parallelism.groovy" + def parallelism = setter.setParallelism(params.PARALLELISM) def scmVars = checkout scm env.IROHA_VERSION = "0x${scmVars.GIT_COMMIT}" env.IROHA_HOME = "/opt/iroha" @@ -17,10 +19,10 @@ def doReleaseBuild(coverageEnabled=false) { -Bbuild \ -DCOVERAGE=OFF \ -DPACKAGE_TGZ=ON \ - -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_BUILD_TYPE=${params.build_type} \ -DIROHA_VERSION=${env.IROHA_VERSION} - - cmake --build build --target package -- -j${params.PARALLELISM} + + cmake --build build --target package -- -j${parallelism} mv ./build/iroha-${env.IROHA_VERSION}-*.tar.gz ./build/iroha.tar.gz ccache --show-stats """ diff --git a/.jenkinsci/notifications.groovy b/.jenkinsci/notifications.groovy new file mode 100644 index 0000000000..2c527ef6d0 --- /dev/null +++ b/.jenkinsci/notifications.groovy @@ -0,0 +1,61 @@ +#!/usr/bin/env groovy + +def notifyBuildResults() { + def mergeMessage = '' + def receivers = '' + // notify commiter in case of branch commit + if ( env.CHANGE_ID == null ) { + sendEmail(buildContent(mergeMessage), "${GIT_COMMITER_EMAIL}") + return + } + // merge commit build results + if ( params.merge_pr ) { + if ( currentBuild.currentResult == "SUCCESS" ) { + mergeMessage = "Merge status to ${env.CHANGE_TARGET}: true" + } + else { + mergeMessage = "Merge status to ${env.CHANGE_TARGET}: false" + } + + if ( env.CHANGE_TARGET == 'master' ) { + receivers = "iroha-maintainers@soramitsu.co.jp" + } + else if ( env.CHANGE_TARGET == 'develop' ) { + receivers = "andrei@soramitsu.co.jp, fyodor@soramitsu.co.jp, ${GIT_COMMITER_EMAIL}" + } + else { + receivers = "${GIT_COMMITER_EMAIL}" + } + + sendEmail(buildContent(mergeMessage), receivers) + } + else { + // write comment to the PR page on github if it is a pull request commit + def notify = load ".jenkinsci/github-api.groovy" + notify.writePullRequestComment() + } + return +} + +def buildContent(mergeMessage="") { + return """ +

This email informs you about the build results on Jenkins CI

+

Build status: ${currentBuild.currentResult}. ${mergeMessage}

+

+Check console output to view the results. +

+

+You can find the build log attached to this email +

+""" +} + +def sendEmail(content, to) { + emailext( subject: '$DEFAULT_SUBJECT', + body: "${content}", + attachLog: true, + compressLog: true, + to: "${to}" + ) +} +return this diff --git a/.jenkinsci/post-step.groovy b/.jenkinsci/post-step.groovy new file mode 100644 index 0000000000..e5eab76376 --- /dev/null +++ b/.jenkinsci/post-step.groovy @@ -0,0 +1,40 @@ +#!/usr/bin/env groovy + +// upload artifacts in release builds +def postStep() { + def artifacts = load ".jenkinsci/artifacts.groovy" + def commit = env.GIT_COMMIT + def platform = sh(script: 'uname -m', returnStdout: true).trim() + filePaths = [ '/tmp/${GIT_COMMIT}-${BUILD_NUMBER}/*' ] + artifacts.uploadArtifacts(filePaths, sprintf('/iroha/linux/%4$s/%1$s-%2$s-%3$s', [GIT_LOCAL_BRANCH, sh(script: 'date "+%Y%m%d"', returnStdout: true).trim(), commit.substring(0,6), platform])) +} + +// upload artifacts in release builds (for mac) +def macPostStep() { + def artifacts = load ".jenkinsci/artifacts.groovy" + def commit = env.GIT_COMMIT + filePaths = [ '\$(pwd)/build/*.tar.gz' ] + artifacts.uploadArtifacts(filePaths, sprintf('/iroha/macos/%1$s-%2$s-%3$s', [GIT_LOCAL_BRANCH, sh(script: 'date "+%Y%m%d"', returnStdout: true).trim(), commit.substring(0,6)])) +} + +// clean folders after the build +def cleanUp() { + if ( ! env.NODE_NAME.contains('x86_64') ) { + sh """ + #remove folder with iroha.deb package and Dockerfiles + rm -rf /tmp/${env.GIT_COMMIT}-${BUILD_NUMBER} + rm -rf /tmp/${env.GIT_COMMIT} + """ + } + cleanWs() +} + +// stop postgres and remove workspace folder (for mac) +def macCleanUp() { + sh """ + rm -rf /var/jenkins/${GIT_COMMIT}-${BUILD_NUMBER}/ + """ + cleanWs() +} + +return this diff --git a/.jenkinsci/pre-build.groovy b/.jenkinsci/pre-build.groovy new file mode 100644 index 0000000000..a6875bc28f --- /dev/null +++ b/.jenkinsci/pre-build.groovy @@ -0,0 +1,14 @@ +#!/usr/bin/env groovy + +// replaces bunch of expressions in `when` block at the `stage` in Jenkinsfile +def prepare() { + def mergeBranches = env.CHANGE_TARGET ==~ /(master|develop|trunk)/ ? "true" : "false" + def docsPullRequestBranches = env.CHANGE_TARGET ==~ /(master|develop)/ ? "true" : "false" + def pullRequestCommit = env.CHANGE_ID && env.GIT_PREVIOUS_COMMIT ? "true" : "false" + INITIAL_COMMIT_PR = env.CHANGE_ID && env.GIT_PREVIOUS_COMMIT == null ? "true" : "false" + MERGE_CONDITIONS_SATISFIED = (mergeBranches == "true" && pullRequestCommit == "true" && params.merge_pr) ? "true" : "false" + REST_PR_CONDITIONS_SATISFIED = (docsPullRequestBranches == "true" && pullRequestCommit == "true" && params.merge_pr) ? "true" : "false" + GIT_COMMITER_EMAIL = sh(script: """git --no-pager show -s --format='%ae' ${env.GIT_COMMIT}""", returnStdout: true).trim() +} + +return this diff --git a/.jenkinsci/release-build.groovy b/.jenkinsci/release-build.groovy index d24d6cfb31..422b567c83 100644 --- a/.jenkinsci/release-build.groovy +++ b/.jenkinsci/release-build.groovy @@ -1,25 +1,20 @@ #!/usr/bin/env groovy def doReleaseBuild() { - def parallelism = params.PARALLELISM def manifest = load ".jenkinsci/docker-manifest.groovy" // params are always null unless job is started // this is the case for the FIRST build only. // So just set this to same value as default. // This is a known bug. See https://issues.jenkins-ci.org/browse/JENKINS-41929 - if (!parallelism) { - parallelism = 4 - } - if (env.NODE_NAME.contains('arm7')) { - parallelism = 1 - } + def setter = load ".jenkinsci/set-parallelism.groovy" + def parallelism = setter.setParallelism(params.PARALLELISM) def platform = sh(script: 'uname -m', returnStdout: true).trim() sh "mkdir /tmp/${env.GIT_COMMIT}-${BUILD_NUMBER} || true" iC = docker.image("${DOCKER_REGISTRY_BASENAME}:${platform}-develop-build") iC.pull() iC.inside("" + " -v /tmp/${GIT_COMMIT}-${BUILD_NUMBER}:/tmp/${GIT_COMMIT}" - + " -v /var/jenkins/ccache:${CCACHE_RELEASE_DIR}") { + + " -v ${CCACHE_RELEASE_DIR}:${CCACHE_DIR}") { def scmVars = checkout scm env.IROHA_VERSION = "0x${scmVars.GIT_COMMIT}" @@ -36,7 +31,7 @@ def doReleaseBuild() { cmake \ -H. \ -Bbuild \ - -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_BUILD_TYPE=${params.build_type} \ -DIROHA_VERSION=${env.IROHA_VERSION} \ -DPACKAGE_DEB=ON \ -DPACKAGE_TGZ=ON \ diff --git a/.jenkinsci/selected-branches-coverage.groovy b/.jenkinsci/selected-branches-coverage.groovy deleted file mode 100644 index d6c3f95967..0000000000 --- a/.jenkinsci/selected-branches-coverage.groovy +++ /dev/null @@ -1,13 +0,0 @@ -#!/usr/bin/env groovy - -def selectedBranchesCoverage(branches, PRCoverage=true) { - // trigger coverage if branch is either develop or master, or it is a PR - if (PRCoverage) { - return env.GIT_LOCAL_BRANCH in branches || env.CHANGE_ID != null - } - else { - return env.GIT_LOCAL_BRANCH in branches - } -} - -return this \ No newline at end of file diff --git a/.jenkinsci/set-parallelism.groovy b/.jenkinsci/set-parallelism.groovy new file mode 100644 index 0000000000..581488f7ca --- /dev/null +++ b/.jenkinsci/set-parallelism.groovy @@ -0,0 +1,19 @@ +#!/usr/bin/env groovy + +def setParallelism(defaultParameter) { + if (!defaultParameter) { + return 4 + } + if (env.NODE_NAME.contains('arm7')) { + return 1 + } + if (env.NODE_NAME.contains('mac')) { + return 4 + } + if (env.NODE_NAME.contains('x86_64')) { + return 8 + } + return defaultParameter +} + +return this diff --git a/.jenkinsci/test-launcher.groovy b/.jenkinsci/test-launcher.groovy new file mode 100644 index 0000000000..924b0cd6f4 --- /dev/null +++ b/.jenkinsci/test-launcher.groovy @@ -0,0 +1,41 @@ +#!/usr/bin/env groovy + +// format the enum elements output like "(val1|val2|...|valN)*" +def printRange(start, end) { + def output = "" + def set = start..end + TestTypes.values().each { t -> + if (t.getOrder() in set) { + output = [output, (t.getOrder() != start ? "|" : ""), t.name()].join('') + } + } + return ["(", output, ")*"].join('') +} + +// return tests list regex that will be launched by ctest +def chooseTestType() { + if (params.merge_pr) { + if (env.NODE_NAME.contains('x86_64')) { + // choose module, integration, system, cmake, regression tests + return printRange(TestTypes.module.getOrder(), TestTypes.regression.getOrder()) + } + else { + // not to do any tests + return "" + } + } + if (params.nightly) { + if (env.NODE_NAME.contains('x86_64')) { + // choose all tests + return printRange(TestTypes.MIN_VALUE.getOrder(), TestTypes.MAX_VALUE.getOrder()) + } + else { + // choose module, integration, system, cmake, regression tests + return printRange(TestTypes.module.getOrder(), TestTypes.regression.getOrder()) + } + } + // just choose module tests + return [TestTypes.module.toString(), "*"].join('') +} + +return this diff --git a/Jenkinsfile b/Jenkinsfile index 2fd2412580..cb78c4b76e 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -1,12 +1,14 @@ properties([parameters([ - booleanParam(defaultValue: true, description: 'Build `iroha`', name: 'iroha'), - booleanParam(defaultValue: false, description: 'Build `bindings`', name: 'bindings'), booleanParam(defaultValue: true, description: '', name: 'x86_64_linux'), booleanParam(defaultValue: false, description: '', name: 'armv7_linux'), booleanParam(defaultValue: false, description: '', name: 'armv8_linux'), - booleanParam(defaultValue: true, description: '', name: 'x86_64_macos'), + booleanParam(defaultValue: false, description: '', name: 'x86_64_macos'), booleanParam(defaultValue: false, description: '', name: 'x86_64_win'), + booleanParam(defaultValue: false, description: 'Build coverage', name: 'coverage'), + booleanParam(defaultValue: false, description: 'Merge this PR to target after success build', name: 'merge_pr'), + booleanParam(defaultValue: false, description: 'Scheduled nightly build', name: 'nightly'), choice(choices: 'Debug\nRelease', description: 'Iroha build type', name: 'build_type'), + booleanParam(defaultValue: false, description: 'Build `bindings`', name: 'bindings'), booleanParam(defaultValue: false, description: 'Build Java bindings', name: 'JavaBindings'), choice(choices: 'Release\nDebug', description: 'Java bindings build type', name: 'JBBuildType'), booleanParam(defaultValue: false, description: 'Build Python bindings', name: 'PythonBindings'), @@ -19,7 +21,6 @@ properties([parameters([ booleanParam(defaultValue: false, description: 'Build docs', name: 'Doxygen'), string(defaultValue: '4', description: 'How much parallelism should we exploit. "4" is optimal for machines with modest amount of memory and at least 4 cores', name: 'PARALLELISM')])]) - pipeline { environment { CCACHE_DIR = '/opt/.ccache' @@ -28,12 +29,21 @@ pipeline { SONAR_TOKEN = credentials('SONAR_TOKEN') GIT_RAW_BASE_URL = "https://raw.githubusercontent.com/hyperledger/iroha" DOCKER_REGISTRY_BASENAME = "hyperledger/iroha" + JENKINS_DOCKER_IMAGE_DIR = '/tmp/docker' + GIT_COMMITER_EMAIL = '' IROHA_NETWORK = "iroha-0${CHANGE_ID}-${GIT_COMMIT}-${BUILD_NUMBER}" IROHA_POSTGRES_HOST = "pg-0${CHANGE_ID}-${GIT_COMMIT}-${BUILD_NUMBER}" IROHA_POSTGRES_USER = "pguser${GIT_COMMIT}" IROHA_POSTGRES_PASSWORD = "${GIT_COMMIT}" IROHA_POSTGRES_PORT = 5432 + + DOCKER_AGENT_IMAGE = '' + DOCKER_IMAGE_FILE = '' + WORKSPACE_PATH = '' + MERGE_CONDITIONS_SATISFIED = '' + REST_PR_CONDITIONS_SATISFIED = '' + INITIAL_COMMIT_PR = '' } options { @@ -43,10 +53,13 @@ pipeline { agent any stages { - stage ('Stop same job builds') { + stage ('Pre-build') { agent { label 'master' } steps { script { + load ".jenkinsci/enums.groovy" + def preBuildRoutine = load ".jenkinsci/pre-build.groovy" + preBuildRoutine.prepare() if (GIT_LOCAL_BRANCH != "develop") { def builds = load ".jenkinsci/cancel-builds-same-job.groovy" builds.cancelSameJobBuilds() @@ -54,41 +67,37 @@ pipeline { } } } - stage('Build Debug') { - when { - allOf { - expression { params.build_type == 'Debug' } - expression { return params.iroha } - } - } + stage('Build') { parallel { stage ('x86_64_linux') { when { beforeAgent true - expression { return params.x86_64_linux } + anyOf { + expression { return params.x86_64_linux } + expression { return MERGE_CONDITIONS_SATISFIED == "true" } + } } - agent { label 'x86_64' } + agent { label 'x86_64_aws_build' } steps { script { - debugBuild = load ".jenkinsci/debug-build.groovy" - coverage = load ".jenkinsci/selected-branches-coverage.groovy" - if (coverage.selectedBranchesCoverage(['develop', 'master'])) { - debugBuild.doDebugBuild(true) + def debugBuild = load ".jenkinsci/debug-build.groovy" + def coverage = load ".jenkinsci/build-coverage.groovy" + if (params.build_type == 'Debug') { + debugBuild.doDebugBuild(coverage.checkCoverageConditions()) } - else { - debugBuild.doDebugBuild() - } - if (GIT_LOCAL_BRANCH ==~ /(master|develop)/) { - releaseBuild = load ".jenkinsci/release-build.groovy" + if (params.build_type == 'Release') { + def releaseBuild = load ".jenkinsci/release-build.groovy" releaseBuild.doReleaseBuild() } } } post { - always { + success { script { - post = load ".jenkinsci/linux-post-step.groovy" - post.linuxPostStep() + if (params.build_type == 'Release') { + def post = load ".jenkinsci/post-step.groovy" + post.postStep() + } } } } @@ -96,30 +105,31 @@ pipeline { stage('armv7_linux') { when { beforeAgent true - expression { return params.armv7_linux } + anyOf { + expression { return params.armv7_linux } + // expression { return MERGE_CONDITIONS_SATISFIED == "true" } + } } agent { label 'armv7' } steps { script { - debugBuild = load ".jenkinsci/debug-build.groovy" - coverage = load ".jenkinsci/selected-branches-coverage.groovy" - if (!params.x86_64_linux && !params.armv8_linux && !params.x86_64_macos && (coverage.selectedBranchesCoverage(['develop', 'master']))) { - debugBuild.doDebugBuild(true) - } - else { + if (params.build_type == 'Debug') { + def debugBuild = load ".jenkinsci/debug-build.groovy" debugBuild.doDebugBuild() } - if (GIT_LOCAL_BRANCH ==~ /(master|develop)/) { - releaseBuild = load ".jenkinsci/release-build.groovy" + else { + def releaseBuild = load ".jenkinsci/release-build.groovy" releaseBuild.doReleaseBuild() } } } post { - always { + success { script { - post = load ".jenkinsci/linux-post-step.groovy" - post.linuxPostStep() + if (params.build_type == 'Release') { + def post = load ".jenkinsci/post-step.groovy" + post.postStep() + } } } } @@ -127,129 +137,63 @@ pipeline { stage('armv8_linux') { when { beforeAgent true - expression { return params.armv8_linux } + anyOf { + expression { return params.armv8_linux } + // expression { return MERGE_CONDITIONS_SATISFIED == "true" } + } } agent { label 'armv8' } steps { script { - debugBuild = load ".jenkinsci/debug-build.groovy" - coverage = load ".jenkinsci/selected-branches-coverage.groovy" - if (!params.x86_64_linux && !params.x86_64_macos && (coverage.selectedBranchesCoverage(['develop', 'master']))) { - debugBuild.doDebugBuild(true) - } - else { + if ( params.build_type == 'Debug') { + def debugBuild = load ".jenkinsci/debug-build.groovy" debugBuild.doDebugBuild() } - if (GIT_LOCAL_BRANCH ==~ /(master|develop)/) { - releaseBuild = load ".jenkinsci/release-build.groovy" + else { + def releaseBuild = load ".jenkinsci/release-build.groovy" releaseBuild.doReleaseBuild() } } } post { - always { + success { script { - post = load ".jenkinsci/linux-post-step.groovy" - post.linuxPostStep() + if (params.build_type == 'Release') { + def post = load ".jenkinsci/post-step.groovy" + post.postStep() + } } } } } - stage('x86_64_macos'){ + stage('x86_64_macos') { when { beforeAgent true - expression { return params.x86_64_macos } + anyOf { + expression { return INITIAL_COMMIT_PR == "true" } + expression { return MERGE_CONDITIONS_SATISFIED == "true" } + expression { return params.x86_64_macos } + } } agent { label 'mac' } steps { script { - def coverageEnabled = false - def cmakeOptions = "" - coverage = load ".jenkinsci/selected-branches-coverage.groovy" - if (!params.x86_64_linux && (coverage.selectedBranchesCoverage(['develop', 'master']))) { - coverageEnabled = true - cmakeOptions = " -DCOVERAGE=ON " - } - def scmVars = checkout scm - env.IROHA_VERSION = "0x${scmVars.GIT_COMMIT}" - env.IROHA_HOME = "/opt/iroha" - env.IROHA_BUILD = "${env.IROHA_HOME}/build" - - sh """ - ccache --version - ccache --show-stats - ccache --zero-stats - ccache --max-size=5G - """ - sh """ - cmake \ - -DTESTING=ON \ - -H. \ - -Bbuild \ - -DCMAKE_BUILD_TYPE=${params.build_type} \ - -DIROHA_VERSION=${env.IROHA_VERSION} \ - ${cmakeOptions} - """ - sh "cmake --build build -- -j${params.PARALLELISM}" - sh "ccache --show-stats" - if ( coverageEnabled ) { - sh "cmake --build build --target coverage.init.info" + if (params.build_type == 'Debug') { + def macDebugBuild = load ".jenkinsci/mac-debug-build.groovy" + macDebugBuild.doDebugBuild() } - sh """ - export IROHA_POSTGRES_PASSWORD=${IROHA_POSTGRES_PASSWORD}; \ - export IROHA_POSTGRES_USER=${IROHA_POSTGRES_USER}; \ - mkdir -p /var/jenkins/${GIT_COMMIT}-${BUILD_NUMBER}; \ - initdb -D /var/jenkins/${GIT_COMMIT}-${BUILD_NUMBER}/ -U ${IROHA_POSTGRES_USER} --pwfile=<(echo ${IROHA_POSTGRES_PASSWORD}); \ - pg_ctl -D /var/jenkins/${GIT_COMMIT}-${BUILD_NUMBER}/ -o '-p 5433' -l /var/jenkins/${GIT_COMMIT}-${BUILD_NUMBER}/events.log start; \ - psql -h localhost -d postgres -p 5433 -U ${IROHA_POSTGRES_USER} --file=<(echo create database ${IROHA_POSTGRES_USER};) - """ - def testExitCode = sh(script: """cd build && IROHA_POSTGRES_HOST=localhost IROHA_POSTGRES_PORT=5433 ctest --output-on-failure """, returnStatus: true) - if (testExitCode != 0) { - currentBuild.result = "UNSTABLE" - } - if ( coverageEnabled ) { - sh "cmake --build build --target cppcheck" - // Sonar - if (env.CHANGE_ID != null) { - sh """ - sonar-scanner \ - -Dsonar.github.disableInlineComments \ - -Dsonar.github.repository='hyperledger/iroha' \ - -Dsonar.analysis.mode=preview \ - -Dsonar.login=${SONAR_TOKEN} \ - -Dsonar.projectVersion=${BUILD_TAG} \ - -Dsonar.github.oauth=${SORABOT_TOKEN} - """ - } - sh "cmake --build build --target coverage.info" - sh "python /usr/local/bin/lcov_cobertura.py build/reports/coverage.info -o build/reports/coverage.xml" - cobertura autoUpdateHealth: false, autoUpdateStability: false, coberturaReportFile: '**/build/reports/coverage.xml', conditionalCoverageTargets: '75, 50, 0', failUnhealthy: false, failUnstable: false, lineCoverageTargets: '75, 50, 0', maxNumberOfBuilds: 50, methodCoverageTargets: '75, 50, 0', onlyStable: false, zoomCoverageChart: false - } - if (GIT_LOCAL_BRANCH ==~ /(master|develop)/) { - releaseBuild = load ".jenkinsci/mac-release-build.groovy" - releaseBuild.doReleaseBuild() + else { + def macReleaseBuild = load ".jenkinsci/mac-release-build.groovy" + macReleaseBuild.doReleaseBuild() } } } post { - always { + success { script { - timeout(time: 600, unit: "SECONDS") { - try { - if (currentBuild.currentResult == "SUCCESS" && GIT_LOCAL_BRANCH ==~ /(master|develop)/) { - def artifacts = load ".jenkinsci/artifacts.groovy" - def commit = env.GIT_COMMIT - filePaths = [ '\$(pwd)/build/*.tar.gz' ] - artifacts.uploadArtifacts(filePaths, sprintf('/iroha/macos/%1$s-%2$s-%3$s', [GIT_LOCAL_BRANCH, sh(script: 'date "+%Y%m%d"', returnStdout: true).trim(), commit.substring(0,6)])) - } - } - finally { - cleanWs() - sh """ - pg_ctl -D /var/jenkins/${GIT_COMMIT}-${BUILD_NUMBER}/ stop && \ - rm -rf /var/jenkins/${GIT_COMMIT}-${BUILD_NUMBER}/ - """ - } + if (params.build_type == 'Release') { + def post = load ".jenkinsci/post-step.groovy" + post.macPostStep() } } } @@ -257,29 +201,54 @@ pipeline { } } } - stage('Build Release') { + stage('Pre-Coverage') { + when { + beforeAgent true + anyOf { + expression { params.coverage } // by request + expression { return INITIAL_COMMIT_PR == "true" } + expression { return MERGE_CONDITIONS_SATISFIED == "true" } + allOf { + expression { return params.build_type == 'Debug' } + expression { return env.GIT_LOCAL_BRANCH ==~ /master/ } + } + } + } + agent { label 'x86_64_aws_cov'} + steps { + script { + def coverage = load '.jenkinsci/debug-build.groovy' + coverage.doPreCoverageStep() + } + } + } + stage('Tests') { when { - expression { params.build_type == 'Release' } - expression { return params.iroha } + beforeAgent true + expression { return params.build_type == "Debug" } } parallel { stage('x86_64_linux') { when { beforeAgent true - expression { return params.x86_64_linux } + anyOf { + expression { return params.x86_64_linux } + expression { return MERGE_CONDITIONS_SATISFIED == "true" } + } } - agent { label 'x86_64' } + agent { label 'x86_64_aws_test' } steps { script { - def releaseBuild = load ".jenkinsci/release-build.groovy" - releaseBuild.doReleaseBuild() + def debugBuild = load ".jenkinsci/debug-build.groovy" + def testSelect = load ".jenkinsci/test-launcher.groovy" + debugBuild.doTestStep(testSelect.chooseTestType()) } } post { - always { + cleanup { script { - post = load ".jenkinsci/linux-post-step.groovy" - post.linuxPostStep() + def clean = load ".jenkinsci/docker-cleanup.groovy" + clean.doDockerNetworkCleanup() } } } @@ -287,20 +256,24 @@ pipeline { stage('armv7_linux') { when { beforeAgent true - expression { return params.armv7_linux } + allOf { + expression { return params.armv7_linux } + // expression { return MERGE_CONDITIONS_SATISFIED == "false" } + } } agent { label 'armv7' } steps { script { - def releaseBuild = load ".jenkinsci/release-build.groovy" - releaseBuild.doReleaseBuild() + def debugBuild = load ".jenkinsci/debug-build.groovy" + def testSelect = load ".jenkinsci/test-launcher.groovy" + debugBuild.doTestStep(testSelect.chooseTestType()) } } post { - always { + cleanup { script { - post = load ".jenkinsci/linux-post-step.groovy" - post.linuxPostStep() + def clean = load ".jenkinsci/docker-cleanup.groovy" + clean.doDockerNetworkCleanup() } } } @@ -308,20 +281,24 @@ pipeline { stage('armv8_linux') { when { beforeAgent true - expression { return params.armv8_linux } + allOf { + expression { return params.armv8_linux } + // expression { return MERGE_CONDITIONS_SATISFIED == "false" } + } } agent { label 'armv8' } steps { script { - def releaseBuild = load ".jenkinsci/release-build.groovy" - releaseBuild.doReleaseBuild() + def debugBuild = load ".jenkinsci/debug-build.groovy" + def testSelect = load ".jenkinsci/test-launcher.groovy" + debugBuild.doTestStep(testSelect.chooseTestType()) } } post { - always { + cleanup { script { - post = load ".jenkinsci/linux-post-step.groovy" - post.linuxPostStep() + def clean = load ".jenkinsci/docker-cleanup.groovy" + clean.doDockerNetworkCleanup() } } } @@ -329,97 +306,154 @@ pipeline { stage('x86_64_macos') { when { beforeAgent true - expression { return params.x86_64_macos } + allOf { + expression { return INITIAL_COMMIT_PR == "false" } + expression { return MERGE_CONDITIONS_SATISFIED == "false" } + expression { return params.x86_64_macos } + } } agent { label 'mac' } steps { script { - def releaseBuild = load ".jenkinsci/mac-release-build.groovy" - releaseBuild.doReleaseBuild() + def macDebugBuild = load ".jenkinsci/mac-debug-build.groovy" + def testSelect = load ".jenkinsci/test-launcher.groovy" + macDebugBuild.doTestStep(testSelect.chooseTestType()) } } post { - always { + cleanup { script { - timeout(time: 600, unit: "SECONDS") { - try { - if (currentBuild.currentResult == "SUCCESS" && GIT_LOCAL_BRANCH ==~ /(master|develop)/) { - def artifacts = load ".jenkinsci/artifacts.groovy" - def commit = env.GIT_COMMIT - filePaths = [ '\$(pwd)/build/*.tar.gz' ] - artifacts.uploadArtifacts(filePaths, sprintf('/iroha/macos/%1$s-%2$s-%3$s', [GIT_LOCAL_BRANCH, sh(script: 'date "+%Y%m%d"', returnStdout: true).trim(), commit.substring(0,6)])) - } - } - finally { - cleanWs() - } - } + sh "pg_ctl -D /var/jenkins/${GIT_COMMIT}-${BUILD_NUMBER}/ stop" } } } } } } - stage('Build docs') { + stage('Post-Coverage') { when { beforeAgent true - allOf { - expression { return params.Doxygen } - expression { GIT_LOCAL_BRANCH ==~ /(master|develop)/ } + anyOf { + expression { params.coverage } // by request + expression { return INITIAL_COMMIT_PR == "true" } + expression { return MERGE_CONDITIONS_SATISFIED == "true" } + allOf { + expression { return params.build_type == 'Debug' } + expression { return env.GIT_LOCAL_BRANCH ==~ /master/ } + } } } - // build docs on any vacant node. Prefer `x86_64` over - // others as nodes are more powerful - agent { label 'x86_64 || arm' } - steps { - script { - def doxygen = load ".jenkinsci/doxygen.groovy" - docker.image("${env.DOCKER_IMAGE}").inside { - def scmVars = checkout scm - doxygen.doDoxygen() + parallel { + stage('lcov_cobertura') { + agent { label 'x86_64_aws_cov' } + steps { + script { + def coverage = load '.jenkinsci/debug-build.groovy' + coverage.doPostCoverageCoberturaStep() + } + } + } + stage('sonarqube') { + agent { label 'x86_64_aws_cov' } + steps { + script { + def coverage = load '.jenkinsci/debug-build.groovy' + coverage.doPostCoverageSonarStep() + } } } } } - stage('Build bindings') { - when { - beforeAgent true - expression { return params.bindings } - } + stage ('Build rest') { parallel { - stage('Linux bindings') { + stage('linux_release') { + when { + beforeAgent true + anyOf { + allOf { + expression { return params.x86_64_linux } + expression { return params.build_type == 'Debug' } + expression { return env.GIT_LOCAL_BRANCH ==~ /(develop|master|trunk)/ } + } + expression { return MERGE_CONDITIONS_SATISFIED == "true" } + } + } + agent { label 'x86_64_aws_build' } + steps { + script { + def releaseBuild = load '.jenkinsci/release-build.groovy' + releaseBuild.doReleaseBuild() + } + } + post { + success { + script { + if (params.build_type == 'Release') { + def post = load ".jenkinsci/post-step.groovy" + post.postStep() + } + } + } + } + } + stage('docs') { + when { + beforeAgent true + anyOf { + expression { return params.Doxygen } + expression { return GIT_LOCAL_BRANCH ==~ /(master|develop)/ } + expression { return REST_PR_CONDITIONS_SATISFIED == "true" } + } + } + agent { label 'x86_64_aws_cov' } + steps { + script { + def doxygen = load ".jenkinsci/doxygen.groovy" + sh "docker load -i ${JENKINS_DOCKER_IMAGE_DIR}/${DOCKER_IMAGE_FILE}" + def iC = docker.image("${DOCKER_AGENT_IMAGE}") + iC.inside { + def scmVars = checkout scm + doxygen.doDoxygen() + } + } + } + } + stage('bindings') { when { beforeAgent true - expression { return params.x86_64_linux } + anyOf { + expression { return params.bindings } + expression { return REST_PR_CONDITIONS_SATISFIED == "true" } + } } - agent { label 'x86_64' } + agent { label 'x86_64_aws_bindings' } environment { - JAVA_HOME = "/usr/lib/jvm/java-8-oracle" + JAVA_HOME = '/usr/lib/jvm/java-8-oracle' } steps { script { def bindings = load ".jenkinsci/bindings.groovy" def dPullOrBuild = load ".jenkinsci/docker-pull-or-build.groovy" def platform = sh(script: 'uname -m', returnStdout: true).trim() - if (params.JavaBindings || params.PythonBindings) { + if (params.JavaBindings || params.PythonBindings || REST_PR_CONDITIONS_SATISFIED == "true") { def iC = dPullOrBuild.dockerPullOrUpdate( "$platform-develop-build", "${env.GIT_RAW_BASE_URL}/${env.GIT_COMMIT}/docker/develop/Dockerfile", "${env.GIT_RAW_BASE_URL}/${env.GIT_PREVIOUS_COMMIT}/docker/develop/Dockerfile", "${env.GIT_RAW_BASE_URL}/develop/docker/develop/Dockerfile", ['PARALLELISM': params.PARALLELISM]) - if (params.JavaBindings) { - iC.inside("-v /tmp/${env.GIT_COMMIT}/bindings-artifact:/tmp/bindings-artifact") { + if (params.JavaBindings || REST_PR_CONDITIONS_SATISFIED == "true") { + iC.inside("-v /tmp/${env.GIT_COMMIT}/bindings-artifact:/tmp/bindings-artifact --user root") { bindings.doJavaBindings('linux', params.JBBuildType) } } - if (params.PythonBindings) { - iC.inside("-v /tmp/${env.GIT_COMMIT}/bindings-artifact:/tmp/bindings-artifact") { + if (params.PythonBindings || REST_PR_CONDITIONS_SATISFIED == "true") { + iC.inside("-v /tmp/${env.GIT_COMMIT}/bindings-artifact:/tmp/bindings-artifact --user root") { bindings.doPythonBindings('linux', params.PBBuildType) } } } - if (params.AndroidBindings) { + if (params.AndroidBindings || REST_PR_CONDITIONS_SATISFIED == "true") { def iC = dPullOrBuild.dockerPullOrUpdate( "android-${params.ABPlatform}-${params.ABBuildType}", "${env.GIT_RAW_BASE_URL}/${env.GIT_COMMIT}/docker/android/Dockerfile", @@ -428,7 +462,7 @@ pipeline { ['PARALLELISM': params.PARALLELISM, 'PLATFORM': params.ABPlatform, 'BUILD_TYPE': params.ABBuildType]) sh "curl -L -o /tmp/${env.GIT_COMMIT}/entrypoint.sh ${env.GIT_RAW_BASE_URL}/${env.GIT_COMMIT}/docker/android/entrypoint.sh" sh "chmod +x /tmp/${env.GIT_COMMIT}/entrypoint.sh" - iC.inside("-v /tmp/${env.GIT_COMMIT}/entrypoint.sh:/entrypoint.sh:ro -v /tmp/${env.GIT_COMMIT}/bindings-artifact:/tmp/bindings-artifact") { + iC.inside("-v /tmp/${env.GIT_COMMIT}/entrypoint.sh:/entrypoint.sh:ro -v /tmp/${env.GIT_COMMIT}/bindings-artifact:/tmp/bindings-artifact --user root") { bindings.doAndroidBindings(params.ABABIVersion) } } @@ -439,39 +473,42 @@ pipeline { script { def artifacts = load ".jenkinsci/artifacts.groovy" def commit = env.GIT_COMMIT - if (params.JavaBindings) { + if (params.JavaBindings || REST_PR_CONDITIONS_SATISFIED == "true") { javaBindingsFilePaths = [ '/tmp/${GIT_COMMIT}/bindings-artifact/java-bindings-*.zip' ] artifacts.uploadArtifacts(javaBindingsFilePaths, '/iroha/bindings/java') } - if (params.PythonBindings) { + if (params.PythonBindings || REST_PR_CONDITIONS_SATISFIED == "true") { pythonBindingsFilePaths = [ '/tmp/${GIT_COMMIT}/bindings-artifact/python-bindings-*.zip' ] artifacts.uploadArtifacts(pythonBindingsFilePaths, '/iroha/bindings/python') } - if (params.AndroidBindings) { + if (params.AndroidBindings || REST_PR_CONDITIONS_SATISFIED == "true") { androidBindingsFilePaths = [ '/tmp/${GIT_COMMIT}/bindings-artifact/android-bindings-*.zip' ] artifacts.uploadArtifacts(androidBindingsFilePaths, '/iroha/bindings/android') } } } cleanup { - sh "rm -rf /tmp/${env.GIT_COMMIT}" + sh "sudo rm -rf /tmp/${env.GIT_COMMIT}" cleanWs() } } } - stage ('Windows bindings') { + stage ('windows_bindings') { when { beforeAgent true - expression { return params.x86_64_win } + anyOf { + expression { return params.x86_64_win } + expression { return REST_PR_CONDITIONS_SATISFIED == "true" } + } } agent { label 'win' } steps { script { def bindings = load ".jenkinsci/bindings.groovy" - if (params.JavaBindings) { + if (params.JavaBindings || REST_PR_CONDITIONS_SATISFIED == "true") { bindings.doJavaBindings('windows', params.JBBuildType) } - if (params.PythonBindings) { + if (params.PythonBindings || REST_PR_CONDITIONS_SATISFIED == "true") { bindings.doPythonBindings('windows', params.PBBuildType) } } @@ -481,20 +518,70 @@ pipeline { script { def artifacts = load ".jenkinsci/artifacts.groovy" def commit = env.GIT_COMMIT - if (params.JavaBindings) { + if (params.JavaBindings || REST_PR_CONDITIONS_SATISFIED == "true") { javaBindingsFilePaths = [ '/tmp/${GIT_COMMIT}/bindings-artifact/java-bindings-*.zip' ] artifacts.uploadArtifacts(javaBindingsFilePaths, '/iroha/bindings/java') } - if (params.PythonBindings) { + if (params.PythonBindings || REST_PR_CONDITIONS_SATISFIED == "true") { pythonBindingsFilePaths = [ '/tmp/${GIT_COMMIT}/bindings-artifact/python-bindings-*.zip' ] artifacts.uploadArtifacts(pythonBindingsFilePaths, '/iroha/bindings/python') } } } - cleanup { - sh "rm -rf /tmp/${env.GIT_COMMIT}" - cleanWs() - } + } + } + } + } + } + post { + success { + script { + // merge pull request if everything is ok and clean stale docker images stored on EFS + if ( params.merge_pr ) { + def merge = load ".jenkinsci/github-api.groovy" + if (merge.mergePullRequest()) { + currentBuild.result = "SUCCESS" + def clean = load ".jenkinsci/docker-cleanup.groovy" + clean.doStaleDockerImagesCleanup() + } + else { + currentBuild.result = "FAILURE" + } + } + } + } + cleanup { + script { + def post = load ".jenkinsci/post-step.groovy" + def clean = load ".jenkinsci/docker-cleanup.groovy" + def notify = load ".jenkinsci/notifications.groovy" + notify.notifyBuildResults() + + if (params.x86_64_linux || params.merge_pr) { + node ('x86_64_aws_test') { + post.cleanUp() + } + } + if (params.armv8_linux) { + node ('armv8') { + post.cleanUp() + clean.doDockerCleanup() + } + } + if (params.armv7_linux) { + node ('armv7') { + post.cleanUp() + clean.doDockerCleanup() + } + } + if (params.x86_64_macos || params.merge_pr) { + node ('mac') { + post.macCleanUp() + } + } + if (params.x86_64_win || params.merge_pr) { + node ('win') { + post.cleanUp() } } } From f8537eb0c154b8498c8820270a2fd47e933d0736 Mon Sep 17 00:00:00 2001 From: tyvision Date: Fri, 29 Jun 2018 21:18:48 +0300 Subject: [PATCH 13/97] hotfixes to #1392 Signed-off-by: tyvision --- .jenkinsci/release-build.groovy | 8 ++++++-- Jenkinsfile | 11 +++++++++-- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/.jenkinsci/release-build.groovy b/.jenkinsci/release-build.groovy index 422b567c83..5403512933 100644 --- a/.jenkinsci/release-build.groovy +++ b/.jenkinsci/release-build.groovy @@ -52,7 +52,9 @@ def doReleaseBuild() { sh "chmod +x /tmp/${env.GIT_COMMIT}/entrypoint.sh" iCRelease = docker.build("${DOCKER_REGISTRY_BASENAME}:${GIT_COMMIT}-${BUILD_NUMBER}-release", "--no-cache -f /tmp/${env.GIT_COMMIT}/Dockerfile /tmp/${env.GIT_COMMIT}") if (env.GIT_LOCAL_BRANCH == 'develop') { - iCRelease.push("${platform}-develop") + withDockerRegistry([ credentialsId: "docker-hub-credentials", url: "" ]) { + iCRelease.push("${platform}-develop") + } if (manifest.manifestSupportEnabled()) { manifest.manifestCreate("${DOCKER_REGISTRY_BASENAME}:develop", ["${DOCKER_REGISTRY_BASENAME}:x86_64-develop", @@ -73,7 +75,9 @@ def doReleaseBuild() { } } else if (env.GIT_LOCAL_BRANCH == 'master') { - iCRelease.push("${platform}-latest") + withDockerRegistry([ credentialsId: "docker-hub-credentials", url: "" ]) { + iCRelease.push("${platform}-latest") + } if (manifest.manifestSupportEnabled()) { manifest.manifestCreate("${DOCKER_REGISTRY_BASENAME}:latest", ["${DOCKER_REGISTRY_BASENAME}:x86_64-latest", diff --git a/Jenkinsfile b/Jenkinsfile index cb78c4b76e..cfea416560 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -215,6 +215,7 @@ pipeline { } } agent { label 'x86_64_aws_cov'} + options { skipDefaultCheckout() } steps { script { def coverage = load '.jenkinsci/debug-build.groovy' @@ -237,6 +238,7 @@ pipeline { } } agent { label 'x86_64_aws_test' } + options { skipDefaultCheckout() } steps { script { def debugBuild = load ".jenkinsci/debug-build.groovy" @@ -262,6 +264,7 @@ pipeline { } } agent { label 'armv7' } + options { skipDefaultCheckout() } steps { script { def debugBuild = load ".jenkinsci/debug-build.groovy" @@ -287,6 +290,7 @@ pipeline { } } agent { label 'armv8' } + options { skipDefaultCheckout() } steps { script { def debugBuild = load ".jenkinsci/debug-build.groovy" @@ -313,6 +317,7 @@ pipeline { } } agent { label 'mac' } + options { skipDefaultCheckout() } steps { script { def macDebugBuild = load ".jenkinsci/mac-debug-build.groovy" @@ -346,6 +351,7 @@ pipeline { parallel { stage('lcov_cobertura') { agent { label 'x86_64_aws_cov' } + options { skipDefaultCheckout() } steps { script { def coverage = load '.jenkinsci/debug-build.groovy' @@ -355,6 +361,7 @@ pipeline { } stage('sonarqube') { agent { label 'x86_64_aws_cov' } + options { skipDefaultCheckout() } steps { script { def coverage = load '.jenkinsci/debug-build.groovy' @@ -379,6 +386,7 @@ pipeline { } } agent { label 'x86_64_aws_build' } + options { skipDefaultCheckout() } steps { script { def releaseBuild = load '.jenkinsci/release-build.groovy' @@ -401,11 +409,10 @@ pipeline { beforeAgent true anyOf { expression { return params.Doxygen } - expression { return GIT_LOCAL_BRANCH ==~ /(master|develop)/ } expression { return REST_PR_CONDITIONS_SATISFIED == "true" } } } - agent { label 'x86_64_aws_cov' } + agent { label 'x86_64_aws_docs' } steps { script { def doxygen = load ".jenkinsci/doxygen.groovy" From f07b9e314114e310dc58499a437f87305d4cc207 Mon Sep 17 00:00:00 2001 From: Akvinikym Date: Mon, 2 Jul 2018 18:40:27 +0300 Subject: [PATCH 14/97] Stateful error responses to log (#1493) Stateful error responses are now written to log Signed-off-by: Akvinikym --- irohad/ametsuchi/impl/temporary_wsv_impl.cpp | 89 +++++++----- irohad/ametsuchi/impl/temporary_wsv_impl.hpp | 7 +- irohad/ametsuchi/temporary_wsv.hpp | 15 +- irohad/simulator/impl/simulator.cpp | 12 +- irohad/validation/CMakeLists.txt | 3 +- .../impl/stateful_validator_impl.cpp | 134 +++++++++++++++--- .../impl/stateful_validator_impl.hpp | 12 +- irohad/validation/stateful_validator.hpp | 6 +- .../validation/stateful_validator_common.hpp | 46 ++++++ .../irohad/simulator/simulator_test.cpp | 8 +- .../irohad/validation/validation_mocks.hpp | 3 +- 11 files changed, 249 insertions(+), 86 deletions(-) create mode 100644 irohad/validation/stateful_validator_common.hpp diff --git a/irohad/ametsuchi/impl/temporary_wsv_impl.cpp b/irohad/ametsuchi/impl/temporary_wsv_impl.cpp index ea5637ece1..5045bf09df 100644 --- a/irohad/ametsuchi/impl/temporary_wsv_impl.cpp +++ b/irohad/ametsuchi/impl/temporary_wsv_impl.cpp @@ -38,49 +38,68 @@ namespace iroha { transaction_->exec("BEGIN;"); } - bool TemporaryWsvImpl::apply( + expected::Result + TemporaryWsvImpl::apply( const shared_model::interface::Transaction &tx, - std::function apply_function) { + std::function( + const shared_model::interface::Transaction &, WsvQuery &)> + apply_function) { const auto &tx_creator = tx.creatorAccountId(); command_executor_->setCreatorAccountId(tx_creator); command_validator_->setCreatorAccountId(tx_creator); - auto execute_command = [this, &tx_creator](auto &command) { - auto account = wsv_->getAccount(tx_creator).value(); - - // Temporary variant: going to be a chain of results in future pull - // requests - auto validation_result = - boost::apply_visitor(*command_validator_, command.get()) - .match([](expected::Value &) { return true; }, - [this](expected::Error &e) { - log_->error(e.error.toString()); - return false; - }); - if (not validation_result) { - return false; - } - auto execution_result = - boost::apply_visitor(*command_executor_, command.get()); - return execution_result.match( - [](expected::Value &v) { return true; }, - [this](expected::Error &e) { - log_->error(e.error.toString()); - return false; - }); + auto execute_command = [this](auto &command, size_t command_index) + -> expected::Result { + // Validate command + return expected::map_error( + boost::apply_visitor(*command_validator_, command.get()), + [command_index](CommandError &error) { + return validation::CommandNameAndError{ + error.command_name, + (boost::format("stateful validation error: could " + "not validate " + "command with index %d: %s") + % command_index % error.toString()) + .str()}; + }) + // Execute commands + .and_res(expected::map_error( + boost::apply_visitor(*command_executor_, command.get()), + [command_index](CommandError &error) { + return validation::CommandNameAndError{ + error.command_name, + (boost::format("stateful validation error: could not " + "execute command with index %d: %s") + % command_index % error.toString()) + .str()}; + })); }; transaction_->exec("SAVEPOINT savepoint_;"); - auto result = - apply_function(tx, *wsv_) - and std::all_of( - tx.commands().begin(), tx.commands().end(), execute_command); - if (result) { + + return apply_function(tx, *wsv_) | [this, &execute_command, &tx]() + -> expected::Result { + // check transaction's commands validness + const auto &commands = tx.commands(); + validation::CommandNameAndError cmd_name_error; + for (size_t i = 0; i < commands.size(); ++i) { + // in case of failed command, rollback and return + if (not execute_command(commands[i], i) + .match( + [](expected::Value &) { return true; }, + [&cmd_name_error]( + expected::Error + &error) { + cmd_name_error = error.error; + return false; + })) { + transaction_->exec("ROLLBACK TO SAVEPOINT savepoint_;"); + return expected::makeError(cmd_name_error); + } + } + // success transaction_->exec("RELEASE SAVEPOINT savepoint_;"); - } else { - transaction_->exec("ROLLBACK TO SAVEPOINT savepoint_;"); - } - return result; + return {}; + }; } TemporaryWsvImpl::~TemporaryWsvImpl() { diff --git a/irohad/ametsuchi/impl/temporary_wsv_impl.hpp b/irohad/ametsuchi/impl/temporary_wsv_impl.hpp index 0b962d4c43..c4c27c5a4e 100644 --- a/irohad/ametsuchi/impl/temporary_wsv_impl.hpp +++ b/irohad/ametsuchi/impl/temporary_wsv_impl.hpp @@ -33,10 +33,11 @@ namespace iroha { TemporaryWsvImpl(std::unique_ptr connection, std::unique_ptr transaction); - bool apply( + expected::Result apply( const shared_model::interface::Transaction &, - std::function function) override; + std::function( + const shared_model::interface::Transaction &, WsvQuery &)> + function) override; ~TemporaryWsvImpl() override; diff --git a/irohad/ametsuchi/temporary_wsv.hpp b/irohad/ametsuchi/temporary_wsv.hpp index edd02851d3..f7ae5a6ef8 100644 --- a/irohad/ametsuchi/temporary_wsv.hpp +++ b/irohad/ametsuchi/temporary_wsv.hpp @@ -22,6 +22,7 @@ #include "ametsuchi/wsv_command.hpp" #include "ametsuchi/wsv_query.hpp" +#include "validation/stateful_validator_common.hpp" namespace shared_model { namespace interface { @@ -47,14 +48,16 @@ namespace iroha { * Function parameters: * - Transaction @see transaction * - WsvQuery - world state view query interface for temporary storage - * Function returns true if the transaction is successfully applied, false - * otherwise. - * @return True if transaction was successfully applied, false otherwise + * Function returns void result value, if transaction is successfully + * applied, and string result error otherwise + * @return void result value, if transaction was successfully applied, and + * vector of strings with errors of all failed command otherwise */ - virtual bool apply( + virtual expected::Result apply( const shared_model::interface::Transaction &, - std::function function) = 0; + std::function( + const shared_model::interface::Transaction &, WsvQuery &)> + function) = 0; virtual ~TemporaryWsv() = default; }; diff --git a/irohad/simulator/impl/simulator.cpp b/irohad/simulator/impl/simulator.cpp index 72efa5ec86..db68d96fde 100644 --- a/irohad/simulator/impl/simulator.cpp +++ b/irohad/simulator/impl/simulator.cpp @@ -93,9 +93,15 @@ namespace iroha { temporaryStorageResult.match( [&](expected::Value> &temporaryStorage) { - auto validated_proposal = + auto validated_proposal_and_errors = validator_->validate(proposal, *temporaryStorage.value); - notifier_.get_subscriber().on_next(validated_proposal); + // Temporary variant; errors are lost now, but then they are going + // to be handled upwards + notifier_.get_subscriber().on_next( + validated_proposal_and_errors.first); + for (const auto &transaction_and_error: validated_proposal_and_errors.second) { + log_->error(transaction_and_error.first.second); + } }, [&](expected::Error &error) { log_->error(error.error); @@ -116,7 +122,7 @@ namespace iroha { return static_cast(tx); }); - auto sign_and_send = [this](const auto& any_block){ + auto sign_and_send = [this](const auto &any_block) { crypto_signer_->sign(*any_block); block_notifier_.get_subscriber().on_next(any_block); }; diff --git a/irohad/validation/CMakeLists.txt b/irohad/validation/CMakeLists.txt index d0be3ffb91..ce6587ad11 100644 --- a/irohad/validation/CMakeLists.txt +++ b/irohad/validation/CMakeLists.txt @@ -26,7 +26,8 @@ target_link_libraries(stateful_validator ) add_library(chain_validator - impl/chain_validator_impl.cpp) + impl/chain_validator_impl.cpp + ) target_link_libraries(chain_validator rxcpp shared_model_interfaces diff --git a/irohad/validation/impl/stateful_validator_impl.cpp b/irohad/validation/impl/stateful_validator_impl.cpp index f273d51699..bcce329c06 100644 --- a/irohad/validation/impl/stateful_validator_impl.cpp +++ b/irohad/validation/impl/stateful_validator_impl.cpp @@ -17,46 +17,131 @@ #include "validation/impl/stateful_validator_impl.hpp" +#include #include +#include #include +#include #include "builders/protobuf/proposal.hpp" +#include "common/result.hpp" #include "validation/utils.hpp" namespace iroha { namespace validation { + /** + * Forms a readable error string from transaction signatures and account + * signatories + * @param signatures of the transaction + * @param signatories of the transaction creator + * @return well-formed error string + */ + static std::string formSignaturesErrorMsg( + const shared_model::interface::types::SignatureRangeType &signatures, + const std::vector + &signatories) { + std::string signatures_string, signatories_string; + for (const auto &signature : signatures) { + signatures_string.append(signature.publicKey().toString().append("\n")); + } + for (const auto &signatory : signatories) { + signatories_string.append(signatory.toString().append("\n")); + } + return (boost::format( + "stateful validator error: signatures in transaction are not " + "account signatories:\n" + "signatures' public keys: %s\n" + "signatories: %s") + % signatures_string % signatories_string) + .str(); + } + StatefulValidatorImpl::StatefulValidatorImpl() { log_ = logger::log("SFV"); } - std::shared_ptr - StatefulValidatorImpl::validate( + validation::VerifiedProposalAndErrors StatefulValidatorImpl::validate( const shared_model::interface::Proposal &proposal, ametsuchi::TemporaryWsv &temporaryWsv) { log_->info("transactions in proposal: {}", proposal.transactions().size()); auto checking_transaction = [](const auto &tx, auto &queries) { - return bool(queries.getAccount(tx.creatorAccountId()) | - [&](const auto &account) { - // Check if tx creator has account and has quorum to - // execute transaction - return boost::size(tx.signatures()) >= account->quorum() - ? queries.getSignatories(tx.creatorAccountId()) - : boost::none; - } - | - [&](const auto &signatories) { - // Check if signatures in transaction are account - // signatory - return signaturesSubset(tx.signatures(), signatories) - ? boost::make_optional(signatories) - : boost::none; - }); + return expected::Result( + [&]() -> expected::Result< + std::shared_ptr, + validation::CommandNameAndError> { + // Check if tx creator has account + auto account = queries.getAccount(tx.creatorAccountId()); + if (account) { + return expected::makeValue(*account); + } + return expected::makeError(validation::CommandNameAndError{ + "Initial transaction verification: no such account", + (boost::format("stateful validator error: could not fetch " + "account with id %s") + % tx.creatorAccountId()) + .str()}); + }() | + [&](const auto &account) + -> expected::Result< + std::vector< + shared_model::interface::types::PubkeyType>, + validation::CommandNameAndError> { + // Check if account has signatories and quorum to execute + // transaction + if (boost::size(tx.signatures()) >= account->quorum()) { + auto signatories = + queries.getSignatories(tx.creatorAccountId()); + if (signatories) { + return expected::makeValue(*signatories); + } + return expected::makeError(validation::CommandNameAndError{ + "Initial transaction verification: could not fetch " + "signatories", + (boost::format("stateful validator error: could not fetch " + "signatories of " + "account %s") + % tx.creatorAccountId()) + .str()}); + } + return expected::makeError(validation::CommandNameAndError{ + "Initial transaction verification: not enough signatures", + (boost::format( + "stateful validator error: not enough " + "signatures in transaction; account's quorum %d, " + "transaction's " + "signatures amount %d") + % account->quorum() % boost::size(tx.signatures())) + .str()}); + } | [&tx](const auto &signatories) + -> expected::Result { + // Check if signatures in transaction are in account + // signatory + if (signaturesSubset(tx.signatures(), signatories)) { + return {}; + } + return expected::makeError(validation::CommandNameAndError{ + "Initial transaction verification: signatures are not " + "account's signatories", + formSignaturesErrorMsg(tx.signatures(), signatories)}); + }); }; - // Filter only valid transactions - auto filter = [&temporaryWsv, checking_transaction](auto &tx) { - return temporaryWsv.apply(tx, checking_transaction); + // Filter only valid transactions and accumulate errors + auto transactions_errors_log = validation::TransactionsErrors{}; + auto filter = [&temporaryWsv, + checking_transaction, + &transactions_errors_log](auto &tx) { + return temporaryWsv.apply(tx, checking_transaction) + .match( + [](expected::Value &) { return true; }, + [&transactions_errors_log, + &tx](expected::Error &error) { + transactions_errors_log.push_back( + std::make_pair(error.error, tx.hash())); + return false; + }); }; // TODO: kamilsa IR-1010 20.02.2018 rework validation logic, so that this @@ -67,6 +152,7 @@ namespace iroha { | boost::adaptors::transformed([](auto &tx) { return static_cast(tx); }); + auto validated_proposal = shared_model::proto::ProposalBuilder() .createdTime(proposal.createdTime()) .height(proposal.height()) @@ -76,8 +162,10 @@ namespace iroha { log_->info("transactions in verified proposal: {}", validated_proposal.transactions().size()); - return std::make_shared( - validated_proposal.getTransport()); + return std::make_pair(std::make_shared( + validated_proposal.getTransport()), + transactions_errors_log); } + } // namespace validation } // namespace iroha diff --git a/irohad/validation/impl/stateful_validator_impl.hpp b/irohad/validation/impl/stateful_validator_impl.hpp index dcb47ea67f..8eeb643ee7 100644 --- a/irohad/validation/impl/stateful_validator_impl.hpp +++ b/irohad/validation/impl/stateful_validator_impl.hpp @@ -31,20 +31,12 @@ namespace iroha { public: StatefulValidatorImpl(); - /** - * Function perform stateful validation on proposal - * and return proposal with valid transactions - * @param proposal - proposal for validation - * @param wsv - temporary wsv for validation, - * this wsv not affected on ledger, - * all changes after removing wsv will be ignored - * @return proposal with valid transactions - */ - std::shared_ptr validate( + VerifiedProposalAndErrors validate( const shared_model::interface::Proposal &proposal, ametsuchi::TemporaryWsv &temporaryWsv) override; logger::Logger log_; + }; } // namespace validation } // namespace iroha diff --git a/irohad/validation/stateful_validator.hpp b/irohad/validation/stateful_validator.hpp index 4e267b226f..a1bfe7164f 100644 --- a/irohad/validation/stateful_validator.hpp +++ b/irohad/validation/stateful_validator.hpp @@ -18,6 +18,7 @@ limitations under the License. #include "ametsuchi/temporary_wsv.hpp" #include "interfaces/iroha_internal/proposal.hpp" +#include "validation/stateful_validator_common.hpp" namespace iroha { namespace validation { @@ -36,9 +37,10 @@ namespace iroha { * @param wsv - temporary wsv for validation, * this wsv not affected on ledger, * all changes after removing wsv will be ignored - * @return proposal with valid transactions + * @return proposal with valid transactions and errors, which appeared in + * a process of validating */ - virtual std::shared_ptr validate( + virtual VerifiedProposalAndErrors validate( const shared_model::interface::Proposal &proposal, ametsuchi::TemporaryWsv &temporaryWsv) = 0; }; diff --git a/irohad/validation/stateful_validator_common.hpp b/irohad/validation/stateful_validator_common.hpp new file mode 100644 index 0000000000..af57a695c1 --- /dev/null +++ b/irohad/validation/stateful_validator_common.hpp @@ -0,0 +1,46 @@ +/** + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef IROHA_STATEFUL_VALIDATOR_COMMON_HPP +#define IROHA_STATEFUL_VALIDATOR_COMMON_HPP + +#include "common/types.hpp" + +namespace shared_model { + namespace interface { + class Proposal; + } +} + +namespace iroha { + namespace validation { + + /// Name of the failed command + using CommandName = std::string; + + /// Error, with which the command failed + using CommandError = std::string; + + /// Failed command's name and error + using CommandNameAndError = std::pair; + + /// Type of per-transaction errors, which appeared during validation + /// process; contains names of commands, commands errors themselves and + /// transaction hashes + using TransactionsErrors = + std::vector>; + + /// Type of verified proposal and errors appeared in the process; first + /// dimension of errors vector is transaction, second is error itself with + /// number of transaction, where it happened + using VerifiedProposalAndErrors = + std::pair, + TransactionsErrors>; + + } // namespace validation +} // namespace iroha + +#endif // IROHA_STATEFUL_VALIDATOR_COMMON_HPP diff --git a/test/module/irohad/simulator/simulator_test.cpp b/test/module/irohad/simulator/simulator_test.cpp index 45827be262..c1d0f92972 100644 --- a/test/module/irohad/simulator/simulator_test.cpp +++ b/test/module/irohad/simulator/simulator_test.cpp @@ -15,7 +15,8 @@ * limitations under the License. */ -#include "simulator/impl/simulator.hpp" +#include + #include "backend/protobuf/transaction.hpp" #include "builders/protobuf/proposal.hpp" #include "builders/protobuf/transaction.hpp" @@ -27,6 +28,7 @@ #include "module/shared_model/builders/protobuf/test_block_builder.hpp" #include "module/shared_model/builders/protobuf/test_proposal_builder.hpp" #include "module/shared_model/cryptography/crypto_model_signer_mock.hpp" +#include "simulator/impl/simulator.hpp" using namespace iroha; using namespace iroha::validation; @@ -138,7 +140,9 @@ TEST_F(SimulatorTest, ValidWhenPreviousBlock) { EXPECT_CALL(*query, getTopBlockHeight()).WillOnce(Return(1)); - EXPECT_CALL(*validator, validate(_, _)).WillOnce(Return(proposal)); + EXPECT_CALL(*validator, validate(_, _)) + .WillOnce(Return( + std::make_pair(proposal, iroha::validation::TransactionsErrors{}))); EXPECT_CALL(*ordering_gate, on_proposal()) .WillOnce(Return(rxcpp::observable<>::empty< diff --git a/test/module/irohad/validation/validation_mocks.hpp b/test/module/irohad/validation/validation_mocks.hpp index 5bdddbfa80..646f3f3500 100644 --- a/test/module/irohad/validation/validation_mocks.hpp +++ b/test/module/irohad/validation/validation_mocks.hpp @@ -21,6 +21,7 @@ #include #include "interfaces/iroha_internal/proposal.hpp" +#include "interfaces/common_objects/types.hpp" #include "validation/chain_validator.hpp" #include "validation/stateful_validator.hpp" @@ -29,7 +30,7 @@ namespace iroha { class MockStatefulValidator : public validation::StatefulValidator { public: MOCK_METHOD2(validate, - std::shared_ptr( + VerifiedProposalAndErrors( const shared_model::interface::Proposal &, ametsuchi::TemporaryWsv &)); }; From 09c0affff300cd9215618873336260a833f82a1a Mon Sep 17 00:00:00 2001 From: Kitsu Date: Mon, 2 Jul 2018 21:59:05 +0300 Subject: [PATCH 15/97] Add color diagnostics (#1515) Signed-off-by: Kitsu --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d3ae2d5202..e3ad2bbdc5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,7 +9,7 @@ endif() PROJECT(iroha C CXX) SET(CMAKE_POSITION_INDEPENDENT_CODE TRUE) -SET(CMAKE_CXX_FLAGS "-std=c++1y -Wall") +SET(CMAKE_CXX_FLAGS "-std=c++1y -Wall -fdiagnostics-color=always") SET(CMAKE_CXX_FLAGS_RELEASE "-O3") SET(CMAKE_CXX_FLAGS_DEBUG "-g -Wextra -Wno-unused-parameter -Wno-deprecated-declarations -O0") SET(CMAKE_CXX_OUTPUT_EXTENSION_REPLACE 1) From 5d3df507f0981c8874bf39c8ee6f5d432a0c9299 Mon Sep 17 00:00:00 2001 From: Kitsu Date: Mon, 2 Jul 2018 21:59:46 +0300 Subject: [PATCH 16/97] Reuse Peer in mst.proto (#1522) Signed-off-by: Kitsu --- .../transport/impl/mst_transport_grpc.cpp | 4 ++-- schema/mst.proto | 9 ++------- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/irohad/multi_sig_transactions/transport/impl/mst_transport_grpc.cpp b/irohad/multi_sig_transactions/transport/impl/mst_transport_grpc.cpp index 114148d561..035ba6931d 100644 --- a/irohad/multi_sig_transactions/transport/impl/mst_transport_grpc.cpp +++ b/irohad/multi_sig_transactions/transport/impl/mst_transport_grpc.cpp @@ -54,7 +54,7 @@ grpc::Status MstTransportGrpc::SendState( auto from = std::make_shared( shared_model::proto::PeerBuilder() .address(peer.address()) - .pubkey(shared_model::crypto::PublicKey(peer.pubkey())) + .pubkey(shared_model::crypto::PublicKey(peer.peer_key())) .build()); subscriber_.lock()->onNewState(std::move(from), std::move(newState)); @@ -76,7 +76,7 @@ void MstTransportGrpc::sendState(const shared_model::interface::Peer &to, transport::MstState protoState; auto peer = protoState.mutable_peer(); - peer->set_pubkey(shared_model::crypto::toBinaryString(to.pubkey())); + peer->set_peer_key(shared_model::crypto::toBinaryString(to.pubkey())); peer->set_address(to.address()); for (auto &tx : providing_state.getTransactions()) { auto addtxs = protoState.add_transactions(); diff --git a/schema/mst.proto b/schema/mst.proto index 202b061201..4d721dadc6 100644 --- a/schema/mst.proto +++ b/schema/mst.proto @@ -2,17 +2,12 @@ syntax = "proto3"; package iroha.network.transport; import "block.proto"; +import "primitive.proto"; import "google/protobuf/empty.proto"; -// TODO: @l4l (04/05/18) remove in favor of primitive.proto IR-1321 -message Peer { - bytes pubkey = 1; - string address = 2; -} - message MstState { repeated iroha.protocol.Transaction transactions = 1; - Peer peer = 2; + iroha.protocol.Peer peer = 2; } service MstTransportGrpc { From 52e3f86e67bf65beeaf55f9a14630d8573c3d2c6 Mon Sep 17 00:00:00 2001 From: Igor Egorov Date: Tue, 3 Jul 2018 11:14:07 +0300 Subject: [PATCH 17/97] Fix tests for createRole with empty set of permissions (#1520) The behavior of createRole command was changed, so the tests had to be updated. Signed-off-by: Igor Egorov --- shared_model/packages/javascript/tests/txbuilder.js | 2 +- test/module/shared_model/bindings/BuilderTest.java | 7 ++----- test/module/shared_model/bindings/builder-test.py | 4 ++-- 3 files changed, 5 insertions(+), 8 deletions(-) diff --git a/shared_model/packages/javascript/tests/txbuilder.js b/shared_model/packages/javascript/tests/txbuilder.js index af46727baf..9487e81804 100644 --- a/shared_model/packages/javascript/tests/txbuilder.js +++ b/shared_model/packages/javascript/tests/txbuilder.js @@ -128,7 +128,7 @@ test('ModelTransactionBuilder tests', function (t) { validPermissions.set(iroha.Role_kAddPeer) validPermissions.set(iroha.Role_kAddAssetQty) - t.throws(() => correctTx.createRole('new_user_role', emptyPerm).build(), /Permission set should contain at least one permission/, 'Should throw Permission set should contain at least one permission') + t.doesNotThrow(() => correctTx.createRole('new_user_role', emptyPerm).build(), null, 'Should not throw any exceptions') t.throws(() => correctTx.createRole('', validPermissions).build(), /Wrongly formed role_id, passed value: ''/, 'Should throw Wrongly formed role_id') t.throws(() => correctTx.createRole('@@@', validPermissions).build(), /Wrongly formed role_id, passed value: '@@@'/, 'Should throw Wrongly formed role_id') t.throws(() => correctTx.createRole('new_user_role', '').build(), /argument 3 of type 'shared_model::interface::RolePermissionSet/, 'Should throw ...argument 3 of type...') diff --git a/test/module/shared_model/bindings/BuilderTest.java b/test/module/shared_model/bindings/BuilderTest.java index cb72c692a2..c53aa22cdf 100644 --- a/test/module/shared_model/bindings/BuilderTest.java +++ b/test/module/shared_model/bindings/BuilderTest.java @@ -819,11 +819,8 @@ void createRoleWithInvalidName() { @Test void createRoleEmptyPermissions() { - RolePermissionSet permissions = new RolePermissionSet(); - - ModelTransactionBuilder builder = new ModelTransactionBuilder(); - builder.createRole("new_role", permissions); - assertThrows(IllegalArgumentException.class, builder::build); + UnsignedTx tx = builder.createRole("new_role", new RolePermissionSet()).build(); + assertTrue(checkProtoTx(proto(tx))); } /* ====================== DetachRole Tests ====================== */ diff --git a/test/module/shared_model/bindings/builder-test.py b/test/module/shared_model/bindings/builder-test.py index c82ca7175e..f7e6d21b4b 100644 --- a/test/module/shared_model/bindings/builder-test.py +++ b/test/module/shared_model/bindings/builder-test.py @@ -532,8 +532,8 @@ def test_create_role_with_invalid_name(self): self.base().createRole(name, iroha.RolePermissionSet([iroha.Role_kReceive, iroha.Role_kGetRoles])).build() def test_create_role_with_empty_permissions(self): - with self.assertRaises(ValueError): - self.base().createRole("user", iroha.RolePermissionSet([])).build() + tx = self.builder.createRole("user", iroha.RolePermissionSet([])).build() + self.assertTrue(self.check_proto_tx(self.proto(tx))) # ====================== DetachRole Tests ====================== From 376dd532402afc31c90c61077a8c2fe0b1649661 Mon Sep 17 00:00:00 2001 From: kamilsa Date: Tue, 3 Jul 2018 11:31:41 +0300 Subject: [PATCH 18/97] Proposal and block txs validation (#1506) * Update proposal and block validators Signed-off-by: kamilsa --- .../iroha_internal/transaction_sequence.cpp | 15 +++++++- .../iroha_internal/transaction_sequence.hpp | 4 ++- shared_model/validators/block_validator.hpp | 21 +++++++----- .../validators/container_validator.hpp | 19 ++++++++--- shared_model/validators/default_validator.hpp | 22 +++++++----- .../validators/proposal_validator.hpp | 21 +++++++----- ...gned_transactions_collection_validator.cpp | 34 +++++++++++++++++-- ...gned_transactions_collection_validator.hpp | 5 ++- .../transactions_collection_validator.hpp | 9 +++++ ...gned_transactions_collection_validator.cpp | 34 +++++++++++++++++-- ...gned_transactions_collection_validator.hpp | 5 ++- 11 files changed, 151 insertions(+), 38 deletions(-) diff --git a/shared_model/interfaces/iroha_internal/transaction_sequence.cpp b/shared_model/interfaces/iroha_internal/transaction_sequence.cpp index 2f21609c48..04aaf8f682 100644 --- a/shared_model/interfaces/iroha_internal/transaction_sequence.cpp +++ b/shared_model/interfaces/iroha_internal/transaction_sequence.cpp @@ -4,14 +4,18 @@ */ #include "interfaces/iroha_internal/transaction_sequence.hpp" +#include "validators/field_validator.hpp" +#include "validators/transaction_validator.hpp" namespace shared_model { namespace interface { + template iroha::expected::Result TransactionSequence::createTransactionSequence( const types::TransactionsForwardCollectionType &transactions, - const validation::TransactionsCollectionValidator &validator) { + const validation::TransactionsCollectionValidator + &validator) { auto answer = validator.validate(transactions); if (answer.hasErrors()) { return iroha::expected::makeError(answer.reason()); @@ -19,6 +23,15 @@ namespace shared_model { return iroha::expected::makeValue(TransactionSequence(transactions)); } + template iroha::expected::Result + TransactionSequence::createTransactionSequence( + const types::TransactionsForwardCollectionType &transactions, + const validation::TransactionsCollectionValidator< + validation::TransactionValidator< + validation::FieldValidator, + validation::CommandValidatorVisitor< + validation::FieldValidator>>> &validator); + types::TransactionsForwardCollectionType TransactionSequence::transactions() { return transactions_; diff --git a/shared_model/interfaces/iroha_internal/transaction_sequence.hpp b/shared_model/interfaces/iroha_internal/transaction_sequence.hpp index 9c5a8fe0ed..43e3aa7f56 100644 --- a/shared_model/interfaces/iroha_internal/transaction_sequence.hpp +++ b/shared_model/interfaces/iroha_internal/transaction_sequence.hpp @@ -28,10 +28,12 @@ namespace shared_model { * @return Result containing transaction sequence if validation successful * and string message containing error otherwise */ + template static iroha::expected::Result createTransactionSequence( const types::TransactionsForwardCollectionType &transactions, - const validation::TransactionsCollectionValidator &validator); + const validation::TransactionsCollectionValidator< + TransactionValidator> &validator); /** * Get transactions collection diff --git a/shared_model/validators/block_validator.hpp b/shared_model/validators/block_validator.hpp index 31b1b95c8c..975b762b02 100644 --- a/shared_model/validators/block_validator.hpp +++ b/shared_model/validators/block_validator.hpp @@ -33,10 +33,14 @@ namespace shared_model { /** * Class that validates block */ - template - class BlockValidator : public ContainerValidator { + template + class BlockValidator + : public ContainerValidator { public: /** * Applies validation on block @@ -44,10 +48,11 @@ namespace shared_model { * @return Answer containing found error if any */ Answer validate(const interface::Block &block) const { - return ContainerValidator::validate(block, - "Block"); + return ContainerValidator< + interface::Block, + FieldValidator, + TransactionValidator, + TransactionsCollectionValidator>::validate(block, "Block"); } }; diff --git a/shared_model/validators/container_validator.hpp b/shared_model/validators/container_validator.hpp index afec679e87..79a7b5ecde 100644 --- a/shared_model/validators/container_validator.hpp +++ b/shared_model/validators/container_validator.hpp @@ -34,7 +34,8 @@ namespace shared_model { */ template + typename TransactionValidator, + typename TransactionsCollectionValidator> class ContainerValidator { protected: void validateTransaction( @@ -50,18 +51,25 @@ namespace shared_model { ReasonsGroupType &reason, const interface::types::TransactionsCollectionType &transactions) const { - for (const auto &tx : transactions) { - validateTransaction(reason, tx); + auto answer = transactions_collection_validator_.validate(transactions); + if (answer.hasErrors()) { + reason.second.push_back(answer.reason()); } } public: - ContainerValidator( + explicit ContainerValidator( + const TransactionsCollectionValidator + &transactions_collection_validator = + TransactionsCollectionValidator(), const TransactionValidator &transaction_validator = TransactionValidator(), const FieldValidator &field_validator = FieldValidator()) - : transaction_validator_(transaction_validator), + : transactions_collection_validator_( + transactions_collection_validator), + transaction_validator_(transaction_validator), field_validator_(field_validator) {} + Answer validate(const Iface &cont, std::string reason_name) const { Answer answer; ReasonsGroupType reason; @@ -76,6 +84,7 @@ namespace shared_model { } private: + TransactionsCollectionValidator transactions_collection_validator_; TransactionValidator transaction_validator_; protected: diff --git a/shared_model/validators/default_validator.hpp b/shared_model/validators/default_validator.hpp index 072d2043ad..a1055100a8 100644 --- a/shared_model/validators/default_validator.hpp +++ b/shared_model/validators/default_validator.hpp @@ -27,20 +27,29 @@ #include "validators/query_validator.hpp" #include "validators/signable_validator.hpp" #include "validators/transaction_validator.hpp" +#include "validators/transactions_collection/signed_transactions_collection_validator.hpp" +#include "validators/transactions_collection/unsigned_transactions_collection_validator.hpp" namespace shared_model { namespace validation { using DefaultTransactionValidator = TransactionValidator>; + using DefaultQueryValidator = QueryValidator>; + using DefaultBlocksQueryValidator = BlocksQueryValidator; - using DefaultProposalValidator = - ProposalValidator; - using DefaultBlockValidator = - BlockValidator; + using DefaultProposalValidator = ProposalValidator< + FieldValidator, + DefaultTransactionValidator, + UnsignedTransactionsCollectionValidator>; + + using DefaultBlockValidator = BlockValidator< + FieldValidator, + DefaultTransactionValidator, + SignedTransactionsCollectionValidator>; using DefaultEmptyBlockValidator = EmptyBlockValidator; @@ -57,11 +66,6 @@ namespace shared_model { const interface::Query &, FieldValidator>; - using DefaultSignableProposalValidator = - SignableModelValidator; - using DefaultSignableBlockValidator = SignableModelValidator - class ProposalValidator : public ContainerValidator { + template + class ProposalValidator + : public ContainerValidator { public: /** * Applies validation on proposal @@ -44,10 +48,11 @@ namespace shared_model { * @return Answer containing found error if any */ Answer validate(const interface::Proposal &prop) const { - return ContainerValidator::validate(prop, - "Proposal"); + return ContainerValidator< + interface::Proposal, + FieldValidator, + TransactionValidator, + TransactionsCollectionValidator>::validate(prop, "Proposal"); } }; diff --git a/shared_model/validators/transactions_collection/signed_transactions_collection_validator.cpp b/shared_model/validators/transactions_collection/signed_transactions_collection_validator.cpp index c23266de92..528858d7a3 100644 --- a/shared_model/validators/transactions_collection/signed_transactions_collection_validator.cpp +++ b/shared_model/validators/transactions_collection/signed_transactions_collection_validator.cpp @@ -5,14 +5,44 @@ #include "validators/transactions_collection/signed_transactions_collection_validator.hpp" +#include +#include "validators/field_validator.hpp" +#include "validators/transaction_validator.hpp" + namespace shared_model { namespace validation { - Answer SignedTransactionsCollectionValidator::validate( + template + Answer + SignedTransactionsCollectionValidator::validate( const interface::types::TransactionsForwardCollectionType &transactions) const { - return Answer(); + ReasonsGroupType reason; + reason.first = "Transaction list"; + for (const auto &tx : transactions) { + auto answer = + SignedTransactionsCollectionValidator::transaction_validator_ + .validate(tx); + if (answer.hasErrors()) { + auto message = + (boost::format("Tx %s : %s") % tx.hash().hex() % answer.reason()) + .str(); + reason.second.push_back(message); + } + } + + Answer res; + if (not reason.second.empty()) { + res.addReason(std::move(reason)); + } + return res; } + template Answer SignedTransactionsCollectionValidator< + TransactionValidator>>:: + validate(const interface::types::TransactionsForwardCollectionType + &transactions) const; + } // namespace validation } // namespace shared_model diff --git a/shared_model/validators/transactions_collection/signed_transactions_collection_validator.hpp b/shared_model/validators/transactions_collection/signed_transactions_collection_validator.hpp index 882e8b3009..d5853b3c50 100644 --- a/shared_model/validators/transactions_collection/signed_transactions_collection_validator.hpp +++ b/shared_model/validators/transactions_collection/signed_transactions_collection_validator.hpp @@ -16,9 +16,12 @@ namespace shared_model { * transaction from the collection to be unsigned. Batch logic should be * checked */ + template class SignedTransactionsCollectionValidator - : public TransactionsCollectionValidator { + : public TransactionsCollectionValidator { public: + using TransactionsCollectionValidator< + TransactionValidator>::TransactionsCollectionValidator; Answer validate(const interface::types::TransactionsForwardCollectionType &transactions) const override; }; diff --git a/shared_model/validators/transactions_collection/transactions_collection_validator.hpp b/shared_model/validators/transactions_collection/transactions_collection_validator.hpp index 3255295720..2a530d7257 100644 --- a/shared_model/validators/transactions_collection/transactions_collection_validator.hpp +++ b/shared_model/validators/transactions_collection/transactions_collection_validator.hpp @@ -16,8 +16,17 @@ namespace shared_model { * Validator of transaction's collection, this is not fair implementation * now, it always returns empty answer */ + template class TransactionsCollectionValidator { + protected: + TransactionValidator transaction_validator_; + public: + TransactionsCollectionValidator( + const TransactionValidator &transactions_validator = + TransactionValidator()) + : transaction_validator_(transactions_validator) {} + /** * Validates collection of transactions * @param transactions collection of transactions diff --git a/shared_model/validators/transactions_collection/unsigned_transactions_collection_validator.cpp b/shared_model/validators/transactions_collection/unsigned_transactions_collection_validator.cpp index af8bdf9715..c68d194e3f 100644 --- a/shared_model/validators/transactions_collection/unsigned_transactions_collection_validator.cpp +++ b/shared_model/validators/transactions_collection/unsigned_transactions_collection_validator.cpp @@ -5,14 +5,44 @@ #include "validators/transactions_collection/unsigned_transactions_collection_validator.hpp" +#include +#include "validators/field_validator.hpp" +#include "validators/transaction_validator.hpp" + namespace shared_model { namespace validation { - Answer UnsignedTransactionsCollectionValidator::validate( + template + Answer + UnsignedTransactionsCollectionValidator::validate( const interface::types::TransactionsForwardCollectionType &transactions) const { - return Answer(); + ReasonsGroupType reason; + reason.first = "Transaction list"; + for (const auto &tx : transactions) { + auto answer = + UnsignedTransactionsCollectionValidator::transaction_validator_ + .validate(tx); + if (answer.hasErrors()) { + auto message = + (boost::format("Tx %s : %s") % tx.hash().hex() % answer.reason()) + .str(); + reason.second.push_back(message); + } + } + + Answer res; + if (not reason.second.empty()) { + res.addReason(std::move(reason)); + } + return res; } + template Answer UnsignedTransactionsCollectionValidator< + TransactionValidator>>:: + validate(const interface::types::TransactionsForwardCollectionType + &transactions) const; + } // namespace validation } // namespace shared_model diff --git a/shared_model/validators/transactions_collection/unsigned_transactions_collection_validator.hpp b/shared_model/validators/transactions_collection/unsigned_transactions_collection_validator.hpp index 5afdbbcf97..6513dfd76f 100644 --- a/shared_model/validators/transactions_collection/unsigned_transactions_collection_validator.hpp +++ b/shared_model/validators/transactions_collection/unsigned_transactions_collection_validator.hpp @@ -15,9 +15,12 @@ namespace shared_model { * Unsigned transactions collection validator allows to some transaction * from the collection to be unsigned. Batch logic should be checked */ + template class UnsignedTransactionsCollectionValidator - : public TransactionsCollectionValidator { + : public TransactionsCollectionValidator { public: + using TransactionsCollectionValidator< + TransactionValidator>::TransactionsCollectionValidator; Answer validate(const interface::types::TransactionsForwardCollectionType &transactions) const override; }; From e34c3e0db5c2ad34fb290239310d94523da95ae6 Mon Sep 17 00:00:00 2001 From: Kitsu Date: Tue, 3 Jul 2018 12:28:34 +0300 Subject: [PATCH 19/97] Remove account_id from Add/Subtract commands (#1511) Signed-off-by: Kitsu --- docs/source/api/commands.rst | 16 +-- docs/source/guides/libraries/python.rst | 2 +- example/python/tx-example.py | 2 +- .../impl/interactive_transaction_cli.cpp | 5 +- irohad/execution/impl/command_executor.cpp | 42 ++---- irohad/model/commands/add_asset_quantity.hpp | 11 +- .../commands/subtract_asset_quantity.hpp | 11 +- .../converters/impl/json_command_factory.cpp | 16 +-- .../converters/impl/pb_command_factory.cpp | 6 - irohad/model/generators/command_generator.hpp | 8 +- .../generators/impl/command_generator.cpp | 13 +- irohad/model/impl/model_operators.cpp | 6 +- schema/commands.proto | 10 +- .../impl/proto_add_asset_quantity.cpp | 4 - .../impl/proto_subtract_asset_quantity.cpp | 5 - .../commands/proto_add_asset_quantity.hpp | 2 - .../proto_subtract_asset_quantity.hpp | 2 - .../bindings/model_transaction_builder.cpp | 6 +- .../bindings/model_transaction_builder.hpp | 4 - .../transaction_template.hpp | 11 +- .../commands/add_asset_quantity.hpp | 4 - .../commands/impl/add_asset_quantity.cpp | 4 +- .../commands/impl/subtract_asset_quantity.cpp | 4 +- .../commands/subtract_asset_quantity.hpp | 4 - .../packages/javascript/tests/txbuilder.js | 30 ++-- .../validators/transaction_validator.hpp | 2 - .../acceptance/add_asset_qty_test.cpp | 95 +------------ .../acceptance/get_account_assets_test.cpp | 7 +- .../acceptance/grant_permission_test.cpp | 58 ++++---- .../acceptance/subtract_asset_qty_test.cpp | 62 +-------- .../acceptance/transfer_asset_test.cpp | 8 +- .../acceptance/tx_acceptance_test.cpp | 131 +++++------------- test/integration/pipeline/pipeline_test.cpp | 2 +- .../transport/ordering_gate_service_test.cpp | 2 +- .../irohad/ametsuchi/ametsuchi_test.cpp | 36 ++--- .../command_validate_execute_test.cpp | 98 ++++++------- .../model/converters/json_commands_test.cpp | 4 +- .../model/converters/pb_commands_test.cpp | 2 - .../model/operators/model_operators_test.cpp | 2 - .../irohad/ordering/ordering_gate_test.cpp | 4 +- .../irohad/ordering/ordering_service_test.cpp | 2 +- .../irohad/simulator/simulator_test.cpp | 4 +- .../shared_proto_transaction_test.cpp | 26 ++-- .../shared_model/bindings/BuilderTest.java | 71 ++-------- .../shared_model/bindings/builder-test.py | 52 ++----- .../builders/protobuf/block_builder_test.cpp | 2 +- test/regression/regression_test.cpp | 4 +- 47 files changed, 254 insertions(+), 648 deletions(-) diff --git a/docs/source/api/commands.rst b/docs/source/api/commands.rst index 4ac57860ef..b09e356226 100644 --- a/docs/source/api/commands.rst +++ b/docs/source/api/commands.rst @@ -19,9 +19,8 @@ Schema .. code-block:: proto message AddAssetQuantity { - string account_id = 1; - string asset_id = 2; - Amount amount = 3; + string asset_id = 1; + Amount amount = 2; } message uint256 { @@ -48,7 +47,6 @@ Structure :header: "Field", "Description", "Constraint", "Example" :widths: 15, 30, 20, 15 - "Account ID", "account id in which to add asset", "@", "alex@morgan" "Asset ID", "id of the asset", "#", "usd#morgan" "Amount", "positive amount of the asset to add", "> 0", "200.02" @@ -58,7 +56,6 @@ Validation 1. Asset and account should exist 2. Added quantity precision should be equal to asset precision 3. Creator of a transaction should have a role which has permissions for issuing assets -4. Creator of a transaction adds account quantity to his/her account only Add peer -------- @@ -557,10 +554,9 @@ Schema .. code-block:: proto - message AddAssetQuantity { - string account_id = 1; - string asset_id = 2; - Amount amount = 3; + message SubtractAssetQuantity { + string asset_id = 1; + Amount amount = 2; } message uint256 { @@ -586,7 +582,6 @@ Structure :header: "Field", "Description", "Constraint", "Example" :widths: 15, 30, 20, 15 - "Account ID", "account id from which to subtract asset", "already existent", "makoto@soramitsu" "Asset ID", "id of the asset", "#", "usd#morgan" "Amount", "positive amount of the asset to subtract", "> 0", "200" @@ -596,7 +591,6 @@ Validation 1. Asset and account should exist 2. Added quantity precision should be equal to asset precision 3. Creator of the transaction should have a role which has permissions for subtraction of assets -4. Creator of transaction subtracts account quantity in his/her account only Transfer asset -------------- diff --git a/docs/source/guides/libraries/python.rst b/docs/source/guides/libraries/python.rst index 7a01427835..d5caf4b7c7 100644 --- a/docs/source/guides/libraries/python.rst +++ b/docs/source/guides/libraries/python.rst @@ -242,7 +242,7 @@ Create asset quantity: tx = tx_builder.creatorAccountId(creator) \ .createdTime(current_time) \ - .addAssetQuantity("admin@test", "coin#domain", "1000.2").build() + .addAssetQuantity("coin#domain", "1000.2").build() send_tx(tx, key_pair) print_status(tx) diff --git a/example/python/tx-example.py b/example/python/tx-example.py index cd519c2748..f2b9b8e80f 100644 --- a/example/python/tx-example.py +++ b/example/python/tx-example.py @@ -135,7 +135,7 @@ def add_coin_to_admin(): """ tx = tx_builder.creatorAccountId(creator) \ .createdTime(current_time) \ - .addAssetQuantity("admin@test", "coin#domain", "1000.00").build() + .addAssetQuantity("coin#domain", "1000.00").build() send_tx(tx, key_pair) print_status_streaming(tx) diff --git a/iroha-cli/interactive/impl/interactive_transaction_cli.cpp b/iroha-cli/interactive/impl/interactive_transaction_cli.cpp index 8fe7798c11..c485634949 100644 --- a/iroha-cli/interactive/impl/interactive_transaction_cli.cpp +++ b/iroha-cli/interactive/impl/interactive_transaction_cli.cpp @@ -329,8 +329,7 @@ namespace iroha_cli { std::shared_ptr InteractiveTransactionCli::parseAddAssetQuantity( std::vector params) { - auto account_id = params[0]; - auto asset_id = params[1]; + auto asset_id = params[0]; auto val_int = parser::parseValue(params[2]); auto precision = parser::parseValue(params[3]); @@ -344,7 +343,7 @@ namespace iroha_cli { } std::cout << val_int.value() << " " << precision.value() << std::endl; iroha::Amount amount(val_int.value(), precision.value()); - return generator_.generateAddAssetQuantity(account_id, asset_id, amount); + return generator_.generateAddAssetQuantity(asset_id, amount); } std::shared_ptr diff --git a/irohad/execution/impl/command_executor.cpp b/irohad/execution/impl/command_executor.cpp index ebd1bbb941..b1b32f83c4 100644 --- a/irohad/execution/impl/command_executor.cpp +++ b/irohad/execution/impl/command_executor.cpp @@ -81,13 +81,13 @@ namespace iroha { } auto command_amount = makeAmountWithPrecision(command.amount(), asset.value()->precision()); - if (not queries->getAccount(command.accountId())) { + if (not queries->getAccount(creator_account_id)) { return makeCommandError( - (boost::format("account %s is absent") % command.accountId()).str(), + (boost::format("account %s is absent") % creator_account_id).str(), command_name); } auto account_asset = - queries->getAccountAsset(command.accountId(), command.assetId()); + queries->getAccountAsset(creator_account_id, command.assetId()); auto new_balance = command_amount | [this](const auto &amount) { return amount_builder_.precision(amount->precision()) @@ -109,13 +109,13 @@ namespace iroha { result = (*new_balance_val.value + account_asset.value()->balance()) | [this, &command](const auto &balance) { return account_asset_builder_.balance(*balance) - .accountId(command.accountId()) + .accountId(creator_account_id) .assetId(command.assetId()) .build(); }; } else { result = account_asset_builder_.balance(*new_balance_val.value) - .accountId(command.accountId()) + .accountId(creator_account_id) .assetId(command.assetId()) .build(); } @@ -359,10 +359,10 @@ namespace iroha { auto command_amount = makeAmountWithPrecision(command.amount(), asset.value()->precision()); auto account_asset = - queries->getAccountAsset(command.accountId(), command.assetId()); + queries->getAccountAsset(creator_account_id, command.assetId()); if (not account_asset) { return makeCommandError((boost::format("%s do not have %s") - % command.accountId() % command.assetId()) + % creator_account_id % command.assetId()) .str(), command_name); } @@ -495,18 +495,9 @@ namespace iroha { ametsuchi::WsvQuery &queries, const shared_model::interface::types::AccountIdType &creator_account_id) { auto command_name = "AddAssetQuantity"; - // Check if creator has MoneyCreator permission. - // One can only add to his/her account // TODO: 03.02.2018 grimadas IR-935, Separate asset creation for distinct // asset types, now: anyone having permission "can_add_asset_qty" can add // any asset - if (creator_account_id != command.accountId()) { - return makeCommandError( - "has permission command validation failed: creator account " - + creator_account_id + " is not command account " - + command.accountId(), - command_name); - } if (not checkAccountRolePermission( creator_account_id, queries, Role::kAddAssetQty)) { return makeCommandError( @@ -850,23 +841,14 @@ namespace iroha { ametsuchi::WsvQuery &queries, const shared_model::interface::types::AccountIdType &creator_account_id) { auto command_name = "SubtractAssetQuantity"; - if (creator_account_id == command.accountId()) { - if (checkAccountRolePermission( - creator_account_id, queries, Role::kSubtractAssetQty)) { - return {}; - } else { - return makeCommandError( - "has permission command validation failed: account " - + creator_account_id + " does not have permission " - + toString(Role::kSubtractAssetQty) + " for his own account", - command_name); - } + if (checkAccountRolePermission( + creator_account_id, queries, Role::kSubtractAssetQty)) { + return {}; } else { return makeCommandError( "has permission command validation failed: account " - + creator_account_id - + " cannot subtract asset quantity from account " - + command.accountId(), + + creator_account_id + " does not have permission " + + toString(Role::kSubtractAssetQty) + " for his own account", command_name); } } diff --git a/irohad/model/commands/add_asset_quantity.hpp b/irohad/model/commands/add_asset_quantity.hpp index 6b3c3e68b8..43c87c784e 100644 --- a/irohad/model/commands/add_asset_quantity.hpp +++ b/irohad/model/commands/add_asset_quantity.hpp @@ -29,11 +29,6 @@ namespace iroha { * Add amount of asset to an account */ struct AddAssetQuantity : public Command { - /** - * Account where to add assets - */ - std::string account_id; - /** * Asset to issue * Note: must exist in the system @@ -49,10 +44,8 @@ namespace iroha { AddAssetQuantity() {} - AddAssetQuantity(const std::string &account_id, - const std::string &asset_id, - Amount amount) - : account_id(account_id), asset_id(asset_id), amount(amount) {} + AddAssetQuantity(const std::string &asset_id, Amount amount) + : asset_id(asset_id), amount(amount) {} }; } // namespace model } // namespace iroha diff --git a/irohad/model/commands/subtract_asset_quantity.hpp b/irohad/model/commands/subtract_asset_quantity.hpp index 401cdc1c3d..c7cde162c1 100644 --- a/irohad/model/commands/subtract_asset_quantity.hpp +++ b/irohad/model/commands/subtract_asset_quantity.hpp @@ -28,11 +28,6 @@ namespace iroha { * Subtract amount of asset to an account */ struct SubtractAssetQuantity : public Command { - /** - * Account where to subtract assets - */ - std::string account_id; - /** * Asset to issue * Note: must exist in the system @@ -48,10 +43,8 @@ namespace iroha { SubtractAssetQuantity() {} - SubtractAssetQuantity(const std::string &account_id, - const std::string &asset_id, - Amount amount) - : account_id(account_id), asset_id(asset_id), amount(amount) {} + SubtractAssetQuantity(const std::string &asset_id, Amount amount) + : asset_id(asset_id), amount(amount) {} }; } // namespace model } // namespace iroha diff --git a/irohad/model/converters/impl/json_command_factory.cpp b/irohad/model/converters/impl/json_command_factory.cpp index fa7d02136f..7b41f81bbc 100644 --- a/irohad/model/converters/impl/json_command_factory.cpp +++ b/irohad/model/converters/impl/json_command_factory.cpp @@ -64,7 +64,8 @@ namespace iroha { rapidjson::Document dd; precision = des.document["precision"].GetUint(); - return boost::make_optional(Amount(value, static_cast(precision))); + return boost::make_optional( + Amount(value, static_cast(precision))); } }; @@ -80,9 +81,10 @@ namespace iroha { return boost::none; } - return boost::make_optional(Peer(address.value(), - iroha::hexstringToArray(pubkey.value()) - .value())); + return boost::make_optional(Peer( + address.value(), + iroha::hexstringToArray(pubkey.value()) + .value())); } }; @@ -149,8 +151,6 @@ namespace iroha { document.SetObject(); document.AddMember("command_type", "AddAssetQuantity", allocator); - document.AddMember( - "account_id", add_asset_quantity->account_id, allocator); document.AddMember("asset_id", add_asset_quantity->asset_id, allocator); Value amount; @@ -169,7 +169,6 @@ namespace iroha { const Value &document) { auto des = makeFieldDeserializer(document); return make_optional_ptr() - | des.String(&AddAssetQuantity::account_id, "account_id") | des.String(&AddAssetQuantity::asset_id, "asset_id") | des.Object(&AddAssetQuantity::amount, "amount") | toCommand; } @@ -563,8 +562,6 @@ namespace iroha { document.SetObject(); document.AddMember("command_type", "SubtractAssetQuantity", allocator); - document.AddMember( - "account_id", subtract_asset_quantity->account_id, allocator); document.AddMember( "asset_id", subtract_asset_quantity->asset_id, allocator); @@ -587,7 +584,6 @@ namespace iroha { const Value &document) { auto des = makeFieldDeserializer(document); return make_optional_ptr() - | des.String(&SubtractAssetQuantity::account_id, "account_id") | des.String(&SubtractAssetQuantity::asset_id, "asset_id") | des.Object(&SubtractAssetQuantity::amount, "amount") | toCommand; } diff --git a/irohad/model/converters/impl/pb_command_factory.cpp b/irohad/model/converters/impl/pb_command_factory.cpp index c70b2b9247..2b74db1e30 100644 --- a/irohad/model/converters/impl/pb_command_factory.cpp +++ b/irohad/model/converters/impl/pb_command_factory.cpp @@ -159,7 +159,6 @@ namespace iroha { protocol::AddAssetQuantity PbCommandFactory::serializeAddAssetQuantity( const model::AddAssetQuantity &add_asset_quantity) { protocol::AddAssetQuantity pb_add_asset_quantity; - pb_add_asset_quantity.set_account_id(add_asset_quantity.account_id); pb_add_asset_quantity.set_asset_id(add_asset_quantity.asset_id); auto amount = pb_add_asset_quantity.mutable_amount(); amount->CopyFrom(serializeAmount(add_asset_quantity.amount)); @@ -169,7 +168,6 @@ namespace iroha { model::AddAssetQuantity PbCommandFactory::deserializeAddAssetQuantity( const protocol::AddAssetQuantity &pb_add_asset_quantity) { model::AddAssetQuantity add_asset_quantity; - add_asset_quantity.account_id = pb_add_asset_quantity.account_id(); add_asset_quantity.asset_id = pb_add_asset_quantity.asset_id(); add_asset_quantity.amount = deserializeAmount(pb_add_asset_quantity.amount()); @@ -182,8 +180,6 @@ namespace iroha { PbCommandFactory::serializeSubtractAssetQuantity( const model::SubtractAssetQuantity &subtract_asset_quantity) { protocol::SubtractAssetQuantity pb_subtract_asset_quantity; - pb_subtract_asset_quantity.set_account_id( - subtract_asset_quantity.account_id); pb_subtract_asset_quantity.set_asset_id( subtract_asset_quantity.asset_id); auto amount = pb_subtract_asset_quantity.mutable_amount(); @@ -195,8 +191,6 @@ namespace iroha { PbCommandFactory::deserializeSubtractAssetQuantity( const protocol::SubtractAssetQuantity &pb_subtract_asset_quantity) { model::SubtractAssetQuantity subtract_asset_quantity; - subtract_asset_quantity.account_id = - pb_subtract_asset_quantity.account_id(); subtract_asset_quantity.asset_id = pb_subtract_asset_quantity.asset_id(); subtract_asset_quantity.amount = diff --git a/irohad/model/generators/command_generator.hpp b/irohad/model/generators/command_generator.hpp index ba65d4f75a..c00b0935cb 100644 --- a/irohad/model/generators/command_generator.hpp +++ b/irohad/model/generators/command_generator.hpp @@ -73,14 +73,10 @@ namespace iroha { const std::string &account_id, uint32_t quorum); std::shared_ptr generateAddAssetQuantity( - const std::string &account_id, - const std::string &asset_id, - const Amount &amount); + const std::string &asset_id, const Amount &amount); std::shared_ptr generateSubtractAssetQuantity( - const std::string &account_id, - const std::string &asset_id, - const Amount &amount); + const std::string &asset_id, const Amount &amount); /** * Generate transfer assets from source account_id to target account_id * @param src_account_id - source account identifier diff --git a/irohad/model/generators/impl/command_generator.cpp b/irohad/model/generators/impl/command_generator.cpp index 6c1fcd4220..70a7220fc6 100644 --- a/irohad/model/generators/impl/command_generator.cpp +++ b/irohad/model/generators/impl/command_generator.cpp @@ -101,18 +101,13 @@ namespace iroha { } std::shared_ptr CommandGenerator::generateAddAssetQuantity( - const std::string &account_id, - const std::string &asset_id, - const Amount &amount) { - return generateCommand(account_id, asset_id, amount); + const std::string &asset_id, const Amount &amount) { + return generateCommand(asset_id, amount); } std::shared_ptr CommandGenerator::generateSubtractAssetQuantity( - const std::string &account_id, - const std::string &asset_id, - const Amount &amount) { - return generateCommand( - account_id, asset_id, amount); + const std::string &asset_id, const Amount &amount) { + return generateCommand(asset_id, amount); } std::shared_ptr CommandGenerator::generateSetQuorum( diff --git a/irohad/model/impl/model_operators.cpp b/irohad/model/impl/model_operators.cpp index fe6dd7dae5..58934aca69 100644 --- a/irohad/model/impl/model_operators.cpp +++ b/irohad/model/impl/model_operators.cpp @@ -97,8 +97,7 @@ namespace iroha { if (! instanceof (command)) return false; auto add_asset_quantity = static_cast(command); - return add_asset_quantity.account_id == account_id - && add_asset_quantity.asset_id == asset_id + return add_asset_quantity.asset_id == asset_id && add_asset_quantity.amount == amount; } @@ -108,8 +107,7 @@ namespace iroha { return false; auto subtract_asset_quantity = static_cast(command); - return subtract_asset_quantity.account_id == account_id - && subtract_asset_quantity.asset_id == asset_id + return subtract_asset_quantity.asset_id == asset_id && subtract_asset_quantity.amount == amount; } diff --git a/schema/commands.proto b/schema/commands.proto index d6c4243ce4..ec854a548c 100644 --- a/schema/commands.proto +++ b/schema/commands.proto @@ -3,9 +3,8 @@ package iroha.protocol; import "primitive.proto"; message AddAssetQuantity { - string account_id = 1; - string asset_id = 2; - Amount amount = 3; + string asset_id = 1; + Amount amount = 2; } message AddPeer { @@ -84,9 +83,8 @@ message RevokePermission { } message SubtractAssetQuantity { - string account_id = 1; - string asset_id = 2; - Amount amount = 3; + string asset_id = 1; + Amount amount = 2; } message Command { diff --git a/shared_model/backend/protobuf/commands/impl/proto_add_asset_quantity.cpp b/shared_model/backend/protobuf/commands/impl/proto_add_asset_quantity.cpp index eef87be27b..f8fa221292 100644 --- a/shared_model/backend/protobuf/commands/impl/proto_add_asset_quantity.cpp +++ b/shared_model/backend/protobuf/commands/impl/proto_add_asset_quantity.cpp @@ -29,10 +29,6 @@ namespace shared_model { AddAssetQuantity::AddAssetQuantity(AddAssetQuantity &&o) noexcept : AddAssetQuantity(std::move(o.proto_)) {} - const interface::types::AccountIdType &AddAssetQuantity::accountId() const { - return add_asset_quantity_.account_id(); - } - const interface::types::AssetIdType &AddAssetQuantity::assetId() const { return add_asset_quantity_.asset_id(); } diff --git a/shared_model/backend/protobuf/commands/impl/proto_subtract_asset_quantity.cpp b/shared_model/backend/protobuf/commands/impl/proto_subtract_asset_quantity.cpp index 032e33e2a6..23906c79b6 100644 --- a/shared_model/backend/protobuf/commands/impl/proto_subtract_asset_quantity.cpp +++ b/shared_model/backend/protobuf/commands/impl/proto_subtract_asset_quantity.cpp @@ -30,11 +30,6 @@ namespace shared_model { SubtractAssetQuantity &&o) noexcept : SubtractAssetQuantity(std::move(o.proto_)) {} - const interface::types::AccountIdType &SubtractAssetQuantity::accountId() - const { - return subtract_asset_quantity_.account_id(); - } - const interface::types::AssetIdType &SubtractAssetQuantity::assetId() const { return subtract_asset_quantity_.asset_id(); diff --git a/shared_model/backend/protobuf/commands/proto_add_asset_quantity.hpp b/shared_model/backend/protobuf/commands/proto_add_asset_quantity.hpp index e7ba66d1c6..88b4733388 100644 --- a/shared_model/backend/protobuf/commands/proto_add_asset_quantity.hpp +++ b/shared_model/backend/protobuf/commands/proto_add_asset_quantity.hpp @@ -39,8 +39,6 @@ namespace shared_model { AddAssetQuantity(AddAssetQuantity &&o) noexcept; - const interface::types::AccountIdType &accountId() const override; - const interface::types::AssetIdType &assetId() const override; const interface::Amount &amount() const override; diff --git a/shared_model/backend/protobuf/commands/proto_subtract_asset_quantity.hpp b/shared_model/backend/protobuf/commands/proto_subtract_asset_quantity.hpp index a69d2fd760..eee21947b0 100644 --- a/shared_model/backend/protobuf/commands/proto_subtract_asset_quantity.hpp +++ b/shared_model/backend/protobuf/commands/proto_subtract_asset_quantity.hpp @@ -39,8 +39,6 @@ namespace shared_model { SubtractAssetQuantity(SubtractAssetQuantity &&o) noexcept; - const interface::types::AccountIdType &accountId() const override; - const interface::types::AssetIdType &assetId() const override; const interface::Amount &amount() const override; diff --git a/shared_model/bindings/model_transaction_builder.cpp b/shared_model/bindings/model_transaction_builder.cpp index 3886471563..5211fba63e 100644 --- a/shared_model/bindings/model_transaction_builder.cpp +++ b/shared_model/bindings/model_transaction_builder.cpp @@ -39,11 +39,10 @@ namespace shared_model { } ModelTransactionBuilder ModelTransactionBuilder::addAssetQuantity( - const interface::types::AccountIdType &account_id, const interface::types::AssetIdType &asset_id, const std::string &amount) { return ModelTransactionBuilder( - builder_.addAssetQuantity(account_id, asset_id, amount)); + builder_.addAssetQuantity(asset_id, amount)); } ModelTransactionBuilder ModelTransactionBuilder::addPeer( @@ -140,11 +139,10 @@ namespace shared_model { } ModelTransactionBuilder ModelTransactionBuilder::subtractAssetQuantity( - const interface::types::AccountIdType &account_id, const interface::types::AssetIdType &asset_id, const std::string &amount) { return ModelTransactionBuilder( - builder_.subtractAssetQuantity(account_id, asset_id, amount)); + builder_.subtractAssetQuantity(asset_id, amount)); } ModelTransactionBuilder ModelTransactionBuilder::transferAsset( diff --git a/shared_model/bindings/model_transaction_builder.hpp b/shared_model/bindings/model_transaction_builder.hpp index 478076892a..cdc6a6f209 100644 --- a/shared_model/bindings/model_transaction_builder.hpp +++ b/shared_model/bindings/model_transaction_builder.hpp @@ -63,13 +63,11 @@ namespace shared_model { /** * Adds given quantity of given asset to account - * @param account_id - account id * @param asset_id - asset id * @param amount - amount of asset to add * @return builder with asset quantity command appended */ ModelTransactionBuilder addAssetQuantity( - const interface::types::AccountIdType &account_id, const interface::types::AssetIdType &asset_id, const std::string &amount); @@ -211,13 +209,11 @@ namespace shared_model { /** * Subtracts asset quantity - * @param account_id - account id to subtract asset quantity from * @param asset_id - asset id to subtract * @param amount - amount to subtract * @return builder with subtract asset quantity command appended */ ModelTransactionBuilder subtractAssetQuantity( - const interface::types::AccountIdType &account_id, const interface::types::AssetIdType &asset_id, const std::string &amount); diff --git a/shared_model/builders/protobuf/builder_templates/transaction_template.hpp b/shared_model/builders/protobuf/builder_templates/transaction_template.hpp index 2ebb85832f..ea5a4a182b 100644 --- a/shared_model/builders/protobuf/builder_templates/transaction_template.hpp +++ b/shared_model/builders/protobuf/builder_templates/transaction_template.hpp @@ -120,12 +120,10 @@ namespace shared_model { [&](auto &tx) { tx.mutable_payload()->set_quorum(quorum); }); } - auto addAssetQuantity(const interface::types::AccountIdType &account_id, - const interface::types::AssetIdType &asset_id, + auto addAssetQuantity(const interface::types::AssetIdType &asset_id, const std::string &amount) const { return addCommand([&](auto proto_command) { auto command = proto_command->mutable_add_asset_quantity(); - command->set_account_id(account_id); command->set_asset_id(asset_id); initializeProtobufAmount(command->mutable_amount(), amount); }); @@ -265,13 +263,10 @@ namespace shared_model { }); } - auto subtractAssetQuantity( - const interface::types::AccountIdType &account_id, - const interface::types::AssetIdType &asset_id, - const std::string &amount) const { + auto subtractAssetQuantity(const interface::types::AssetIdType &asset_id, + const std::string &amount) const { return addCommand([&](auto proto_command) { auto command = proto_command->mutable_subtract_asset_quantity(); - command->set_account_id(account_id); command->set_asset_id(asset_id); initializeProtobufAmount(command->mutable_amount(), amount); }); diff --git a/shared_model/interfaces/commands/add_asset_quantity.hpp b/shared_model/interfaces/commands/add_asset_quantity.hpp index f704f9e66b..24e768e8a3 100644 --- a/shared_model/interfaces/commands/add_asset_quantity.hpp +++ b/shared_model/interfaces/commands/add_asset_quantity.hpp @@ -30,10 +30,6 @@ namespace shared_model { */ class AddAssetQuantity : public ModelPrimitive { public: - /** - * @return Identity of user, that add quantity - */ - virtual const types::AccountIdType &accountId() const = 0; /** * @return asset identifier */ diff --git a/shared_model/interfaces/commands/impl/add_asset_quantity.cpp b/shared_model/interfaces/commands/impl/add_asset_quantity.cpp index f6d5c13833..f26dcdb337 100644 --- a/shared_model/interfaces/commands/impl/add_asset_quantity.cpp +++ b/shared_model/interfaces/commands/impl/add_asset_quantity.cpp @@ -11,15 +11,13 @@ namespace shared_model { std::string AddAssetQuantity::toString() const { return detail::PrettyStringBuilder() .init("AddAssetQuantity") - .append("account_id", accountId()) .append("asset_id", assetId()) .append("amount", amount().toString()) .finalize(); } bool AddAssetQuantity::operator==(const ModelType &rhs) const { - return accountId() == rhs.accountId() and assetId() == rhs.assetId() - and amount() == rhs.amount(); + return assetId() == rhs.assetId() and amount() == rhs.amount(); } } // namespace interface diff --git a/shared_model/interfaces/commands/impl/subtract_asset_quantity.cpp b/shared_model/interfaces/commands/impl/subtract_asset_quantity.cpp index ead13191d2..9928f4dc59 100644 --- a/shared_model/interfaces/commands/impl/subtract_asset_quantity.cpp +++ b/shared_model/interfaces/commands/impl/subtract_asset_quantity.cpp @@ -11,15 +11,13 @@ namespace shared_model { std::string SubtractAssetQuantity::toString() const { return detail::PrettyStringBuilder() .init("SubtractAssetQuantity") - .append("account_id", accountId()) .append("asset_id", assetId()) .append("amount", amount().toString()) .finalize(); } bool SubtractAssetQuantity::operator==(const ModelType &rhs) const { - return accountId() == rhs.accountId() and assetId() == rhs.assetId() - and amount() == rhs.amount(); + return assetId() == rhs.assetId() and amount() == rhs.amount(); } } // namespace interface diff --git a/shared_model/interfaces/commands/subtract_asset_quantity.hpp b/shared_model/interfaces/commands/subtract_asset_quantity.hpp index da43458fd3..9e181c5440 100644 --- a/shared_model/interfaces/commands/subtract_asset_quantity.hpp +++ b/shared_model/interfaces/commands/subtract_asset_quantity.hpp @@ -30,10 +30,6 @@ namespace shared_model { */ class SubtractAssetQuantity : public ModelPrimitive { public: - /** - * @return Identity of user to subtract quantity from - */ - virtual const types::AccountIdType &accountId() const = 0; /** * @return asset identifier */ diff --git a/shared_model/packages/javascript/tests/txbuilder.js b/shared_model/packages/javascript/tests/txbuilder.js index 9487e81804..5642f00d53 100644 --- a/shared_model/packages/javascript/tests/txbuilder.js +++ b/shared_model/packages/javascript/tests/txbuilder.js @@ -9,7 +9,7 @@ const assetId = 'coin#test' const testAccountId = 'test@test' test('ModelTransactionBuilder tests', function (t) { - t.plan(133) + t.plan(127) let crypto = new iroha.ModelCrypto() let keypair = crypto.convertFromExisting(publicKey, privateKey) @@ -38,14 +38,11 @@ test('ModelTransactionBuilder tests', function (t) { t.comment('Testing addAssetQuantity()') t.throws(() => correctTx.addAssetQuantity(), /Error: Illegal number of arguments/, 'Should throw Illegal number of arguments') t.throws(() => correctTx.addAssetQuantity(''), /Error: Illegal number of arguments/, 'Should throw Illegal number of arguments') - t.throws(() => correctTx.addAssetQuantity('', ''), /Error: Illegal number of arguments/, 'Should throw Illegal number of arguments') - t.throws(() => correctTx.addAssetQuantity('', '', '').build(), /AddAssetQuantity: \[\[Wrongly formed account_id, passed value: ''(.*)Wrongly formed asset_id, passed value: ''(.*)Amount must be greater than 0, passed value: 0 \]\]/, 'Should throw wrongly formed account_id, asset_id, Amount must be greater than 0') - t.throws(() => correctTx.addAssetQuantity(adminAccountId, assetId, '0').build(), /AddAssetQuantity: \[\[Amount must be greater than 0, passed value: 0 \]\]/, 'Should throw Amount must be greater than 0') - t.throws(() => correctTx.addAssetQuantity('', assetId, '1000').build(), /Wrongly formed account_id, passed value: ''/, 'Should throw Wrongly formed account_id') - t.throws(() => correctTx.addAssetQuantity('@@@', assetId, '1000').build(), /Wrongly formed account_id, passed value: '@@@'/, 'Should throw Wrongly formed account_id') - t.throws(() => correctTx.addAssetQuantity(adminAccountId, '', '1000').build(), /Wrongly formed asset_id, passed value: ''/, 'Should throw Wrongly formed asset_id') - t.throws(() => correctTx.addAssetQuantity(adminAccountId, '###', '1000').build(), /Wrongly formed asset_id, passed value: '###'/, 'Should throw Wrongly formed asset_id') - t.doesNotThrow(() => correctTx.addAssetQuantity(adminAccountId, assetId, '1000').build(), null, 'Should not throw any exceptions') + t.throws(() => correctTx.addAssetQuantity('', '').build(), /AddAssetQuantity: \[\[Wrongly formed asset_id, passed value: ''(.*)Amount must be greater than 0, passed value: 0 \]\]/, 'Should throw wrongly formed account_id, asset_id, Amount must be greater than 0') + t.throws(() => correctTx.addAssetQuantity(assetId, '0').build(), /AddAssetQuantity: \[\[Amount must be greater than 0, passed value: 0 \]\]/, 'Should throw Amount must be greater than 0') + t.throws(() => correctTx.addAssetQuantity('', '1000').build(), /Wrongly formed asset_id, passed value: ''/, 'Should throw Wrongly formed asset_id') + t.throws(() => correctTx.addAssetQuantity('###', '1000').build(), /Wrongly formed asset_id, passed value: '###'/, 'Should throw Wrongly formed asset_id') + t.doesNotThrow(() => correctTx.addAssetQuantity(assetId, '1000').build(), null, 'Should not throw any exceptions') // addPeer() tests t.comment('Testing addPeer()') @@ -188,16 +185,13 @@ test('ModelTransactionBuilder tests', function (t) { t.comment('Testing subtractAssetQuantity()') t.throws(() => correctTx.subtractAssetQuantity(), /Error: Illegal number of arguments/, 'Should throw Illegal number of arguments') t.throws(() => correctTx.subtractAssetQuantity(''), /Error: Illegal number of arguments/, 'Should throw Illegal number of arguments') - t.throws(() => correctTx.subtractAssetQuantity('', ''), /Error: Illegal number of arguments/, 'Should throw Illegal number of arguments') - t.throws(() => correctTx.subtractAssetQuantity('', '', '').build(), /SubtractAssetQuantity: \[\[Wrongly formed account_id, passed value: ''(.*)Wrongly formed asset_id, passed value: ''(.*)Amount must be greater than 0, passed value: 0 \]\]/, 'Should throw wrongly formed account_id, asset_id, Amount must be greater than 0') - t.throws(() => correctTx.subtractAssetQuantity(adminAccountId, assetId, '0').build(), /SubtractAssetQuantity: \[\[Amount must be greater than 0, passed value: 0 \]\]/, 'Should throw Amount must be greater than 0') + t.throws(() => correctTx.subtractAssetQuantity('', '').build(), /SubtractAssetQuantity: \[\[Wrongly formed asset_id, passed value: ''(.*)Amount must be greater than 0, passed value: 0 \]\]/, 'Should throw wrongly formed account_id, asset_id, Amount must be greater than 0') + t.throws(() => correctTx.subtractAssetQuantity(assetId, '0').build(), /SubtractAssetQuantity: \[\[Amount must be greater than 0, passed value: 0 \]\]/, 'Should throw Amount must be greater than 0') // TODO: MAYBE Throw an exception on real amount - // t.throws(() => correctTx.subtractAssetQuantity(adminAccountId, assetId, '0.123').build(), /SubtractAssetQuantity: \[\[Amount must be integer, passed value: 0.123 \]\]/, 'Should throw Amount must be integer') - t.throws(() => correctTx.subtractAssetQuantity('', assetId, '1000').build(), /Wrongly formed account_id, passed value: ''/, 'Should throw Wrongly formed account_id') - t.throws(() => correctTx.subtractAssetQuantity('@@@', assetId, '1000').build(), /Wrongly formed account_id, passed value: '@@@'/, 'Should throw Wrongly formed account_id') - t.throws(() => correctTx.subtractAssetQuantity(adminAccountId, '', '1000').build(), /Wrongly formed asset_id, passed value: ''/, 'Should throw Wrongly formed asset_id') - t.throws(() => correctTx.subtractAssetQuantity(adminAccountId, '###', '1000').build(), /Wrongly formed asset_id, passed value: '###'/, 'Should throw Wrongly formed asset_id') - t.doesNotThrow(() => correctTx.subtractAssetQuantity(adminAccountId, assetId, '1000').build(), null, 'Should not throw any exceptions') + // t.throws(() => correctTx.subtractAssetQuantity(assetId, '0.123').build(), /SubtractAssetQuantity: \[\[Amount must be integer, passed value: 0.123 \]\]/, 'Should throw Amount must be integer') + t.throws(() => correctTx.subtractAssetQuantity('', '1000').build(), /Wrongly formed asset_id, passed value: ''/, 'Should throw Wrongly formed asset_id') + t.throws(() => correctTx.subtractAssetQuantity('###', '1000').build(), /Wrongly formed asset_id, passed value: '###'/, 'Should throw Wrongly formed asset_id') + t.doesNotThrow(() => correctTx.subtractAssetQuantity(assetId, '1000').build(), null, 'Should not throw any exceptions') // transferAsset() tests t.comment('Testing transferAsset()') diff --git a/shared_model/validators/transaction_validator.hpp b/shared_model/validators/transaction_validator.hpp index c364d383cd..6e1dcb07d2 100644 --- a/shared_model/validators/transaction_validator.hpp +++ b/shared_model/validators/transaction_validator.hpp @@ -45,7 +45,6 @@ namespace shared_model { ReasonsGroupType reason; addInvalidCommand(reason, "AddAssetQuantity"); - validator_.validateAccountId(reason, aaq.accountId()); validator_.validateAssetId(reason, aaq.assetId()); validator_.validateAmount(reason, aaq.amount()); @@ -196,7 +195,6 @@ namespace shared_model { ReasonsGroupType reason; addInvalidCommand(reason, "SubtractAssetQuantity"); - validator_.validateAccountId(reason, saq.accountId()); validator_.validateAssetId(reason, saq.assetId()); validator_.validateAmount(reason, saq.amount()); diff --git a/test/integration/acceptance/add_asset_qty_test.cpp b/test/integration/acceptance/add_asset_qty_test.cpp index 066fcd8260..d9ed77adbb 100644 --- a/test/integration/acceptance/add_asset_qty_test.cpp +++ b/test/integration/acceptance/add_asset_qty_test.cpp @@ -31,7 +31,7 @@ TEST_F(AddAssetQuantity, Basic) { .sendTx(makeUserWithPerms()) .skipProposal() .skipBlock() - .sendTx(complete(baseTx().addAssetQuantity(kUserId, kAsset, kAmount))) + .sendTx(complete(baseTx().addAssetQuantity(kAsset, kAmount))) .skipProposal() .checkBlock( [](auto &block) { ASSERT_EQ(block->transactions().size(), 1); }) @@ -49,7 +49,7 @@ TEST_F(AddAssetQuantity, NoPermissions) { .sendTx(makeUserWithPerms({interface::permissions::Role::kGetMyTxs})) .skipProposal() .skipBlock() - .sendTx(complete(baseTx().addAssetQuantity(kUserId, kAsset, kAmount))) + .sendTx(complete(baseTx().addAssetQuantity(kAsset, kAmount))) .skipProposal() .checkBlock( [](auto &block) { ASSERT_EQ(block->transactions().size(), 0); }) @@ -68,7 +68,7 @@ TEST_F(AddAssetQuantity, NegativeAmount) { .sendTx(makeUserWithPerms()) .skipProposal() .skipBlock() - .sendTx(complete(baseTx().addAssetQuantity(kUserId, kAsset, "-1.0")), + .sendTx(complete(baseTx().addAssetQuantity(kAsset, "-1.0")), checkStatelessInvalid); } @@ -84,7 +84,7 @@ TEST_F(AddAssetQuantity, ZeroAmount) { .sendTx(makeUserWithPerms()) .skipProposal() .skipBlock() - .sendTx(complete(baseTx().addAssetQuantity(kUserId, kAsset, "0.0")), + .sendTx(complete(baseTx().addAssetQuantity(kAsset, "0.0")), checkStatelessInvalid); } @@ -105,33 +105,12 @@ TEST_F(AddAssetQuantity, Uint256DestOverflow) { .skipProposal() .skipBlock() // Add first half of the maximum - .sendTx( - complete(baseTx().addAssetQuantity(kUserId, kAsset, uint256_halfmax))) + .sendTx(complete(baseTx().addAssetQuantity(kAsset, uint256_halfmax))) .skipProposal() .checkBlock( [](auto &block) { ASSERT_EQ(block->transactions().size(), 1); }) // Add second half of the maximum - .sendTx( - complete(baseTx().addAssetQuantity(kUserId, kAsset, uint256_halfmax))) - .skipProposal() - .checkBlock( - [](auto &block) { ASSERT_EQ(block->transactions().size(), 0); }) - .done(); -} - -/** - * @given some user with all required permissions - * @when execute tx with AddAssetQuantity command with nonexistent account - * @then there is an empty proposal - */ -TEST_F(AddAssetQuantity, NonexistentAccount) { - std::string nonexistent = "inexist@test"; - IntegrationTestFramework(1) - .setInitialState(kAdminKeypair) - .sendTx(makeUserWithPerms()) - .skipProposal() - .skipBlock() - .sendTx(complete(baseTx().addAssetQuantity(nonexistent, kAsset, kAmount))) + .sendTx(complete(baseTx().addAssetQuantity(kAsset, uint256_halfmax))) .skipProposal() .checkBlock( [](auto &block) { ASSERT_EQ(block->transactions().size(), 0); }) @@ -150,69 +129,9 @@ TEST_F(AddAssetQuantity, NonexistentAsset) { .sendTx(makeUserWithPerms()) .skipProposal() .skipBlock() - .sendTx( - complete(baseTx().addAssetQuantity(kUserId, nonexistent, kAmount))) - .skipProposal() - .checkBlock( - [](auto &block) { ASSERT_EQ(block->transactions().size(), 0); }) - .done(); -} - -/** - * @given some user with all required permission - * @when execute tx with AddAssetQuantity command to some other user - * @then there is no tx in proposal - */ -TEST_F(AddAssetQuantity, OtherUser) { - IntegrationTestFramework(1) - .setInitialState(kAdminKeypair) - .sendTx(makeUserWithPerms()) - .skipProposal() - .skipBlock() - .sendTx(complete(baseTx().addAssetQuantity( - IntegrationTestFramework::kAdminId, kAsset, kAmount))) + .sendTx(complete(baseTx().addAssetQuantity(nonexistent, kAmount))) .skipProposal() .checkBlock( [](auto &block) { ASSERT_EQ(block->transactions().size(), 0); }) .done(); } - -/** - * @given pair of user in different domains with all required permission - * @when first one execute a tx with AddAssetQuantity command to the second one - * @then the tx hasn't passed stateless validation - * (aka skipProposal throws) - */ -TEST_F(AddAssetQuantity, OtherDomain) { - const auto kNewRole = "newrl"; - const auto kNewDomain = "newdom"; - const auto kNewUser = "newusr"; - IntegrationTestFramework(2) - .setInitialState(kAdminKeypair) - .sendTx(makeUserWithPerms()) - // Generate new domain, new user and an asset - .sendTx( - TestUnsignedTransactionBuilder() - .creatorAccountId( - integration_framework::IntegrationTestFramework::kAdminId) - .createdTime(getUniqueTime()) - .createRole(kNewRole, - {interface::permissions::Role::kGetMyTxs}) - .createDomain(kNewDomain, kNewRole) - .createAccount( - kNewUser, - kNewDomain, - crypto::DefaultCryptoAlgorithmType::generateKeypair() - .publicKey()) - .createAsset(IntegrationTestFramework::kAssetName, kNewDomain, 1) - .quorum(1) - .build() - .signAndAddSignature(kAdminKeypair) - .finish()) - .skipProposal() - // Make sure everything is committed - .checkBlock( - [](auto &block) { ASSERT_EQ(block->transactions().size(), 2); }) - .sendTx(complete(baseTx().addAssetQuantity(kNewUser, kAsset, kAmount)), - checkStatelessInvalid); -} diff --git a/test/integration/acceptance/get_account_assets_test.cpp b/test/integration/acceptance/get_account_assets_test.cpp index ab65cb4c4b..ce835e7911 100644 --- a/test/integration/acceptance/get_account_assets_test.cpp +++ b/test/integration/acceptance/get_account_assets_test.cpp @@ -31,14 +31,13 @@ class GetAccountAssets : public AcceptanceFixture { /// Create command for adding assets auto addAssets() { - return complete( - AcceptanceFixture::baseTx().addAssetQuantity(kUserId, kAsset, "1")); + return complete(AcceptanceFixture::baseTx().addAssetQuantity(kAsset, "1")); } /// Create command for removing assets auto removeAssets() { - return complete(AcceptanceFixture::baseTx().subtractAssetQuantity( - kUserId, kAsset, "1")); + return complete( + AcceptanceFixture::baseTx().subtractAssetQuantity(kAsset, "1")); } /** diff --git a/test/integration/acceptance/grant_permission_test.cpp b/test/integration/acceptance/grant_permission_test.cpp index fdbb238c96..6901a78cf7 100644 --- a/test/integration/acceptance/grant_permission_test.cpp +++ b/test/integration/acceptance/grant_permission_test.cpp @@ -103,9 +103,9 @@ class GrantPermissionTest : public AcceptanceFixture { permitee_account_name + "@" + kDomain; const std::string account_id = account_name + "@" + kDomain; return (TxBuilder() - .creatorAccountId(permitee_account_id) - .createdTime(getUniqueTime()) - .*f)(account_id, permitee_key.publicKey()) + .creatorAccountId(permitee_account_id) + .createdTime(getUniqueTime()) + .*f)(account_id, permitee_key.publicKey()) .quorum(1) .build() .signAndAddSignature(permitee_key) @@ -185,7 +185,7 @@ class GrantPermissionTest : public AcceptanceFixture { return TxBuilder() .creatorAccountId(creator_account_id) .createdTime(getUniqueTime()) - .addAssetQuantity(creator_account_id, asset_id, amount) + .addAssetQuantity(asset_id, amount) .transferAsset( creator_account_id, receiver_account_id, asset_id, "", amount) .quorum(1) @@ -293,19 +293,19 @@ class GrantPermissionTest : public AcceptanceFixture { int quantity, bool is_contained) { return [&signatory, quantity, is_contained]( - const shared_model::proto::QueryResponse &query_response) { + const shared_model::proto::QueryResponse &query_response) { ASSERT_NO_THROW({ - const auto &resp = boost::apply_visitor( - framework::SpecifiedVisitor(), - query_response.get()); + const auto &resp = boost::apply_visitor( + framework::SpecifiedVisitor(), + query_response.get()); - ASSERT_EQ(resp.keys().size(), quantity); - auto &keys = resp.keys(); + ASSERT_EQ(resp.keys().size(), quantity); + auto &keys = resp.keys(); - ASSERT_EQ((std::find(keys.begin(), keys.end(), signatory.publicKey()) - != keys.end()), - is_contained); - }); + ASSERT_EQ((std::find(keys.begin(), keys.end(), signatory.publicKey()) + != keys.end()), + is_contained); + }); }; } @@ -317,14 +317,14 @@ class GrantPermissionTest : public AcceptanceFixture { */ static auto checkQuorum(int quorum_quantity) { return [quorum_quantity]( - const shared_model::proto::QueryResponse &query_response) { + const shared_model::proto::QueryResponse &query_response) { ASSERT_NO_THROW({ - const auto &resp = boost::apply_visitor( - framework::SpecifiedVisitor(), - query_response.get()); + const auto &resp = boost::apply_visitor( + framework::SpecifiedVisitor(), + query_response.get()); - ASSERT_EQ(resp.account().quorum(), quorum_quantity); - }); + ASSERT_EQ(resp.account().quorum(), quorum_quantity); + }); }; } @@ -337,14 +337,14 @@ class GrantPermissionTest : public AcceptanceFixture { static auto checkAccountDetail(const std::string &key, const std::string &detail) { return [&key, - &detail](const shared_model::proto::QueryResponse &query_response) { + &detail](const shared_model::proto::QueryResponse &query_response) { ASSERT_NO_THROW({ - const auto &resp = boost::apply_visitor( - framework::SpecifiedVisitor(), - query_response.get()); - ASSERT_TRUE(resp.detail().find(key) != std::string::npos); - ASSERT_TRUE(resp.detail().find(detail) != std::string::npos); - }); + const auto &resp = boost::apply_visitor( + framework::SpecifiedVisitor(), + query_response.get()); + ASSERT_TRUE(resp.detail().find(key) != std::string::npos); + ASSERT_TRUE(resp.detail().find(detail) != std::string::npos); + }); }; } @@ -424,7 +424,7 @@ TEST_F(GrantPermissionTest, GrantAddSignatoryPermission) { .skipProposal() .checkBlock( [](auto &block) { ASSERT_EQ(block->transactions().size(), 1); }) - // Add signatory + // Add signatory .sendTx( permiteeModifySignatory(&TestUnsignedTransactionBuilder::addSignatory, kAccount2, @@ -629,7 +629,7 @@ TEST_F(GrantPermissionTest, GrantWithoutGrantPermissions) { createTwoAccounts(itf, {Role::kReceive}, {Role::kReceive}); for (auto &perm : kAllGrantable) { itf.sendTx( - accountGrantToAccount(kAccount1, kAccount1Keypair, kAccount2, perm)) + accountGrantToAccount(kAccount1, kAccount1Keypair, kAccount2, perm)) .skipProposal() .checkBlock( [](auto &block) { ASSERT_EQ(block->transactions().size(), 0); }); diff --git a/test/integration/acceptance/subtract_asset_qty_test.cpp b/test/integration/acceptance/subtract_asset_qty_test.cpp index 45ffbf44e3..70df2d3648 100644 --- a/test/integration/acceptance/subtract_asset_qty_test.cpp +++ b/test/integration/acceptance/subtract_asset_qty_test.cpp @@ -30,7 +30,7 @@ class SubtractAssetQuantity : public AcceptanceFixture { * @return built tx that adds kAmount assets to the users */ auto replenish() { - return complete(baseTx().addAssetQuantity(kUserId, kAsset, kAmount)); + return complete(baseTx().addAssetQuantity(kAsset, kAmount)); } const std::string kAmount = "1.0"; @@ -50,8 +50,7 @@ TEST_F(SubtractAssetQuantity, Everything) { .sendTx(replenish()) .skipProposal() .skipBlock() - .sendTx( - complete(baseTx().subtractAssetQuantity(kUserId, kAsset, kAmount))) + .sendTx(complete(baseTx().subtractAssetQuantity(kAsset, kAmount))) .skipProposal() .checkBlock( [](auto &block) { ASSERT_EQ(block->transactions().size(), 1); }) @@ -73,7 +72,7 @@ TEST_F(SubtractAssetQuantity, Overdraft) { .sendTx(replenish()) .skipProposal() .skipBlock() - .sendTx(complete(baseTx().subtractAssetQuantity(kUserId, kAsset, "2.0"))) + .sendTx(complete(baseTx().subtractAssetQuantity(kAsset, "2.0"))) .skipProposal() .checkBlock( [](auto &block) { ASSERT_EQ(block->transactions().size(), 0); }) @@ -94,8 +93,7 @@ TEST_F(SubtractAssetQuantity, NoPermissions) { .sendTx(replenish()) .skipProposal() .skipBlock() - .sendTx( - complete(baseTx().subtractAssetQuantity(kUserId, kAsset, kAmount))) + .sendTx(complete(baseTx().subtractAssetQuantity(kAsset, kAmount))) .skipProposal() .checkBlock( [](auto &block) { ASSERT_EQ(block->transactions().size(), 0); }) @@ -117,7 +115,7 @@ TEST_F(SubtractAssetQuantity, NegativeAmount) { .sendTx(replenish()) .skipProposal() .skipBlock() - .sendTx(complete(baseTx().subtractAssetQuantity(kUserId, kAsset, "-1.0")), + .sendTx(complete(baseTx().subtractAssetQuantity(kAsset, "-1.0")), checkStatelessInvalid); } @@ -136,33 +134,10 @@ TEST_F(SubtractAssetQuantity, ZeroAmount) { .sendTx(replenish()) .skipProposal() .skipBlock() - .sendTx(complete(baseTx().subtractAssetQuantity(kUserId, kAsset, "0.0")), + .sendTx(complete(baseTx().subtractAssetQuantity(kAsset, "0.0")), checkStatelessInvalid); } -/** - * @given some user with all required permissions - * @when execute tx with SubtractAssetQuantity command with nonexitent account - * @then there is an empty proposal - */ -TEST_F(SubtractAssetQuantity, NonexistentAccount) { - std::string nonexistent = "inexist@test"; - IntegrationTestFramework(1) - .setInitialState(kAdminKeypair) - .sendTx(makeUserWithPerms()) - .skipProposal() - .skipBlock() - .sendTx(replenish()) - .skipProposal() - .skipBlock() - .sendTx(complete( - baseTx().subtractAssetQuantity(nonexistent, kAsset, kAmount))) - .skipProposal() - .checkBlock( - [](auto &block) { ASSERT_EQ(block->transactions().size(), 0); }) - .done(); -} - /** * @given some user with all required permissions * @when execute tx with SubtractAssetQuantity command with nonexistent asset @@ -178,30 +153,7 @@ TEST_F(SubtractAssetQuantity, NonexistentAsset) { .sendTx(replenish()) .skipProposal() .skipBlock() - .sendTx(complete( - baseTx().subtractAssetQuantity(kUserId, nonexistent, kAmount))) - .skipProposal() - .checkBlock( - [](auto &block) { ASSERT_EQ(block->transactions().size(), 0); }) - .done(); -} - -/** - * @given some user with all required permissions - * @when execute tx with SubtractAssetQuantity command to some other user - * @then there is no tx in proposal - */ -TEST_F(SubtractAssetQuantity, OtherUser) { - IntegrationTestFramework(1) - .setInitialState(kAdminKeypair) - .sendTx(makeUserWithPerms()) - .skipProposal() - .skipBlock() - .sendTx(replenish()) - .skipProposal() - .skipBlock() - .sendTx(complete(baseTx().subtractAssetQuantity( - IntegrationTestFramework::kAdminId, kAsset, kAmount))) + .sendTx(complete(baseTx().subtractAssetQuantity(nonexistent, kAmount))) .skipProposal() .checkBlock( [](auto &block) { ASSERT_EQ(block->transactions().size(), 0); }) diff --git a/test/integration/acceptance/transfer_asset_test.cpp b/test/integration/acceptance/transfer_asset_test.cpp index 9f57f8270b..22c03bc7c7 100644 --- a/test/integration/acceptance/transfer_asset_test.cpp +++ b/test/integration/acceptance/transfer_asset_test.cpp @@ -49,7 +49,7 @@ class TransferAsset : public AcceptanceFixture { } proto::Transaction addAssets(const std::string &amount) { - return complete(baseTx().addAssetQuantity(kUserId, kAsset, amount)); + return complete(baseTx().addAssetQuantity(kAsset, amount)); } proto::Transaction makeTransfer(const std::string &amount) { @@ -304,8 +304,7 @@ TEST_F(TransferAsset, InterDomain) { .build() .signAndAddSignature(kAdminKeypair) .finish(); - auto add_assets = - complete(baseTx().addAssetQuantity(kUserId, kNewAssetId, kAmount)); + auto add_assets = complete(baseTx().addAssetQuantity(kNewAssetId, kAmount)); auto make_transfer = complete( baseTx().transferAsset(kUserId, kUser2Id, kNewAssetId, kDesc, kAmount)); @@ -342,8 +341,7 @@ TEST_F(TransferAsset, BigPrecision) { .build() .signAndAddSignature(kAdminKeypair) .finish(); - auto add_assets = - complete(baseTx().addAssetQuantity(kUserId, kNewAssetId, kInitial)); + auto add_assets = complete(baseTx().addAssetQuantity(kNewAssetId, kInitial)); auto make_transfer = complete(baseTx().transferAsset( kUserId, kUser2Id, kNewAssetId, kDesc, kForTransfer)); diff --git a/test/integration/acceptance/tx_acceptance_test.cpp b/test/integration/acceptance/tx_acceptance_test.cpp index 43150d7c39..e2d59758bf 100644 --- a/test/integration/acceptance/tx_acceptance_test.cpp +++ b/test/integration/acceptance/tx_acceptance_test.cpp @@ -10,7 +10,6 @@ class AcceptanceTest : public AcceptanceFixture { public: const std::string kAdmin = "admin@test"; - const std::string kNonUser = "nonuser@test"; const std::function checkStatelessValid = [](auto &status) { @@ -31,6 +30,20 @@ class AcceptanceTest : public AcceptanceFixture { const std::shared_ptr &)> checkStatefulValid = [](auto &block) { ASSERT_EQ(block->transactions().size(), 1); }; + + template + auto baseTx() { + return Builder() + .createdTime(getUniqueTime()) + .creatorAccountId(kAdmin) + .addAssetQuantity(kAsset, "1.0") + .quorum(1); + } + + template + auto complete(T t) { + return t.build().signAndAddSignature(kAdminKeypair).finish(); + } }; /** @@ -40,18 +53,11 @@ class AcceptanceTest : public AcceptanceFixture { * AND STATEFUL_VALIDATION_FAILED on that tx */ TEST_F(AcceptanceTest, NonExistentCreatorAccountId) { - auto tx = TestUnsignedTransactionBuilder() - .createdTime(iroha::time::now()) - .creatorAccountId(kNonUser) - .addAssetQuantity(kAdmin, kAsset, "1.0") - .quorum(1) - .build() - .signAndAddSignature(kAdminKeypair) - .finish(); - + const std::string kNonUser = "nonuser@test"; integration_framework::IntegrationTestFramework(1) .setInitialState(kAdminKeypair) - .sendTx(tx, checkStatelessValid) + .sendTx(complete(baseTx<>().creatorAccountId(kNonUser)), + checkStatelessValid) .checkProposal(checkProposal) .checkBlock(checkStatefulInvalid) .done(); @@ -64,17 +70,11 @@ TEST_F(AcceptanceTest, NonExistentCreatorAccountId) { * AND STATEFUL_VALIDATION_SUCCESS on that tx */ TEST_F(AcceptanceTest, Transaction1HourOld) { - auto tx = TestUnsignedTransactionBuilder() - .createdTime(iroha::time::now(std::chrono::hours(-1))) - .creatorAccountId(kAdmin) - .addAssetQuantity(kAdmin, kAsset, "1.0") - .quorum(1) - .build() - .signAndAddSignature(kAdminKeypair) - .finish(); integration_framework::IntegrationTestFramework(1) .setInitialState(kAdminKeypair) - .sendTx(tx, checkStatelessValid) + .sendTx(complete(baseTx<>().createdTime( + iroha::time::now(std::chrono::hours(-1)))), + checkStatelessValid) .skipProposal() .checkBlock(checkStatefulValid) .done(); @@ -87,18 +87,11 @@ TEST_F(AcceptanceTest, Transaction1HourOld) { * AND STATEFUL_VALIDATION_SUCCESS on that tx */ TEST_F(AcceptanceTest, DISABLED_TransactionLess24HourOld) { - auto tx = TestUnsignedTransactionBuilder() - .createdTime(iroha::time::now(std::chrono::hours(24) - - std::chrono::minutes(1))) - .creatorAccountId(kAdmin) - .addAssetQuantity(kAdmin, kAsset, "1.0") - .quorum(1) - .build() - .signAndAddSignature(kAdminKeypair) - .finish(); integration_framework::IntegrationTestFramework(1) .setInitialState(kAdminKeypair) - .sendTx(tx, checkStatelessValid) + .sendTx(complete(baseTx<>().createdTime(iroha::time::now( + std::chrono::hours(24) - std::chrono::minutes(1)))), + checkStatelessValid) .skipProposal() .checkBlock(checkStatefulValid) .done(); @@ -110,18 +103,11 @@ TEST_F(AcceptanceTest, DISABLED_TransactionLess24HourOld) { * @then receive STATELESS_VALIDATION_FAILED status */ TEST_F(AcceptanceTest, TransactionMore24HourOld) { - auto tx = TestUnsignedTransactionBuilder() - .createdTime(iroha::time::now(std::chrono::hours(24) - + std::chrono::minutes(1))) - .creatorAccountId(kAdmin) - .addAssetQuantity(kAdmin, kAsset, "1.0") - .quorum(1) - .build() - .signAndAddSignature(kAdminKeypair) - .finish(); integration_framework::IntegrationTestFramework(1) .setInitialState(kAdminKeypair) - .sendTx(tx, checkStatelessInvalid) + .sendTx(complete(baseTx<>().createdTime(iroha::time::now( + std::chrono::hours(24) + std::chrono::minutes(1)))), + checkStatelessInvalid) .done(); } @@ -132,19 +118,11 @@ TEST_F(AcceptanceTest, TransactionMore24HourOld) { * AND STATEFUL_VALIDATION_SUCCESS on that tx */ TEST_F(AcceptanceTest, Transaction5MinutesFromFuture) { - auto tx = TestUnsignedTransactionBuilder() - .createdTime(iroha::time::now(std::chrono::minutes(5) - - std::chrono::seconds(10))) - .creatorAccountId(kAdmin) - .addAssetQuantity(kAdmin, kAsset, "1.0") - .quorum(1) - .build() - .signAndAddSignature(kAdminKeypair) - .finish(); - integration_framework::IntegrationTestFramework(1) .setInitialState(kAdminKeypair) - .sendTx(tx, checkStatelessValid) + .sendTx(complete(baseTx<>().createdTime(iroha::time::now( + std::chrono::minutes(5) - std::chrono::seconds(10)))), + checkStatelessValid) .skipProposal() .checkBlock(checkStatefulValid) .done(); @@ -156,17 +134,11 @@ TEST_F(AcceptanceTest, Transaction5MinutesFromFuture) { * @then receive STATELESS_VALIDATION_FAILED status */ TEST_F(AcceptanceTest, Transaction10MinutesFromFuture) { - auto tx = TestUnsignedTransactionBuilder() - .createdTime(iroha::time::now(std::chrono::minutes(10))) - .creatorAccountId(kAdmin) - .addAssetQuantity(kAdmin, kAsset, "1.0") - .quorum(1) - .build() - .signAndAddSignature(kAdminKeypair) - .finish(); integration_framework::IntegrationTestFramework(1) .setInitialState(kAdminKeypair) - .sendTx(tx, checkStatelessInvalid) + .sendTx(complete(baseTx<>().createdTime( + iroha::time::now(std::chrono::minutes(10)))), + checkStatelessInvalid) .done(); } @@ -177,12 +149,7 @@ TEST_F(AcceptanceTest, Transaction10MinutesFromFuture) { */ TEST_F(AcceptanceTest, TransactionEmptyPubKey) { shared_model::proto::Transaction tx = - TestTransactionBuilder() - .createdTime(iroha::time::now()) - .creatorAccountId(kAdmin) - .addAssetQuantity(kAdmin, kAsset, "1.0") - .quorum(1) - .build(); + baseTx().build(); auto signedBlob = shared_model::crypto::CryptoSigner<>::sign( shared_model::crypto::Blob(tx.payload()), kAdminKeypair); @@ -200,12 +167,7 @@ TEST_F(AcceptanceTest, TransactionEmptyPubKey) { */ TEST_F(AcceptanceTest, TransactionEmptySignedblob) { shared_model::proto::Transaction tx = - TestTransactionBuilder() - .createdTime(iroha::time::now()) - .creatorAccountId(kAdmin) - .addAssetQuantity(kAdmin, kAsset, "1.0") - .quorum(1) - .build(); + baseTx().build(); tx.addSignature(shared_model::crypto::Signed(""), kAdminKeypair.publicKey()); integration_framework::IntegrationTestFramework(1) .setInitialState(kAdminKeypair) @@ -220,12 +182,7 @@ TEST_F(AcceptanceTest, TransactionEmptySignedblob) { */ TEST_F(AcceptanceTest, TransactionInvalidPublicKey) { shared_model::proto::Transaction tx = - TestTransactionBuilder() - .createdTime(iroha::time::now()) - .creatorAccountId(kAdmin) - .addAssetQuantity(kAdmin, kAsset, "1.0") - .quorum(1) - .build(); + baseTx().build(); auto signedBlob = shared_model::crypto::CryptoSigner<>::sign( shared_model::crypto::Blob(tx.payload()), kAdminKeypair); tx.addSignature( @@ -246,12 +203,7 @@ TEST_F(AcceptanceTest, TransactionInvalidPublicKey) { */ TEST_F(AcceptanceTest, TransactionInvalidSignedBlob) { shared_model::proto::Transaction tx = - TestTransactionBuilder() - .createdTime(iroha::time::now()) - .creatorAccountId(kAdmin) - .addAssetQuantity(kAdmin, kAsset, "1.0") - .quorum(1) - .build(); + baseTx().build(); auto signedBlob = shared_model::crypto::CryptoSigner<>::sign( shared_model::crypto::Blob(tx.payload()), kAdminKeypair); @@ -274,18 +226,9 @@ TEST_F(AcceptanceTest, TransactionInvalidSignedBlob) { * AND STATEFUL_VALIDATION_SUCCESS on that tx */ TEST_F(AcceptanceTest, TransactionValidSignedBlob) { - shared_model::proto::Transaction tx = - TestUnsignedTransactionBuilder() - .createdTime(iroha::time::now()) - .creatorAccountId(kAdmin) - .addAssetQuantity(kAdmin, kAsset, "1.0") - .quorum(1) - .build() - .signAndAddSignature(kAdminKeypair) - .finish(); integration_framework::IntegrationTestFramework(1) .setInitialState(kAdminKeypair) - .sendTx(tx, checkStatelessValid) + .sendTx(complete(baseTx<>()), checkStatelessValid) .skipProposal() .checkBlock(checkStatefulValid) .done(); diff --git a/test/integration/pipeline/pipeline_test.cpp b/test/integration/pipeline/pipeline_test.cpp index 004a14342d..ec40c8e841 100644 --- a/test/integration/pipeline/pipeline_test.cpp +++ b/test/integration/pipeline/pipeline_test.cpp @@ -71,7 +71,7 @@ TEST(PipelineIntegrationTest, SendTx) { auto tx = shared_model::proto::TransactionBuilder() .createdTime(iroha::time::now()) .creatorAccountId(kUser) - .addAssetQuantity(kUser, kAsset, "1.0") + .addAssetQuantity(kAsset, "1.0") .quorum(1) .build() .signAndAddSignature( diff --git a/test/integration/transport/ordering_gate_service_test.cpp b/test/integration/transport/ordering_gate_service_test.cpp index 112b38d54c..e9da39d719 100644 --- a/test/integration/transport/ordering_gate_service_test.cpp +++ b/test/integration/transport/ordering_gate_service_test.cpp @@ -145,7 +145,7 @@ class OrderingGateServiceTest : public ::testing::Test { shared_model::proto::TransactionBuilder() .createdTime(iroha::time::now()) .creatorAccountId("admin@ru") - .addAssetQuantity("admin@tu", "coin#coin", "1.0") + .addAssetQuantity("coin#coin", "1.0") .quorum(1) .build() .signAndAddSignature( diff --git a/test/module/irohad/ametsuchi/ametsuchi_test.cpp b/test/module/irohad/ametsuchi/ametsuchi_test.cpp index 5e1f94a72c..0dbc69d073 100644 --- a/test/module/irohad/ametsuchi/ametsuchi_test.cpp +++ b/test/module/irohad/ametsuchi/ametsuchi_test.cpp @@ -193,8 +193,6 @@ TEST_F(AmetsuchiTest, SampleTest) { user1id = "userone@ru", user2id = "usertwo@ru", assetname = "rub", assetid = "rub#ru"; - std::string account, src_account, dest_account, asset; - // Block 1 auto block1 = TestBlockBuilder() .transactions(std::vector( @@ -220,10 +218,10 @@ TEST_F(AmetsuchiTest, SampleTest) { TestBlockBuilder() .transactions(std::vector( {TestTransactionBuilder() - .creatorAccountId("admin2") + .creatorAccountId(user1id) .createAccount(user2name, domain, fake_pubkey) .createAsset(assetname, domain, 1) - .addAssetQuantity(user1id, assetid, "150.0") + .addAssetQuantity(assetid, "150.0") .transferAsset( user1id, user2id, assetid, "Transfer asset", "100.0") .build()})) @@ -247,7 +245,7 @@ TEST_F(AmetsuchiTest, SampleTest) { 2); validateAccountTransactions(blocks, "admin1", 1, 3); - validateAccountTransactions(blocks, "admin2", 1, 4); + validateAccountTransactions(blocks, user1id, 1, 4); validateAccountTransactions(blocks, "non_existing_user", 0, 0); validateAccountAssetTransactions(blocks, user1id, assetid, 1, 4); @@ -291,8 +289,6 @@ TEST_F(AmetsuchiTest, queryGetAccountAssetTransactionsTest) { asset2name = "assettwo", asset1id = "assetone#domain", asset2id = "assettwo#domain"; - std::string account, src_account, dest_account, asset; - // 1st tx auto txn1 = TestTransactionBuilder() @@ -305,16 +301,22 @@ TEST_F(AmetsuchiTest, queryGetAccountAssetTransactionsTest) { .createAccount(user3name, domain, fake_pubkey) .createAsset(asset1name, domain, 1) .createAsset(asset2name, domain, 1) - .addAssetQuantity(user1id, asset1id, "300.0") - .addAssetQuantity(user2id, asset2id, "250.0") .build(); - auto block1 = - TestBlockBuilder() - .height(1) - .transactions(std::vector({txn1})) - .prevHash(fake_hash) - .build(); + auto block1 = TestBlockBuilder() + .height(1) + .transactions(std::vector( + {txn1, + TestTransactionBuilder() + .creatorAccountId(user1id) + .addAssetQuantity(asset1id, "300.0") + .build(), + TestTransactionBuilder() + .creatorAccountId(user2id) + .addAssetQuantity(asset2id, "250.0") + .build()})) + .prevHash(fake_hash) + .build(); apply(storage, block1); @@ -385,9 +387,7 @@ TEST_F(AmetsuchiTest, queryGetAccountAssetTransactionsTest) { }, 3); - validateAccountTransactions(blocks, admin, 1, 9); - validateAccountTransactions(blocks, user1id, 1, 1); - validateAccountTransactions(blocks, user2id, 1, 2); + validateAccountTransactions(blocks, admin, 1, 7); validateAccountTransactions(blocks, user3id, 0, 0); // (user1 -> user2 # asset1) diff --git a/test/module/irohad/execution/command_validate_execute_test.cpp b/test/module/irohad/execution/command_validate_execute_test.cpp index 0417c243b9..9a2cd52553 100644 --- a/test/module/irohad/execution/command_validate_execute_test.cpp +++ b/test/module/irohad/execution/command_validate_execute_test.cpp @@ -217,8 +217,8 @@ class AddAssetQuantityTest : public CommandValidateExecuteTest { role_permissions = {Role::kAddAssetQty}; // TODO 2018-04-20 Alexey Chernyshov - IR-1276 - rework with CommandBuilder - command = buildCommand(TestTransactionBuilder().addAssetQuantity( - creator->accountId(), kAssetId, kAmount)); + command = buildCommand( + TestTransactionBuilder().addAssetQuantity(kAssetId, kAmount)); add_asset_quantity = getConcreteCommand(command); } @@ -232,11 +232,11 @@ class AddAssetQuantityTest : public CommandValidateExecuteTest { * @then executor will be passed */ TEST_F(AddAssetQuantityTest, ValidWhenNewWallet) { - EXPECT_CALL(*wsv_query, getAccountAsset(add_asset_quantity->accountId(), _)) + EXPECT_CALL(*wsv_query, getAccountAsset(creator->accountId(), _)) .WillOnce(Return(boost::none)); EXPECT_CALL(*wsv_query, getAsset(add_asset_quantity->assetId())) .WillOnce(Return(asset)); - EXPECT_CALL(*wsv_query, getAccount(add_asset_quantity->accountId())) + EXPECT_CALL(*wsv_query, getAccount(creator->accountId())) .WillOnce(Return(account)); EXPECT_CALL(*wsv_command, upsertAccountAsset(_)) .WillOnce(Return(WsvCommandResult())); @@ -254,16 +254,16 @@ TEST_F(AddAssetQuantityTest, ValidWhenNewWallet) { * @then executor will be passed */ TEST_F(AddAssetQuantityTest, ValidWhenExistingWallet) { - EXPECT_CALL(*wsv_query, - getAccountAsset(add_asset_quantity->accountId(), - add_asset_quantity->assetId())) + EXPECT_CALL( + *wsv_query, + getAccountAsset(creator->accountId(), add_asset_quantity->assetId())) .WillOnce(Return(wallet)); EXPECT_CALL(*wsv_query, getAsset(kAssetId)).WillOnce(Return(asset)); - EXPECT_CALL(*wsv_query, getAccount(add_asset_quantity->accountId())) + EXPECT_CALL(*wsv_query, getAccount(creator->accountId())) .WillOnce(Return(account)); EXPECT_CALL(*wsv_command, upsertAccountAsset(_)) .WillOnce(Return(WsvCommandResult())); - EXPECT_CALL(*wsv_query, getAccountRoles(add_asset_quantity->accountId())) + EXPECT_CALL(*wsv_query, getAccountRoles(creator->accountId())) .WillOnce(Return(admin_roles)); EXPECT_CALL(*wsv_query, getRolePermissions(kAdminRole)) .WillOnce(Return(role_permissions)); @@ -276,7 +276,7 @@ TEST_F(AddAssetQuantityTest, ValidWhenExistingWallet) { * @then executor will be failed */ TEST_F(AddAssetQuantityTest, InvalidWhenNoRoles) { - EXPECT_CALL(*wsv_query, getAccountRoles(add_asset_quantity->accountId())) + EXPECT_CALL(*wsv_query, getAccountRoles(creator->accountId())) .WillOnce(Return(boost::none)); ASSERT_TRUE(err(validateAndExecute(command))); } @@ -290,7 +290,7 @@ TEST_F(AddAssetQuantityTest, InvalidWhenWrongPrecision) { // TODO 2018-04-20 Alexey Chernyshov - IR-1276 - IR-1276 - rework with // CommandBuilder command = buildCommand(TestTransactionBuilder().addAssetQuantity( - creator->accountId(), kAssetId, kAmountWrongPrecision)); + kAssetId, kAmountWrongPrecision)); add_asset_quantity = getConcreteCommand(command); @@ -309,12 +309,12 @@ TEST_F(AddAssetQuantityTest, InvalidWhenWrongPrecision) { */ TEST_F(AddAssetQuantityTest, InvalidWhenNoAccount) { // Account to add does not exist - EXPECT_CALL(*wsv_query, getAccountRoles(add_asset_quantity->accountId())) + EXPECT_CALL(*wsv_query, getAccountRoles(creator->accountId())) .WillOnce(Return(admin_roles)); EXPECT_CALL(*wsv_query, getRolePermissions(kAdminRole)) .WillOnce(Return(role_permissions)); EXPECT_CALL(*wsv_query, getAsset(kAssetId)).WillOnce(Return(asset)); - EXPECT_CALL(*wsv_query, getAccount(add_asset_quantity->accountId())) + EXPECT_CALL(*wsv_query, getAccount(creator->accountId())) .WillOnce(Return(boost::none)); ASSERT_TRUE(err(validateAndExecute(command))); @@ -328,8 +328,8 @@ TEST_F(AddAssetQuantityTest, InvalidWhenNoAccount) { TEST_F(AddAssetQuantityTest, InvalidWhenNoAsset) { // TODO 2018-04-20 Alexey Chernyshov - IR-1276 - IR-1276 - rework with // CommandBuilder - command = buildCommand(TestTransactionBuilder().addAssetQuantity( - creator->accountId(), kNoAssetId, kAmount)); + command = buildCommand( + TestTransactionBuilder().addAssetQuantity(kNoAssetId, kAmount)); add_asset_quantity = getConcreteCommand(command); @@ -351,19 +351,19 @@ TEST_F(AddAssetQuantityTest, InvalidWhenNoAsset) { */ TEST_F(AddAssetQuantityTest, InvalidWhenAssetAdditionFails) { // TODO 2018-04-20 Alexey Chernyshov - IR-1276 - rework with CommandBuilder - command = buildCommand(TestTransactionBuilder().addAssetQuantity( - creator->accountId(), kAssetId, kMaxAmountStr)); + command = buildCommand( + TestTransactionBuilder().addAssetQuantity(kAssetId, kMaxAmountStr)); add_asset_quantity = getConcreteCommand(command); - EXPECT_CALL(*wsv_query, - getAccountAsset(add_asset_quantity->accountId(), - add_asset_quantity->assetId())) + EXPECT_CALL( + *wsv_query, + getAccountAsset(creator->accountId(), add_asset_quantity->assetId())) .WillOnce(Return(wallet)); EXPECT_CALL(*wsv_query, getAsset(kAssetId)).WillOnce(Return(asset)); - EXPECT_CALL(*wsv_query, getAccount(add_asset_quantity->accountId())) + EXPECT_CALL(*wsv_query, getAccount(creator->accountId())) .WillOnce(Return(account)); - EXPECT_CALL(*wsv_query, getAccountRoles(add_asset_quantity->accountId())) + EXPECT_CALL(*wsv_query, getAccountRoles(creator->accountId())) .WillOnce(Return(admin_roles)); EXPECT_CALL(*wsv_query, getRolePermissions(kAdminRole)) .WillOnce(Return(role_permissions)); @@ -379,8 +379,8 @@ class SubtractAssetQuantityTest : public CommandValidateExecuteTest { role_permissions = {Role::kSubtractAssetQty}; // TODO 2018-04-20 Alexey Chernyshov - IR-1276 - rework with CommandBuilder - command = buildCommand(TestTransactionBuilder().subtractAssetQuantity( - creator->accountId(), kAssetId, kAmount)); + command = buildCommand( + TestTransactionBuilder().subtractAssetQuantity(kAssetId, kAmount)); subtract_asset_quantity = getConcreteCommand( command); @@ -396,11 +396,11 @@ class SubtractAssetQuantityTest : public CommandValidateExecuteTest { * @then executor will be failed */ TEST_F(SubtractAssetQuantityTest, InvalidWhenNoWallet) { - EXPECT_CALL(*wsv_query, - getAccountAsset(subtract_asset_quantity->accountId(), - subtract_asset_quantity->assetId())) + EXPECT_CALL( + *wsv_query, + getAccountAsset(creator->accountId(), subtract_asset_quantity->assetId())) .WillOnce(Return(boost::none)); - EXPECT_CALL(*wsv_query, getAccountRoles(subtract_asset_quantity->accountId())) + EXPECT_CALL(*wsv_query, getAccountRoles(creator->accountId())) .WillOnce(Return(admin_roles)); EXPECT_CALL(*wsv_query, getRolePermissions(kAdminRole)) .WillOnce(Return(role_permissions)); @@ -414,14 +414,14 @@ TEST_F(SubtractAssetQuantityTest, InvalidWhenNoWallet) { * @then executor will be passed */ TEST_F(SubtractAssetQuantityTest, ValidWhenExistingWallet) { - EXPECT_CALL(*wsv_query, - getAccountAsset(subtract_asset_quantity->accountId(), - subtract_asset_quantity->assetId())) + EXPECT_CALL( + *wsv_query, + getAccountAsset(creator->accountId(), subtract_asset_quantity->assetId())) .WillOnce(Return(wallet)); EXPECT_CALL(*wsv_query, getAsset(kAssetId)).WillOnce(Return(asset)); EXPECT_CALL(*wsv_command, upsertAccountAsset(_)) .WillOnce(Return(WsvCommandResult())); - EXPECT_CALL(*wsv_query, getAccountRoles(subtract_asset_quantity->accountId())) + EXPECT_CALL(*wsv_query, getAccountRoles(creator->accountId())) .WillOnce(Return(admin_roles)); EXPECT_CALL(*wsv_query, getRolePermissions(kAdminRole)) .WillOnce(Return(role_permissions)); @@ -436,17 +436,17 @@ TEST_F(SubtractAssetQuantityTest, ValidWhenExistingWallet) { TEST_F(SubtractAssetQuantityTest, InvalidWhenOverAmount) { // TODO 2018-04-20 Alexey Chernyshov - IR-1276 - rework with CommandBuilder command = buildCommand(TestTransactionBuilder().subtractAssetQuantity( - creator->accountId(), kAssetId, kAmountOverflow)); + kAssetId, kAmountOverflow)); subtract_asset_quantity = getConcreteCommand( command); - EXPECT_CALL(*wsv_query, - getAccountAsset(subtract_asset_quantity->accountId(), - subtract_asset_quantity->assetId())) + EXPECT_CALL( + *wsv_query, + getAccountAsset(creator->accountId(), subtract_asset_quantity->assetId())) .WillOnce(Return(wallet)); - EXPECT_CALL(*wsv_query, getAccountRoles(subtract_asset_quantity->accountId())) + EXPECT_CALL(*wsv_query, getAccountRoles(creator->accountId())) .WillOnce(Return(admin_roles)); EXPECT_CALL(*wsv_query, getRolePermissions(kAdminRole)) .WillOnce(Return(role_permissions)); @@ -461,7 +461,7 @@ TEST_F(SubtractAssetQuantityTest, InvalidWhenOverAmount) { * @then executor will be failed */ TEST_F(SubtractAssetQuantityTest, InvalidWhenNoRoles) { - EXPECT_CALL(*wsv_query, getAccountRoles(subtract_asset_quantity->accountId())) + EXPECT_CALL(*wsv_query, getAccountRoles(creator->accountId())) .WillOnce(Return(boost::none)); ASSERT_TRUE(err(validateAndExecute(command))); } @@ -474,7 +474,7 @@ TEST_F(SubtractAssetQuantityTest, InvalidWhenNoRoles) { TEST_F(SubtractAssetQuantityTest, InvalidWhenWrongPrecision) { // TODO 2018-04-20 Alexey Chernyshov - IR-1276 - rework with CommandBuilder command = buildCommand(TestTransactionBuilder().subtractAssetQuantity( - creator->accountId(), kAssetId, kAmountWrongPrecision)); + kAssetId, kAmountWrongPrecision)); subtract_asset_quantity = getConcreteCommand( command); @@ -487,22 +487,6 @@ TEST_F(SubtractAssetQuantityTest, InvalidWhenWrongPrecision) { ASSERT_TRUE(err(validateAndExecute(command))); } -/** - * @given SubtractAssetQuantity - * @when account doesn't exist - * @then executor will be failed - */ -TEST_F(SubtractAssetQuantityTest, InvalidWhenNoAccount) { - // TODO 2018-04-20 Alexey Chernyshov - IR-1276 - rework with CommandBuilder - command = buildCommand(TestTransactionBuilder().subtractAssetQuantity( - kNoAcountId, kAssetId, kAmount)); - subtract_asset_quantity = - getConcreteCommand( - command); - - ASSERT_TRUE(err(validateAndExecute(command))); -} - /** * @given SubtractAssetQuantity * @when asset doesn't exist @@ -510,8 +494,8 @@ TEST_F(SubtractAssetQuantityTest, InvalidWhenNoAccount) { */ TEST_F(SubtractAssetQuantityTest, InvalidWhenNoAsset) { // TODO 2018-04-20 Alexey Chernyshov - IR-1276 - rework with CommandBuilder - command = buildCommand(TestTransactionBuilder().subtractAssetQuantity( - creator->accountId(), kNoAssetId, kAmount)); + command = buildCommand( + TestTransactionBuilder().subtractAssetQuantity(kNoAssetId, kAmount)); subtract_asset_quantity = getConcreteCommand( command); diff --git a/test/module/irohad/model/converters/json_commands_test.cpp b/test/module/irohad/model/converters/json_commands_test.cpp index df227e5eca..bd5443d7fe 100644 --- a/test/module/irohad/model/converters/json_commands_test.cpp +++ b/test/module/irohad/model/converters/json_commands_test.cpp @@ -36,8 +36,8 @@ #include "model/commands/subtract_asset_quantity.hpp" #include "model/commands/transfer_asset.hpp" #include "model/converters/json_command_factory.hpp" -#include "validators/permissions.hpp" #include "model/sha3_hash.hpp" +#include "validators/permissions.hpp" using namespace rapidjson; using namespace iroha; @@ -104,7 +104,6 @@ TEST_F(JsonCommandTest, create_domain) { TEST_F(JsonCommandTest, add_asset_quantity) { auto orig_command = std::make_shared(); - orig_command->account_id = "23"; iroha::Amount amount(150, 2); orig_command->amount = amount; @@ -125,7 +124,6 @@ TEST_F(JsonCommandTest, add_asset_quantity) { */ TEST_F(JsonCommandTest, subtract_asset_quantity) { auto orig_command = std::make_shared(); - orig_command->account_id = "23"; iroha::Amount amount(150, 2); orig_command->amount = amount; diff --git a/test/module/irohad/model/converters/pb_commands_test.cpp b/test/module/irohad/model/converters/pb_commands_test.cpp index 3b8bf4c783..db63409cd1 100644 --- a/test/module/irohad/model/converters/pb_commands_test.cpp +++ b/test/module/irohad/model/converters/pb_commands_test.cpp @@ -48,7 +48,6 @@ void command_converter_test(iroha::model::Command &abstract_command) { TEST(CommandTest, add_asset_quantity) { auto orig_command = iroha::model::AddAssetQuantity(); - orig_command.account_id = "23"; iroha::Amount amount(50, 1); orig_command.amount = amount; @@ -69,7 +68,6 @@ TEST(CommandTest, add_asset_quantity) { */ TEST(CommandTest, subtract_asset_quantity) { auto orig_command = iroha::model::SubtractAssetQuantity(); - orig_command.account_id = "23"; iroha::Amount amount(50, 1); orig_command.amount = amount; diff --git a/test/module/irohad/model/operators/model_operators_test.cpp b/test/module/irohad/model/operators/model_operators_test.cpp index a00827eb82..93bd5d2893 100644 --- a/test/module/irohad/model/operators/model_operators_test.cpp +++ b/test/module/irohad/model/operators/model_operators_test.cpp @@ -58,7 +58,6 @@ TEST(ModelOperatorTest, AddPeerTest) { AddAssetQuantity createAddAssetQuantity() { AddAssetQuantity aaq; - aaq.account_id = "123"; iroha::Amount amount(1010, 2); aaq.amount = amount; aaq.asset_id = "123"; @@ -78,7 +77,6 @@ TEST(ModelOperatorTest, AddAssetQuantityTest) { SubtractAssetQuantity createSubtractAssetQuantity() { SubtractAssetQuantity saq; - saq.account_id = "acc"; iroha::Amount amount(1010, 2); saq.amount = amount; saq.asset_id = "ast"; diff --git a/test/module/irohad/ordering/ordering_gate_test.cpp b/test/module/irohad/ordering/ordering_gate_test.cpp index 049100542b..1f158b039a 100644 --- a/test/module/irohad/ordering/ordering_gate_test.cpp +++ b/test/module/irohad/ordering/ordering_gate_test.cpp @@ -138,7 +138,7 @@ TEST_F(OrderingGateTest, ProposalReceivedByGateWhenSent) { auto tx = shared_model::proto::TransactionBuilder() .createdTime(iroha::time::now()) .creatorAccountId("admin@ru") - .addAssetQuantity("admin@tu", "coin#coin", "1.0") + .addAssetQuantity("coin#coin", "1.0") .quorum(1) .build() .signAndAddSignature( @@ -212,7 +212,7 @@ TEST_F(QueueBehaviorTest, SendManyProposals) { auto tx = shared_model::proto::TransactionBuilder() .createdTime(iroha::time::now()) .creatorAccountId("admin@ru") - .addAssetQuantity("admin@tu", "coin#coin", "1.0") + .addAssetQuantity("coin#coin", "1.0") .quorum(1) .build() .signAndAddSignature( diff --git a/test/module/irohad/ordering/ordering_service_test.cpp b/test/module/irohad/ordering/ordering_service_test.cpp index 68c0be981f..83953b3fbb 100644 --- a/test/module/irohad/ordering/ordering_service_test.cpp +++ b/test/module/irohad/ordering/ordering_service_test.cpp @@ -72,7 +72,7 @@ class OrderingServiceTest : public ::testing::Test { shared_model::proto::TransactionBuilder() .createdTime(iroha::time::now()) .creatorAccountId("admin@ru") - .addAssetQuantity("admin@tu", "coin#coin", "1.0") + .addAssetQuantity("coin#coin", "1.0") .quorum(1) .build() .signAndAddSignature( diff --git a/test/module/irohad/simulator/simulator_test.cpp b/test/module/irohad/simulator/simulator_test.cpp index c1d0f92972..926067beae 100644 --- a/test/module/irohad/simulator/simulator_test.cpp +++ b/test/module/irohad/simulator/simulator_test.cpp @@ -88,7 +88,7 @@ shared_model::proto::Proposal makeProposal(int height) { auto tx = shared_model::proto::TransactionBuilder() .createdTime(iroha::time::now()) .creatorAccountId("admin@ru") - .addAssetQuantity("admin@tu", "coin#coin", "1.0") + .addAssetQuantity("coin#coin", "1.0") .quorum(1) .build() .signAndAddSignature( @@ -118,7 +118,7 @@ TEST_F(SimulatorTest, ValidWhenPreviousBlock) { auto tx = shared_model::proto::TransactionBuilder() .createdTime(iroha::time::now()) .creatorAccountId("admin@ru") - .addAssetQuantity("admin@tu", "coin#coin", "1.0") + .addAssetQuantity("coin#coin", "1.0") .quorum(1) .build() .signAndAddSignature( diff --git a/test/module/shared_model/backend_proto/shared_proto_transaction_test.cpp b/test/module/shared_model/backend_proto/shared_proto_transaction_test.cpp index e8ae026224..1208c23205 100644 --- a/test/module/shared_model/backend_proto/shared_proto_transaction_test.cpp +++ b/test/module/shared_model/backend_proto/shared_proto_transaction_test.cpp @@ -42,15 +42,13 @@ iroha::protocol::Transaction generateEmptyTransaction() { /** * Helper function to generate AddAssetQuantityCommand - * @param account_id account id to add asset quantity to * @param asset_id asset id to add value to * @return AddAssetQuantity protocol command */ iroha::protocol::AddAssetQuantity generateAddAssetQuantity( - std::string account_id, std::string asset_id) { + std::string asset_id) { iroha::protocol::AddAssetQuantity command; - command.set_account_id(account_id); command.set_asset_id(asset_id); command.mutable_amount()->mutable_value()->set_fourth(1000); command.mutable_amount()->set_precision(2); @@ -66,12 +64,11 @@ iroha::protocol::AddAssetQuantity generateAddAssetQuantity( TEST(ProtoTransaction, Builder) { iroha::protocol::Transaction proto_tx = generateEmptyTransaction(); - std::string account_id = "admin@test", asset_id = "coin#test", - amount = "10.00"; + std::string asset_id = "coin#test", amount = "10.00"; auto command = proto_tx.mutable_payload()->add_commands()->mutable_add_asset_quantity(); - command->CopyFrom(generateAddAssetQuantity(account_id, asset_id)); + command->CopyFrom(generateAddAssetQuantity(asset_id)); auto keypair = shared_model::crypto::CryptoProviderEd25519Sha3::generateKeypair(); @@ -85,7 +82,7 @@ TEST(ProtoTransaction, Builder) { auto tx = shared_model::proto::TransactionBuilder() .creatorAccountId(creator_account_id) - .addAssetQuantity(account_id, asset_id, amount) + .addAssetQuantity(asset_id, amount) .createdTime(created_time) .quorum(1) .build(); @@ -107,12 +104,11 @@ TEST(ProtoTransaction, BuilderWithInvalidTx) { std::string invalid_asset_id = "cointest", // invalid asset_id without # amount = "10.00"; - ASSERT_THROW( - shared_model::proto::TransactionBuilder() - .creatorAccountId(invalid_account_id) - .addAssetQuantity(invalid_account_id, invalid_asset_id, amount) - .createdTime(created_time) - .quorum(1) - .build(), - std::invalid_argument); + ASSERT_THROW(shared_model::proto::TransactionBuilder() + .creatorAccountId(invalid_account_id) + .addAssetQuantity(invalid_asset_id, amount) + .createdTime(created_time) + .quorum(1) + .build(), + std::invalid_argument); } diff --git a/test/module/shared_model/bindings/BuilderTest.java b/test/module/shared_model/bindings/BuilderTest.java index c53aa22cdf..4f6fcb8f08 100644 --- a/test/module/shared_model/bindings/BuilderTest.java +++ b/test/module/shared_model/bindings/BuilderTest.java @@ -284,54 +284,31 @@ void addSignatoryInvalidKey() { @Test void addAssetQuantity() { - UnsignedTx tx = builder.addAssetQuantity("admin@test", "asset#domain", "12.345").build(); + UnsignedTx tx = builder.addAssetQuantity("asset#domain", "12.345").build(); assertTrue(checkProtoTx(proto(tx))); } @Test - void addAssetQuantityValidAccountsAndAssets() { + void addAssetQuantityValidAssets() { for (String domain: validDomains) { for (String name: validNameSymbols1) { - UnsignedTx tx = builder.addAssetQuantity(name + "@" + domain, name + "#" + domain, "100").build(); + UnsignedTx tx = builder.addAssetQuantity(name + "#" + domain, "100").build(); assertTrue(checkProtoTx(proto(tx))); } } } - @Test - void addAssetQuantityInvalidAccountDomain() { - for (String domain: invalidDomains) { - ModelTransactionBuilder builder = base().addAssetQuantity("admin@" + domain, "asset#test", "10"); - assertThrows(IllegalArgumentException.class, builder::build); - } - } - @Test void addAssetQuantityInvalidAssetDomain() { for (String domain: invalidDomains) { - ModelTransactionBuilder builder = base().addAssetQuantity("admin@test", "asset#" + domain, "10"); + ModelTransactionBuilder builder = base().addAssetQuantity("asset#" + domain, "10"); assertThrows(IllegalArgumentException.class, builder::build); } } @Test void addAssetZeroQuantity() { - ModelTransactionBuilder builder = base().addAssetQuantity("admin@test", "asset#domain", "0"); - assertThrows(IllegalArgumentException.class, builder::build); - } - - @Test - void addAssetQuantityInvalidAccountName() { - for (String accountName: invalidNameSymbols1) { - String accountId = accountName + "@test"; - ModelTransactionBuilder builder = base().addAssetQuantity(accountId, "asset#domain", "10"); - assertThrows(IllegalArgumentException.class, builder::build); - } - } - - @Test - void addAssetQuantityEmptyAccount() { - ModelTransactionBuilder builder = base().addAssetQuantity("", "asset#test", "10"); + ModelTransactionBuilder builder = base().addAssetQuantity("asset#domain", "0"); assertThrows(IllegalArgumentException.class, builder::build); } @@ -339,21 +316,21 @@ void addAssetQuantityEmptyAccount() { void addAssetQuantityInvalidAssetName() { for (String assetName: invalidNameSymbols1) { String assetId = assetName + "#test"; - ModelTransactionBuilder builder = base().addAssetQuantity("account@test", assetId, "10"); + ModelTransactionBuilder builder = base().addAssetQuantity(assetId, "10"); assertThrows(IllegalArgumentException.class, builder::build); } } @Test void addAssetQuantityEmptyAsset() { - ModelTransactionBuilder builder = base().addAssetQuantity("account@test", "", "10"); + ModelTransactionBuilder builder = base().addAssetQuantity("", "10"); assertThrows(IllegalArgumentException.class, builder::build); } @Test void addAssetQuantityInvalidAmount() { for (String amount: new String[]{"", "-12", "-13.45", "chars", "chars10"}) { - ModelTransactionBuilder builder = base().addAssetQuantity("account@test", "asset#test", amount); + ModelTransactionBuilder builder = base().addAssetQuantity("asset#test", amount); assertThrows(IllegalArgumentException.class, builder::build); } } @@ -938,32 +915,16 @@ void revokePermissionEmptyAccount() { void subtractAssetQuantity() { for (String name: validNameSymbols1) { for (String domain: validDomains) { - UnsignedTx tx = builder.subtractAssetQuantity(name + "@" + domain, name + "#" + domain, "10.22").build(); + UnsignedTx tx = builder.subtractAssetQuantity(name + "#" + domain, "10.22").build(); assertTrue(checkProtoTx(proto(tx))); } } } - @Test - void subtractAssetQuantityInvalidAccount() { - for (String account: invalidNameSymbols1) { - ModelTransactionBuilder builder = base().subtractAssetQuantity(account + "@test", "coin#test", "10"); - assertThrows(IllegalArgumentException.class, builder::build); - } - } - - @Test - void subtractAssetQuantityInvalidAccountDomain() { - for (String domain: invalidDomains) { - ModelTransactionBuilder builder = base().subtractAssetQuantity("admin@" + domain, "coin#test", "10"); - assertThrows(IllegalArgumentException.class, builder::build); - } - } - @Test void subtractAssetQuantityInvalidAsset() { for (String asset: invalidNameSymbols1) { - ModelTransactionBuilder builder = base().subtractAssetQuantity("admin@test", asset + "#test", "10"); + ModelTransactionBuilder builder = base().subtractAssetQuantity(asset + "#test", "10"); assertThrows(IllegalArgumentException.class, builder::build); } } @@ -971,20 +932,14 @@ void subtractAssetQuantityInvalidAsset() { @Test void subtractAssetQuantityInvalidAssetDomain() { for (String domain: invalidDomains) { - ModelTransactionBuilder builder = base().subtractAssetQuantity("admin@test", "coin#" + domain, "10"); + ModelTransactionBuilder builder = base().subtractAssetQuantity("coin#" + domain, "10"); assertThrows(IllegalArgumentException.class, builder::build); } } - @Test - void subtractAssetQuantityEmptyAccount() { - ModelTransactionBuilder builder = base().subtractAssetQuantity("", "coin#test", "10"); - assertThrows(IllegalArgumentException.class, builder::build); - } - @Test void subtractAssetQuantityEmptyAsset() { - ModelTransactionBuilder builder = base().subtractAssetQuantity("admin@test", "", "10"); + ModelTransactionBuilder builder = base().subtractAssetQuantity("", "10"); assertThrows(IllegalArgumentException.class, builder::build); } @@ -1000,7 +955,7 @@ void subtractAssetQuantityInvalidAmount() { }; for (String amount: invalidAmounts) { - ModelTransactionBuilder builder = base().subtractAssetQuantity("admin@test", "coin#test", amount); + ModelTransactionBuilder builder = base().subtractAssetQuantity("coin#test", amount); assertThrows(IllegalArgumentException.class, builder::build); } } diff --git a/test/module/shared_model/bindings/builder-test.py b/test/module/shared_model/bindings/builder-test.py index f7e6d21b4b..9101678045 100644 --- a/test/module/shared_model/bindings/builder-test.py +++ b/test/module/shared_model/bindings/builder-test.py @@ -208,47 +208,33 @@ def test_add_signatory_invalid_key(self): # ====================== AddAssetQuantity Tests ====================== def test_add_asset_quantity(self): - tx = self.builder.addAssetQuantity("admin@test", "asset#domain", "12.345").build() + tx = self.builder.addAssetQuantity("asset#domain", "12.345").build() self.assertTrue(self.check_proto_tx(self.proto(tx))) - def test_add_asset_quantity_valid_account_and_asset(self): + def test_add_asset_quantity_valid_asset(self): for name in VALID_NAMES_1: for domain in VALID_DOMAINS: - tx = self.builder.addAssetQuantity("{}@{}".format(name, domain), "{}#{}".format(name, domain), "100").build() + tx = self.builder.addAssetQuantity("{}#{}".format(name, domain), "100").build() self.assertTrue(self.check_proto_tx(self.proto(tx))) - def test_add_asset_quantity_invalid_account(self): - for name in INVALID_NAMES_1: - with self.assertRaises(ValueError): - self.base().addAssetQuantity("{}@test".format(name), "coin#test", "10").build() - - def test_add_asset_quantity_invalid_account_domain(self): - for domain in INVALID_DOMAINS: - with self.assertRaises(ValueError): - self.base().addAssetQuantity("admin@{}".format(domain), "coin#test", "10").build() - - def test_add_asset_quantity_empty_account(self): - with self.assertRaises(ValueError): - self.base().addAssetQuantity("", "coin#test", "10").build() - def test_add_asset_quantity_invalid_asset(self): for name in INVALID_NAMES_1: with self.assertRaises(ValueError): - self.base().addAssetQuantity("admin@test", "{}#test".format(name), "10").build() + self.base().addAssetQuantity("{}#test".format(name), "10").build() def test_add_asset_quantity_invalid_asset_domain(self): for domain in INVALID_DOMAINS: with self.assertRaises(ValueError): - self.base().addAssetQuantity("admin@test", "coin#{}".format(domain), "10").build() + self.base().addAssetQuantity("coin#{}".format(domain), "10").build() def test_add_asset_quantity_empty_asset(self): with self.assertRaises(ValueError): - self.base().addAssetQuantity("admin@test", "", "10").build() + self.base().addAssetQuantity("", "10").build() def test_add_asset_quantity_invalid_amount(self): for amount in ["", "-12", "-13.45", "chars", "chars10"]: with self.assertRaises(ValueError): - self.base().addAssetQuantity("admin@test", "coin#test", amount).build() + self.base().addAssetQuantity("coin#test", amount).build() # ====================== RemoveSignatory Tests ====================== @@ -611,42 +597,28 @@ def test_revoke_permission_empty_account(self): def test_subtract_asset_quantity(self): for domain in VALID_DOMAINS: for name in VALID_NAMES_1: - tx = self.builder.subtractAssetQuantity("{}@{}".format(name, domain), "{}#{}".format(name, domain), "10").build() + tx = self.builder.subtractAssetQuantity("{}#{}".format(name, domain), "10").build() self.assertTrue(self.check_proto_tx(self.proto(tx))) - def test_subtract_asset_quantity_invalid_account(self): - for name in INVALID_NAMES_1: - with self.assertRaises(ValueError): - self.base().subtractAssetQuantity("{}@test".format(name), "coin#test", "10").build() - - def test_subtract_asset_quantity_invalid_account_domain(self): - for domain in INVALID_DOMAINS: - with self.assertRaises(ValueError): - self.base().subtractAssetQuantity("admin@{}".format(domain), "coin#test", "10").build() - - def test_subtract_asset_quantity_with_empty_account(self): - with self.assertRaises(ValueError): - self.base().subtractAssetQuantity("", "coin#test", "10").build() - def test_subtract_asset_quantity_invalid_asset_name(self): for name in INVALID_NAMES_1: with self.assertRaises(ValueError): - self.base().subtractAssetQuantity("admin@test", "{}#test".format(name), "10").build() + self.base().subtractAssetQuantity("{}#test".format(name), "10").build() def test_subtract_asset_quantity_invalid_asset_domain(self): for domain in INVALID_DOMAINS: with self.assertRaises(ValueError): - self.base().subtractAssetQuantity("admin@test", "coin#{}".format(domain), "10").build() + self.base().subtractAssetQuantity("coin#{}".format(domain), "10").build() def test_subtract_asset_quantity_empty_account(self): with self.assertRaises(ValueError): - self.base().subtractAssetQuantity("admin@test", "", "10").build() + self.base().subtractAssetQuantity("", "10").build() def test_subtract_asset_quantity_invalid_amount(self): amounts = ["", "0", "chars", "-10", "10chars", "10.10.10"] for amount in amounts: with self.assertRaises(ValueError): - self.base().subtractAssetQuantity("admin@test", "coin#test", amount).build() + self.base().subtractAssetQuantity("coin#test", amount).build() if __name__ == '__main__': unittest.main() diff --git a/test/module/shared_model/builders/protobuf/block_builder_test.cpp b/test/module/shared_model/builders/protobuf/block_builder_test.cpp index bc6ad0013e..3f0979f69a 100644 --- a/test/module/shared_model/builders/protobuf/block_builder_test.cpp +++ b/test/module/shared_model/builders/protobuf/block_builder_test.cpp @@ -21,7 +21,7 @@ TEST(BlockBuilderTest, BlockWithTransactions) { .createdTime(iroha::time::now()) .creatorAccountId("admin@test") .quorum(1) - .addAssetQuantity("admin@test", "coin#test", "1.0") + .addAssetQuantity("coin#test", "1.0") .build(); ASSERT_NO_THROW( diff --git a/test/regression/regression_test.cpp b/test/regression/regression_test.cpp index a67c321328..770d283480 100644 --- a/test/regression/regression_test.cpp +++ b/test/regression/regression_test.cpp @@ -36,7 +36,7 @@ TEST(RegressionTest, SequentialInitialization) { auto tx = shared_model::proto::TransactionBuilder() .createdTime(iroha::time::now()) .creatorAccountId(kUser) - .addAssetQuantity(kUser, kAsset, "1.0") + .addAssetQuantity(kAsset, "1.0") .quorum(1) .build() .signAndAddSignature( @@ -87,7 +87,7 @@ TEST(RegressionTest, StateRecovery) { .createdTime(iroha::time::now()) .creatorAccountId("admin@test") .createAccount("user", "test", userKeypair.publicKey()) - .addAssetQuantity("admin@test", "coin#test", "133.0") + .addAssetQuantity("coin#test", "133.0") .transferAsset( "admin@test", "user@test", "coin#test", "descrs", "97.8") .quorum(1) From 3dd4aa6ef16993b89834df08c131093913f942ca Mon Sep 17 00:00:00 2001 From: Kitsu Date: Tue, 3 Jul 2018 12:38:13 +0300 Subject: [PATCH 20/97] Add oneof case bound check (#1502) * Add check for Command variant case * Add check for Query variant case * Add related tests Signed-off-by: Kitsu --- shared_model/validators/query_validator.hpp | 17 ++++++++++++++--- .../validators/transaction_validator.hpp | 12 ++++++++++++ .../validators/query_validator_test.cpp | 14 ++++++++++++++ .../validators/transaction_validator_test.cpp | 18 +++++++++++++++--- 4 files changed, 55 insertions(+), 6 deletions(-) diff --git a/shared_model/validators/query_validator.hpp b/shared_model/validators/query_validator.hpp index 05e183fb3b..c306cef604 100644 --- a/shared_model/validators/query_validator.hpp +++ b/shared_model/validators/query_validator.hpp @@ -20,7 +20,7 @@ #include -#include "interfaces/queries/query.hpp" +#include "backend/protobuf/queries/proto_query.hpp" #include "validators/answer.hpp" namespace shared_model { @@ -174,9 +174,20 @@ namespace shared_model { answer.addReason(std::move(qry_reason)); } - auto reason = boost::apply_visitor(query_field_validator_, qry.get()); - if (not reason.second.empty()) { + auto qry_case = static_cast(qry) + .getTransport() + .payload() + .query_case(); + if (iroha::protocol::Query_Payload::QUERY_NOT_SET == qry_case) { + ReasonsGroupType reason; + reason.first = "Undefined"; + reason.second.push_back("query is undefined"); answer.addReason(std::move(reason)); + } else { + auto reason = boost::apply_visitor(query_field_validator_, qry.get()); + if (not reason.second.empty()) { + answer.addReason(std::move(reason)); + } } return answer; diff --git a/shared_model/validators/transaction_validator.hpp b/shared_model/validators/transaction_validator.hpp index 6e1dcb07d2..522d904643 100644 --- a/shared_model/validators/transaction_validator.hpp +++ b/shared_model/validators/transaction_validator.hpp @@ -21,6 +21,7 @@ #include #include +#include "backend/protobuf/commands/proto_command.hpp" #include "backend/protobuf/permissions.hpp" #include "backend/protobuf/transaction.hpp" #include "validators/answer.hpp" @@ -271,6 +272,17 @@ namespace shared_model { } for (const auto &command : tx.commands()) { + auto cmd_case = + static_cast(command) + .getTransport() + .command_case(); + if (iroha::protocol::Command::COMMAND_NOT_SET == cmd_case) { + ReasonsGroupType reason; + reason.first = "Undefined"; + reason.second.push_back("command is undefined"); + answer.addReason(std::move(reason)); + continue; + } auto reason = boost::apply_visitor(command_validator_, command.get()); if (not reason.second.empty()) { answer.addReason(std::move(reason)); diff --git a/test/module/shared_model/validators/query_validator_test.cpp b/test/module/shared_model/validators/query_validator_test.cpp index 772712921e..af5f31fb0f 100644 --- a/test/module/shared_model/validators/query_validator_test.cpp +++ b/test/module/shared_model/validators/query_validator_test.cpp @@ -66,6 +66,20 @@ TEST_F(QueryValidatorTest, StatelessValidTest) { }); } +/** + * @given Protobuf query object with unset query + * @when validate is called + * @then there is a error returned + */ +TEST_F(QueryValidatorTest, UnsetQuery) { + iroha::protocol::Query qry; + qry.mutable_payload()->mutable_meta()->set_created_time(created_time); + qry.mutable_payload()->mutable_meta()->set_creator_account_id(account_id); + qry.mutable_payload()->mutable_meta()->set_query_counter(counter); + auto answer = query_validator.validate(proto::Query(qry)); + ASSERT_TRUE(answer.hasErrors()); +} + /** * @given Protobuf query object * @when Query has no fields set, and each query type has no fields set diff --git a/test/module/shared_model/validators/transaction_validator_test.cpp b/test/module/shared_model/validators/transaction_validator_test.cpp index b421bafb7b..6e37db4aa1 100644 --- a/test/module/shared_model/validators/transaction_validator_test.cpp +++ b/test/module/shared_model/validators/transaction_validator_test.cpp @@ -39,6 +39,7 @@ class TransactionValidatorTest : public ValidatorsTest { .getTransport(); return tx; } + shared_model::validation::DefaultTransactionValidator transaction_validator; }; /** @@ -49,7 +50,6 @@ class TransactionValidatorTest : public ValidatorsTest { TEST_F(TransactionValidatorTest, EmptyTransactionTest) { auto tx = generateEmptyTransaction(); tx.mutable_payload()->set_created_time(created_time); - shared_model::validation::DefaultTransactionValidator transaction_validator; auto result = proto::Transaction(iroha::protocol::Transaction(tx)); auto answer = transaction_validator.validate(result); ASSERT_EQ(answer.getReasonsMap().size(), 1); @@ -101,13 +101,26 @@ TEST_F(TransactionValidatorTest, StatelessValidTest) { }, [] {}); - shared_model::validation::DefaultTransactionValidator transaction_validator; auto result = proto::Transaction(iroha::protocol::Transaction(tx)); auto answer = transaction_validator.validate(result); ASSERT_FALSE(answer.hasErrors()) << answer.reason(); } +/** + * @given Protobuf transaction object with unset command + * @when validate is called + * @then there is a error returned + */ +TEST_F(TransactionValidatorTest, UnsetCommand) { + iroha::protocol::Transaction tx = generateEmptyTransaction(); + tx.mutable_payload()->set_creator_account_id(account_id); + tx.mutable_payload()->set_created_time(created_time); + auto answer = transaction_validator.validate(proto::Transaction(tx)); + tx.mutable_payload()->add_commands(); + ASSERT_TRUE(answer.hasErrors()); +} + /** * @given transaction made of commands with invalid fields * @when commands validation is invoked @@ -136,7 +149,6 @@ TEST_F(TransactionValidatorTest, StatelessInvalidTest) { }, [] {}); - shared_model::validation::DefaultTransactionValidator transaction_validator; auto result = proto::Transaction(iroha::protocol::Transaction(tx)); auto answer = transaction_validator.validate(result); From 11508b5fc7d557cd4e0b6e0fdd2a09c29af0bd59 Mon Sep 17 00:00:00 2001 From: Sergei Date: Tue, 3 Jul 2018 21:29:40 +0700 Subject: [PATCH 21/97] Feature/batch meta (#1504) Feature/batch meta #1504 Signed-off-by: Sergei --- example/admin@test.priv | 2 +- example/admin@test.pub | 2 +- example/genesis.block | 237 ++++++++++-------- example/node0.priv | 2 +- example/node0.pub | 2 +- example/test@test.priv | 2 +- example/test@test.pub | 2 +- .../impl/pb_transaction_factory.cpp | 4 +- schema/block.proto | 41 +-- shared_model/backend/protobuf/batch_meta.hpp | 68 +++++ shared_model/backend/protobuf/transaction.hpp | 36 ++- .../transaction_template.hpp | 30 ++- .../interfaces/common_objects/types.hpp | 2 + .../interfaces/iroha_internal/batch_meta.hpp | 48 ++++ shared_model/interfaces/transaction.hpp | 25 +- shared_model/validators/field_validator.cpp | 4 + shared_model/validators/field_validator.hpp | 4 + .../validators/transaction_validator.hpp | 2 + .../shared_proto_transaction_test.cpp | 11 +- .../validators/field_validator_test.cpp | 37 ++- .../validators/transaction_validator_test.cpp | 86 ++++--- .../validators/validators_fixture.hpp | 47 ++++ 22 files changed, 495 insertions(+), 199 deletions(-) create mode 100644 shared_model/backend/protobuf/batch_meta.hpp create mode 100644 shared_model/interfaces/iroha_internal/batch_meta.hpp diff --git a/example/admin@test.priv b/example/admin@test.priv index 87ff52af36..d858ce796c 100644 --- a/example/admin@test.priv +++ b/example/admin@test.priv @@ -1 +1 @@ -0f0ce16d2afbb8eca23c7d8c2724f0c257a800ee2bbd54688cec6b898e3f7e33 \ No newline at end of file +f101537e319568c765b2cc89698325604991dca57b9716b58016b253506cab70 \ No newline at end of file diff --git a/example/admin@test.pub b/example/admin@test.pub index 1dfda5428f..49c3bef482 100644 --- a/example/admin@test.pub +++ b/example/admin@test.pub @@ -1 +1 @@ -889f6b881e331be21487db77dcf32c5f8d3d5e8066e78d2feac4239fe91d416f \ No newline at end of file +313a07e6384776ed95447710d15e59148473ccfc052a681317a72a69f2a49910 \ No newline at end of file diff --git a/example/genesis.block b/example/genesis.block index bb30df3c22..e9802e6a94 100644 --- a/example/genesis.block +++ b/example/genesis.block @@ -3,113 +3,140 @@ "transactions":[ { "payload":{ - "commands":[ - { - "addPeer":{ - "peer":{ - "address":"localhost:10001", - "peerKey":"0E2icbV/5jQmrh3Jf2lSEEA3QR/PTztzncIX9F5fyZs=" + "reducedPayload":{ + "commands":[ + { + "addPeer":{ + "peer":{ + "address":"0.0.0.0:10001", + "peerKey":"vd1YQE0TFeDrJ5AsXXyOsGAsFiOPAFdz30BrwZEwiSk=" + } + } + }, + { + "addPeer":{ + "peer":{ + "address":"0.0.0.0:20001", + "peerKey":"D1Faq417+ORUHAU9clPBphAJxj6Bn1nJhhQf4eCuMj8=" + } + } + }, + { + "addPeer":{ + "peer":{ + "address":"0.0.0.0:30001", + "peerKey":"MMpE7CK12zY3K/0cvfCuupow/y0euzaF4frTJTCiQ10=" + } + } + }, + { + "addPeer":{ + "peer":{ + "address":"0.0.0.0:40001", + "peerKey":"C65E+6pjV8bQP4VYovMU8Yb6Q3G9UWGUH5THnIvKCEo=" + } + } + }, + { + "createRole":{ + "roleName":"admin", + "permissions":[ + "can_add_peer", + "can_add_signatory", + "can_create_account", + "can_create_domain", + "can_get_all_acc_ast", + "can_get_all_acc_ast_txs", + "can_get_all_acc_detail", + "can_get_all_acc_txs", + "can_get_all_accounts", + "can_get_all_signatories", + "can_get_all_txs", + "can_get_blocks", + "can_get_roles", + "can_read_assets", + "can_remove_signatory", + "can_set_quorum" + ] + } + }, + { + "createRole":{ + "roleName":"user", + "permissions":[ + "can_add_signatory", + "can_get_my_acc_ast", + "can_get_my_acc_ast_txs", + "can_get_my_acc_detail", + "can_get_my_acc_txs", + "can_get_my_account", + "can_get_my_signatories", + "can_get_my_txs", + "can_grant_can_add_my_signatory", + "can_grant_can_remove_my_signatory", + "can_grant_can_set_my_account_detail", + "can_grant_can_set_my_quorum", + "can_grant_can_transfer_my_assets", + "can_receive", + "can_remove_signatory", + "can_set_quorum", + "can_transfer" + ] + } + }, + { + "createRole":{ + "roleName":"money_creator", + "permissions":[ + "can_add_asset_qty", + "can_create_asset", + "can_receive", + "can_transfer" + ] + } + }, + { + "createDomain":{ + "domainId":"test", + "defaultRole":"user" + } + }, + { + "createAsset":{ + "assetName":"coin", + "domainId":"test", + "precision":2 + } + }, + { + "createAccount":{ + "accountName":"admin", + "domainId":"test", + "mainPubkey":"MToH5jhHdu2VRHcQ0V5ZFIRzzPwFKmgTF6cqafKkmRA=" + } + }, + { + "createAccount":{ + "accountName":"test", + "domainId":"test", + "mainPubkey":"cW/lBfafGFEaGwg5Faqf9z7zbmaIGZ85WXUNs4uPS/w=" + } + }, + { + "appendRole":{ + "accountId":"admin@test", + "roleName":"admin" + } + }, + { + "appendRole":{ + "accountId":"admin@test", + "roleName":"money_creator" } } - }, - { - "createRole":{ - "roleName":"admin", - "permissions":[ - "can_add_peer", - "can_add_signatory", - "can_create_account", - "can_create_domain", - "can_get_all_acc_ast", - "can_get_all_acc_ast_txs", - "can_get_all_acc_detail", - "can_get_all_acc_txs", - "can_get_all_accounts", - "can_get_all_signatories", - "can_get_all_txs", - "can_get_roles", - "can_read_assets", - "can_remove_signatory", - "can_set_quorum", - "can_get_blocks" - ] - } - }, - { - "createRole":{ - "roleName":"user", - "permissions":[ - "can_add_signatory", - "can_get_my_acc_ast", - "can_get_my_acc_ast_txs", - "can_get_my_acc_detail", - "can_get_my_acc_txs", - "can_get_my_account", - "can_get_my_signatories", - "can_get_my_txs", - "can_grant_can_add_my_signatory", - "can_grant_can_remove_my_signatory", - "can_grant_can_set_my_account_detail", - "can_grant_can_set_my_quorum", - "can_grant_can_transfer_my_assets", - "can_receive", - "can_remove_signatory", - "can_set_quorum", - "can_transfer" - ] - } - }, - { - "createRole":{ - "roleName":"money_creator", - "permissions":[ - "can_add_asset_qty", - "can_create_asset", - "can_receive", - "can_transfer" - ] - } - }, - { - "createDomain":{ - "domainId":"test", - "defaultRole":"user" - } - }, - { - "createAsset":{ - "assetName":"coin", - "domainId":"test", - "precision":2 - } - }, - { - "createAccount":{ - "accountName":"admin", - "domainId":"test", - "mainPubkey":"iJ9riB4zG+IUh9t33PMsX409XoBm540v6sQjn+kdQW8=" - } - }, - { - "createAccount":{ - "accountName":"test", - "domainId":"test", - "mainPubkey":"3MfszkSPLhkKSBrsfKIe5S7aVG5mC0gg9JdtATIVcJc=" - } - }, - { - "appendRole":{ - "accountId":"admin@test", - "roleName":"admin" - } - }, - { - "appendRole":{ - "accountId":"admin@test", - "roleName":"money_creator" - } - } - ] + ], + "quorum":1 + } } } ], diff --git a/example/node0.priv b/example/node0.priv index 1362af9c39..48c73f36e8 100644 --- a/example/node0.priv +++ b/example/node0.priv @@ -1 +1 @@ -41209bd907789fd5a796ac6bdff908bac2f7abcf7a1d0b99a18290f285f6e965 \ No newline at end of file +cc5013e43918bd0e5c4d800416c88bed77892ff077929162bb03ead40a745e88 \ No newline at end of file diff --git a/example/node0.pub b/example/node0.pub index bb7c1c9dcd..35d44a8228 100644 --- a/example/node0.pub +++ b/example/node0.pub @@ -1 +1 @@ -d04da271b57fe63426ae1dc97f6952104037411fcf4f3b739dc217f45e5fc99b \ No newline at end of file +bddd58404d1315e0eb27902c5d7c8eb0602c16238f005773df406bc191308929 \ No newline at end of file diff --git a/example/test@test.priv b/example/test@test.priv index f41898d68e..6605e2840b 100644 --- a/example/test@test.priv +++ b/example/test@test.priv @@ -1 +1 @@ -2e5b37fd881f260323dca4f0776e6e1e969bef7dab14673858f82663e7cd8556 \ No newline at end of file +7e00405ece477bb6dd9b03a78eee4e708afc2f5bcdce399573a5958942f4a390 \ No newline at end of file diff --git a/example/test@test.pub b/example/test@test.pub index 95419850c9..f08d97cac9 100644 --- a/example/test@test.pub +++ b/example/test@test.pub @@ -1 +1 @@ -dcc7ecce448f2e190a481aec7ca21ee52eda546e660b4820f4976d0132157097 \ No newline at end of file +716fe505f69f18511a1b083915aa9ff73ef36e6688199f3959750db38b8f4bfc \ No newline at end of file diff --git a/irohad/model/converters/impl/pb_transaction_factory.cpp b/irohad/model/converters/impl/pb_transaction_factory.cpp index c55d910dcd..2a4bb9ad72 100644 --- a/irohad/model/converters/impl/pb_transaction_factory.cpp +++ b/irohad/model/converters/impl/pb_transaction_factory.cpp @@ -28,7 +28,7 @@ namespace iroha { model::converters::PbCommandFactory factory; protocol::Transaction pbtx; - auto pl = pbtx.mutable_payload(); + auto pl = pbtx.mutable_payload()->mutable_reduced_payload(); pl->set_created_time(tx.created_ts); pl->set_creator_account_id(tx.creator_account_id); pl->set_quorum(tx.quorum); @@ -52,7 +52,7 @@ namespace iroha { model::converters::PbCommandFactory commandFactory; model::Transaction tx; - const auto &pl = pb_tx.payload(); + const auto &pl = pb_tx.payload().reduced_payload(); tx.creator_account_id = pl.creator_account_id(); tx.created_ts = pl.created_time(); tx.quorum = static_cast(pl.quorum()); diff --git a/schema/block.proto b/schema/block.proto index 5e2c891cfa..c66a73feef 100644 --- a/schema/block.proto +++ b/schema/block.proto @@ -7,29 +7,42 @@ message Header { uint64 created_time = 1; repeated Signature signatures = 2; } - - message Transaction { +message Transaction { message Payload { - repeated Command commands = 1; - string creator_account_id = 2; - uint64 created_time = 3; - uint32 quorum = 4; - } + message BatchMeta{ + enum BatchType{ + ATOMIC = 0; + ORDERED = 1; + } + BatchType type = 1; + repeated bytes tx_hashes = 2; + } + message ReducedPayload{ + repeated Command commands = 1; + string creator_account_id = 2; + uint64 created_time = 3; + uint32 quorum = 4; + } + ReducedPayload reduced_payload = 1; + oneof optional_batch_meta{ + BatchMeta batch = 5; + } + } Payload payload = 1; repeated Signature signatures = 2; - } +} - message Block { +message Block { // everything that should be signed: message Payload { repeated Transaction transactions = 1; - uint32 tx_number = 2; // the number of transactions inside. Maximum 16384 or 2^14 - uint64 height = 3; // the current block number in a ledger - bytes prev_block_hash = 5; // Previous block hash + uint32 tx_number = 2; // the number of transactions inside. Maximum 16384 or 2^14 + uint64 height = 3; // the current block number in a ledger + bytes prev_block_hash = 5; // Previous block hash uint64 created_time = 6; - } + } Payload payload = 1; repeated Signature signatures = 2; - } +} diff --git a/shared_model/backend/protobuf/batch_meta.hpp b/shared_model/backend/protobuf/batch_meta.hpp new file mode 100644 index 0000000000..707f7789ff --- /dev/null +++ b/shared_model/backend/protobuf/batch_meta.hpp @@ -0,0 +1,68 @@ +/** + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef IROHA_PROTO_BATCH_META_HPP +#define IROHA_PROTO_BATCH_META_HPP + +#include +#include + +#include "backend/protobuf/common_objects/trivial_proto.hpp" +#include "backend/protobuf/util.hpp" +#include "block.pb.h" +#include "interfaces/common_objects/amount.hpp" +#include "interfaces/common_objects/types.hpp" +#include "utils/lazy_initializer.hpp" + +namespace shared_model { + namespace proto { + class BatchMeta final + : public CopyableProto { + public: + template + explicit BatchMeta(BatchMetaType &&batch_meta) + : CopyableProto(std::forward(batch_meta)), + type_{[this] { + unsigned which = proto_->GetDescriptor() + ->FindFieldByName("type") + ->enum_type() + ->FindValueByNumber(proto_->type()) + ->index(); + return static_cast(which); + }}, + transaction_hashes_{[this] { + return boost::accumulate( + proto_->tx_hashes(), + TransactionHashesType{}, + [](auto &&acc, const auto &hash) { + acc.emplace_back(hash); + return std::forward(acc); + }); + }} {} + + BatchMeta(const BatchMeta &o) : BatchMeta(o.proto_) {} + + BatchMeta(BatchMeta &&o) noexcept : BatchMeta(std::move(o.proto_)) {} + + interface::types::BatchType type() const override { + return *type_; + }; + const TransactionHashesType &transactionHashes() const override { + return *transaction_hashes_; + }; + + private: + template + using Lazy = detail::LazyInitializer; + + Lazy type_; + + const Lazy transaction_hashes_; + }; // namespace proto + } // namespace proto +} // namespace shared_model +#endif // IROHA_PROTO_AMOUNT_HPP diff --git a/shared_model/backend/protobuf/transaction.hpp b/shared_model/backend/protobuf/transaction.hpp index d650a4bd43..623d4c00b9 100644 --- a/shared_model/backend/protobuf/transaction.hpp +++ b/shared_model/backend/protobuf/transaction.hpp @@ -24,6 +24,7 @@ #include "backend/protobuf/commands/proto_command.hpp" #include "backend/protobuf/common_objects/signature.hpp" +#include "batch_meta.hpp" #include "block.pb.h" #include "utils/lazy_initializer.hpp" @@ -43,7 +44,7 @@ namespace shared_model { : Transaction(std::move(o.proto_)) {} const interface::types::AccountIdType &creatorAccountId() const override { - return payload_.creator_account_id(); + return reduced_payload_.creator_account_id(); } Transaction::CommandsType commands() const override { @@ -58,6 +59,10 @@ namespace shared_model { return *blobTypePayload_; } + const interface::types::BlobType &reduced_payload() const override { + return *blobTypeReducedPayload_; + } + interface::types::SignatureRangeType signatures() const override { return *signatures_; } @@ -83,11 +88,16 @@ namespace shared_model { } interface::types::TimestampType createdTime() const override { - return payload_.created_time(); + return reduced_payload_.created_time(); } interface::types::QuorumType quorum() const override { - return payload_.quorum(); + return reduced_payload_.quorum(); + } + + boost::optional> batch_meta() + const override { + return *meta_; } private: @@ -97,9 +107,12 @@ namespace shared_model { const iroha::protocol::Transaction::Payload &payload_{proto_->payload()}; + const iroha::protocol::Transaction::Payload::ReducedPayload + reduced_payload_{proto_->payload().reduced_payload()}; + const Lazy> commands_{[this] { - return std::vector(payload_.commands().begin(), - payload_.commands().end()); + return std::vector(reduced_payload_.commands().begin(), + reduced_payload_.commands().end()); }}; const Lazy blob_{ @@ -108,6 +121,19 @@ namespace shared_model { const Lazy blobTypePayload_{ [this] { return makeBlob(payload_); }}; + const Lazy blobTypeReducedPayload_{ + [this] { return makeBlob(reduced_payload_); }}; + + const Lazy>> meta_{ + [this] () -> boost::optional> { + if (payload_.has_batch()) { + std::shared_ptr b = + std::make_shared(payload_.batch()); + return b; + } + return boost::none; + }}; + const Lazy> signatures_{[this] { auto signatures = proto_->signatures() | boost::adaptors::transformed([](const auto &x) { diff --git a/shared_model/builders/protobuf/builder_templates/transaction_template.hpp b/shared_model/builders/protobuf/builder_templates/transaction_template.hpp index ea5a4a182b..c36e6e6d11 100644 --- a/shared_model/builders/protobuf/builder_templates/transaction_template.hpp +++ b/shared_model/builders/protobuf/builder_templates/transaction_template.hpp @@ -94,7 +94,9 @@ namespace shared_model { template auto addCommand(Transformation t) const { NextBuilder copy = *this; - t(copy.transaction_.mutable_payload()->add_commands()); + t(copy.transaction_.mutable_payload() + ->mutable_reduced_payload() + ->add_commands()); return copy; } @@ -105,19 +107,37 @@ namespace shared_model { auto creatorAccountId( const interface::types::AccountIdType &account_id) const { return transform([&](auto &tx) { - tx.mutable_payload()->set_creator_account_id(account_id); + tx.mutable_payload() + ->mutable_reduced_payload() + ->set_creator_account_id(account_id); + }); + } + + auto batchMeta(interface::types::BatchType type, + std::vector hashes) const { + return transform<0>([&](auto &tx) { + tx.mutable_payload()->mutable_batch()->set_type( + static_cast< + iroha::protocol::Transaction::Payload::BatchMeta::BatchType>( + type)); + for (const auto &hash : hashes) { + tx.mutable_payload()->mutable_batch()->add_tx_hashes( + crypto::toBinaryString(hash)); + } }); } auto createdTime(interface::types::TimestampType created_time) const { return transform([&](auto &tx) { - tx.mutable_payload()->set_created_time(created_time); + tx.mutable_payload()->mutable_reduced_payload()->set_created_time( + created_time); }); } auto quorum(interface::types::QuorumType quorum) const { - return transform( - [&](auto &tx) { tx.mutable_payload()->set_quorum(quorum); }); + return transform([&](auto &tx) { + tx.mutable_payload()->mutable_reduced_payload()->set_quorum(quorum); + }); } auto addAssetQuantity(const interface::types::AssetIdType &asset_id, diff --git a/shared_model/interfaces/common_objects/types.hpp b/shared_model/interfaces/common_objects/types.hpp index 1f50e124c8..a873ae1bca 100644 --- a/shared_model/interfaces/common_objects/types.hpp +++ b/shared_model/interfaces/common_objects/types.hpp @@ -98,6 +98,8 @@ namespace shared_model { const AccountAsset &>; /// Type of the transfer message using DescriptionType = std::string; + + enum class BatchType { ATOMIC = 0, ORDERED = 1}; } // namespace types } // namespace interface } // namespace shared_model diff --git a/shared_model/interfaces/iroha_internal/batch_meta.hpp b/shared_model/interfaces/iroha_internal/batch_meta.hpp new file mode 100644 index 0000000000..a11d244c47 --- /dev/null +++ b/shared_model/interfaces/iroha_internal/batch_meta.hpp @@ -0,0 +1,48 @@ +/** + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef IROHA_SHARED_MODEL_BATCH_META_HPP +#define IROHA_SHARED_MODEL_BATCH_META_HPP + +#include "interfaces/base/model_primitive.hpp" +#include "interfaces/common_objects/types.hpp" + +namespace shared_model { + namespace interface { + + /** + * Representation of fixed point number + */ + class BatchMeta : public ModelPrimitive { + public: + virtual types::BatchType type() const = 0; + + std::string toString() const override { + return detail::PrettyStringBuilder() + .init("BatchMeta") + .append("Type", type() == types::BatchType::ATOMIC ? "ATOMIC" : "ORDERED") + .appendAll(transactionHashes(), + [](auto &hash) { return hash.toString(); }) + .finalize(); + } + /// type of hashes collection + using TransactionHashesType = std::vector; + + /** + * @return Hashes of transactions to fetch + */ + virtual const TransactionHashesType &transactionHashes() const = 0; + /** + * Checks equality of objects inside + * @param rhs - other wrapped value + * @return true, if wrapped objects are same + */ + bool operator==(const ModelType &rhs) const override { + return type() == rhs.type(); + } + }; + } // namespace interface +} // namespace shared_model +#endif // IROHA_SHARED_MODEL_BATCH_META_HPP diff --git a/shared_model/interfaces/transaction.hpp b/shared_model/interfaces/transaction.hpp index 2468b4ff37..f15a022b3d 100644 --- a/shared_model/interfaces/transaction.hpp +++ b/shared_model/interfaces/transaction.hpp @@ -21,6 +21,7 @@ #include "interfaces/base/signable.hpp" #include "interfaces/commands/command.hpp" #include "interfaces/common_objects/types.hpp" +#include "iroha_internal/batch_meta.hpp" #include "utils/string_builder.hpp" namespace shared_model { @@ -30,7 +31,8 @@ namespace shared_model { * Transaction class represent well-formed intent from client to change * state of ledger. */ - class Transaction : public Signable { + using HashProvider = shared_model::crypto::Sha3_256; + class Transaction : public Signable { public: /** * @return creator of transaction @@ -52,6 +54,23 @@ namespace shared_model { */ virtual CommandsType commands() const = 0; + /** + * @return object payload (everything except signatures) + */ + virtual const types::BlobType &reduced_payload() const = 0; + + const types::HashType &reduced_hash() const { + if (reduced_hash_ == boost::none) { + reduced_hash_.emplace(HashProvider::makeHash(reduced_payload())); + } + return *reduced_hash_; + } + /* + * @return Batch Meta if exists + */ + virtual boost::optional> batch_meta() + const = 0; + std::string toString() const override { return detail::PrettyStringBuilder() .init("Transaction") @@ -62,10 +81,14 @@ namespace shared_model { .append("commands") .appendAll(commands(), [](auto &command) { return command.toString(); }) + .append(batch_meta()->get()->toString()) .append("signatures") .appendAll(signatures(), [](auto &sig) { return sig.toString(); }) .finalize(); } + + private: + mutable boost::optional reduced_hash_; }; } // namespace interface diff --git a/shared_model/validators/field_validator.cpp b/shared_model/validators/field_validator.cpp index 34b34e9112..d00dfd9818 100644 --- a/shared_model/validators/field_validator.cpp +++ b/shared_model/validators/field_validator.cpp @@ -351,6 +351,10 @@ namespace shared_model { .str()); } } + void FieldValidator::validateBatchMeta( + shared_model::validation::ReasonsGroupType &reason, + const interface::BatchMeta &batch_meta) + const {} void FieldValidator::validateHeight( shared_model::validation::ReasonsGroupType &reason, diff --git a/shared_model/validators/field_validator.hpp b/shared_model/validators/field_validator.hpp index 02a2bade9f..707aa9914e 100644 --- a/shared_model/validators/field_validator.hpp +++ b/shared_model/validators/field_validator.hpp @@ -143,6 +143,10 @@ namespace shared_model { ReasonsGroupType &reason, const interface::types::DescriptionType &description) const; + void validateBatchMeta( + ReasonsGroupType &reason, + const interface::BatchMeta &description) const; + void validateHeight(ReasonsGroupType &reason, const interface::types::HeightType &height) const; diff --git a/shared_model/validators/transaction_validator.hpp b/shared_model/validators/transaction_validator.hpp index 522d904643..a73e490b87 100644 --- a/shared_model/validators/transaction_validator.hpp +++ b/shared_model/validators/transaction_validator.hpp @@ -266,6 +266,8 @@ namespace shared_model { tx.creatorAccountId()); field_validator_.validateCreatedTime(tx_reason, tx.createdTime()); field_validator_.validateQuorum(tx_reason, tx.quorum()); + if (tx.batch_meta() != boost::none) + field_validator_.validateBatchMeta(tx_reason, **tx.batch_meta()); if (not tx_reason.second.empty()) { answer.addReason(std::move(tx_reason)); diff --git a/test/module/shared_model/backend_proto/shared_proto_transaction_test.cpp b/test/module/shared_model/backend_proto/shared_proto_transaction_test.cpp index 1208c23205..2f20660fb5 100644 --- a/test/module/shared_model/backend_proto/shared_proto_transaction_test.cpp +++ b/test/module/shared_model/backend_proto/shared_proto_transaction_test.cpp @@ -32,7 +32,7 @@ std::string creator_account_id = "admin@test"; */ iroha::protocol::Transaction generateEmptyTransaction() { iroha::protocol::Transaction proto_tx; - auto &payload = *proto_tx.mutable_payload(); + auto &payload = *proto_tx.mutable_payload()->mutable_reduced_payload(); payload.set_creator_account_id(creator_account_id); payload.set_created_time(created_time); payload.set_quorum(1); @@ -64,9 +64,12 @@ iroha::protocol::AddAssetQuantity generateAddAssetQuantity( TEST(ProtoTransaction, Builder) { iroha::protocol::Transaction proto_tx = generateEmptyTransaction(); - std::string asset_id = "coin#test", amount = "10.00"; - auto command = - proto_tx.mutable_payload()->add_commands()->mutable_add_asset_quantity(); + std::string account_id = "admin@test", asset_id = "coin#test", + amount = "10.00"; + auto command = proto_tx.mutable_payload() + ->mutable_reduced_payload() + ->add_commands() + ->mutable_add_asset_quantity(); command->CopyFrom(generateAddAssetQuantity(asset_id)); diff --git a/test/module/shared_model/validators/field_validator_test.cpp b/test/module/shared_model/validators/field_validator_test.cpp index 12ba005b14..8c17d28612 100644 --- a/test/module/shared_model/validators/field_validator_test.cpp +++ b/test/module/shared_model/validators/field_validator_test.cpp @@ -19,6 +19,7 @@ #include #include +#include "block.pb.h" #include #include #include @@ -562,6 +563,17 @@ class FieldValidatorTest : public ValidatorsTest { return all_cases; }(); + std::vector batch_meta_test_cases = [&]() { + iroha::protocol::Transaction::Payload::BatchMeta meta; + meta.set_type(iroha::protocol::Transaction::Payload::BatchMeta::BatchType:: + Transaction_Payload_BatchMeta_BatchType_ATOMIC); + meta.add_tx_hashes("tst"); + std::vector all_cases; + all_cases.push_back(makeTestCase( + "batch meta test", &FieldValidatorTest::batch_meta, meta, true, "")); + return all_cases; + }(); + std::vector precision_test_cases{ makeValidCase(&FieldValidatorTest::precision, 0), makeValidCase(&FieldValidatorTest::precision, 1), @@ -667,7 +679,13 @@ class FieldValidatorTest : public ValidatorsTest { makeValidator("description", &FieldValidator::validateDescription, &FieldValidatorTest::description, - description_test_cases)}; + description_test_cases), + makeTransformValidator( + "batch", + &FieldValidator::validateBatchMeta, + &FieldValidatorTest::batch_meta, + [](auto &&x) { return shared_model::proto::BatchMeta(x); }, + batch_meta_test_cases)}; }; /** @@ -685,7 +703,7 @@ TEST_F(FieldValidatorTest, CommandFieldsValidation) { [] { return iroha::protocol::Command::descriptor(); }, [&](auto field) { // Add new command to transaction - auto command = payload->add_commands(); + auto command = payload->mutable_reduced_payload()->add_commands(); // // Set concrete type for new command return command->GetReflection()->MutableMessage(command, field); }, @@ -700,18 +718,11 @@ TEST_F(FieldValidatorTest, CommandFieldsValidation) { * meaningful message */ TEST_F(FieldValidatorTest, TransactionFieldsValidation) { - iroha::protocol::Transaction proto_tx; - proto_tx.add_signatures(); // at least one signature in message - + auto proto_tx = std::make_shared(); + proto_tx->add_signatures(); // at least one signature in message + proto_tx->mutable_payload()->mutable_reduced_payload()->add_commands(); // iterate over all fields in transaction - iterateContainer( - [] { return iroha::protocol::Transaction::descriptor(); }, - [&](auto field) { - return field->is_repeated() - ? proto_tx.GetReflection()->MutableRepeatedMessage( - &proto_tx, field, 0) - : proto_tx.GetReflection()->MutableMessage(&proto_tx, field); - }, + iterateContainerRecursive(proto_tx, field_validators, [this](auto field, auto transaction_field) { this->runTestCases(field); }, [] {}); } diff --git a/test/module/shared_model/validators/transaction_validator_test.cpp b/test/module/shared_model/validators/transaction_validator_test.cpp index 6e37db4aa1..83c98ac511 100644 --- a/test/module/shared_model/validators/transaction_validator_test.cpp +++ b/test/module/shared_model/validators/transaction_validator_test.cpp @@ -49,7 +49,8 @@ class TransactionValidatorTest : public ValidatorsTest { */ TEST_F(TransactionValidatorTest, EmptyTransactionTest) { auto tx = generateEmptyTransaction(); - tx.mutable_payload()->set_created_time(created_time); + tx.mutable_payload()->mutable_reduced_payload()->set_created_time( + created_time); auto result = proto::Transaction(iroha::protocol::Transaction(tx)); auto answer = transaction_validator.validate(result); ASSERT_EQ(answer.getReasonsMap().size(), 1); @@ -62,12 +63,14 @@ TEST_F(TransactionValidatorTest, EmptyTransactionTest) { */ TEST_F(TransactionValidatorTest, InvalidCreateRolePermission) { auto tx = generateEmptyTransaction(); - tx.mutable_payload()->set_created_time(created_time); + tx.mutable_payload()->mutable_reduced_payload()->set_created_time( + created_time); iroha::protocol::Command cmd; cmd.mutable_create_role()->set_role_name("role"); cmd.mutable_create_role()->add_permissions( static_cast(-1)); - *tx.mutable_payload()->add_commands() = std::move(cmd); + *tx.mutable_payload()->mutable_reduced_payload()->add_commands() = + std::move(cmd); shared_model::validation::DefaultTransactionValidator transaction_validator; auto result = proto::Transaction(iroha::protocol::Transaction(tx)); auto answer = transaction_validator.validate(result); @@ -81,45 +84,40 @@ TEST_F(TransactionValidatorTest, InvalidCreateRolePermission) { */ TEST_F(TransactionValidatorTest, StatelessValidTest) { iroha::protocol::Transaction tx = generateEmptyTransaction(); - tx.mutable_payload()->set_creator_account_id(account_id); - tx.mutable_payload()->set_created_time(created_time); + tx.mutable_payload()->mutable_reduced_payload()->set_creator_account_id( + account_id); + tx.mutable_payload()->mutable_reduced_payload()->set_created_time( + created_time); auto payload = tx.mutable_payload(); // Iterate through all command types, filling command fields with valid values - iterateContainer([] { return iroha::protocol::Command::descriptor(); }, - [&](auto field) { - // Add new command to transaction - auto command = payload->add_commands(); - // Set concrete type for new command - return command->GetReflection()->MutableMessage(command, - field); - }, - [this](auto field, auto command) { - // Will throw key exception in case new field is added - field_setters.at(field->name())( - command->GetReflection(), command, field); - }, - [] {}); + iterateContainer( + [] { return iroha::protocol::Command::descriptor(); }, + [&](auto field) { + // Add new command to transaction + auto command = payload->mutable_reduced_payload()->add_commands(); + // Set concrete type for new command + return command->GetReflection()->MutableMessage(command, field); + }, + [this](auto field, auto command) { + // Will throw key exception in case new field is added + field_setters.at(field->name())( + command->GetReflection(), command, field); + }, + [] {}); auto result = proto::Transaction(iroha::protocol::Transaction(tx)); auto answer = transaction_validator.validate(result); ASSERT_FALSE(answer.hasErrors()) << answer.reason(); } - /** - * @given Protobuf transaction object with unset command - * @when validate is called - * @then there is a error returned + * @given transaction made of commands with valid fields + * @when commands validation is invoked + * @then answer has no errors */ -TEST_F(TransactionValidatorTest, UnsetCommand) { - iroha::protocol::Transaction tx = generateEmptyTransaction(); - tx.mutable_payload()->set_creator_account_id(account_id); - tx.mutable_payload()->set_created_time(created_time); - auto answer = transaction_validator.validate(proto::Transaction(tx)); - tx.mutable_payload()->add_commands(); - ASSERT_TRUE(answer.hasErrors()); -} +TEST_F(TransactionValidatorTest, BatchValidTest) { + std::string creator_account_id = "admin@test"; /** * @given transaction made of commands with invalid fields @@ -132,22 +130,22 @@ TEST_F(TransactionValidatorTest, StatelessInvalidTest) { auto payload = tx.mutable_payload(); iroha::ts64_t invalid_time = 10000000000ull; - payload->set_created_time(invalid_time); + payload->mutable_reduced_payload()->set_created_time(invalid_time); // create commands from default constructors, which will have empty, therefore // invalid values - iterateContainer([] { return iroha::protocol::Command::descriptor(); }, - [&](auto field) { - // Add new command to transaction - auto command = payload->add_commands(); - // Set concrete type for new command - return command->GetReflection()->MutableMessage(command, - field); - }, - [](auto, auto) { - // Note that no fields are set - }, - [] {}); + iterateContainer( + [] { return iroha::protocol::Command::descriptor(); }, + [&](auto field) { + // Add new command to transaction + auto command = payload->mutable_reduced_payload()->add_commands(); + // Set concrete type for new command + return command->GetReflection()->MutableMessage(command, field); + }, + [](auto, auto) { + // Note that no fields are set + }, + [] {}); auto result = proto::Transaction(iroha::protocol::Transaction(tx)); auto answer = transaction_validator.validate(result); diff --git a/test/module/shared_model/validators/validators_fixture.hpp b/test/module/shared_model/validators/validators_fixture.hpp index cfe443b88c..5f69ce8259 100644 --- a/test/module/shared_model/validators/validators_fixture.hpp +++ b/test/module/shared_model/validators/validators_fixture.hpp @@ -23,6 +23,7 @@ #include #include +#include "block.pb.h" #include "datetime/time.hpp" #include "interfaces/permissions.hpp" #include "primitive.pb.h" @@ -118,6 +119,51 @@ class ValidatorsTest : public ::testing::Test { validator(); }); } + // TODO: IR-1490 02.07.2018 Rewrite interator for all containters + /** + * Iterate the container recursively (transaction or query), + * generating concrete subtypes + * and doing operation on concrete subtype fields. Call validator after each + * subtype + * @tparam FieldMap map of validators which are not nessery to traverse but + * validate as a whole + * @tparam FieldOp field operation type + * @tparam Validator validator type + * @param m protobuf message to iterate + * @param field_validators map of validators which are not nessery to traverse + * but validate as a whole + * @param field_op field operation callable object + * @param validator validator callable object + */ + template + void iterateContainerRecursive(std::shared_ptr m, + FieldMap &field_validators, + FieldOp &&field_op, + Validator &&validator) { + const google::protobuf::Descriptor *desc = m->GetDescriptor(); + const google::protobuf::Reflection *refl = m->GetReflection(); + // Get field descriptor for concrete type + const auto &range = boost::irange(0, desc->field_count()) + | boost::adaptors::transformed([&](auto i) { return desc->field(i); }); + // Iterate through all concrete types + boost::for_each(range, [&](auto field) { + if (field->type() != google::protobuf::FieldDescriptor::TYPE_MESSAGE + or field_validators.count(field->name())) { + field_op(field, m); + validator(); + } else { + const google::protobuf::Message *mfield = field->is_repeated() + ? refl->MutableRepeatedMessage(m.get(), field, 0) + : refl->MutableMessage(m.get(), field); + google::protobuf::Message *mcopy = mfield->New(); + mcopy->CopyFrom(*mfield); + void *ptr = new std::shared_ptr(mcopy); + std::shared_ptr *m = + static_cast *>(ptr); + iterateContainerRecursive(*m, field_validators, field_op, validator); + } + }); + } protected: void SetUp() override { @@ -168,6 +214,7 @@ class ValidatorsTest : public ::testing::Test { std::string description; std::string public_key; std::string hash; + iroha::protocol::Transaction::Payload::BatchMeta batch_meta; shared_model::interface::permissions::Role model_role_permission; shared_model::interface::permissions::Grantable model_grantable_permission; iroha::protocol::RolePermission role_permission; From f79b2ff39f553f5cf78000f4cc04dfd5efbf7754 Mon Sep 17 00:00:00 2001 From: Igor Egorov Date: Tue, 3 Jul 2018 17:44:01 +0300 Subject: [PATCH 22/97] Improve Findgrpc.cmake (#1518) Signed-off-by: Igor Egorov Before the change it was not possible to build grpc_grpc target with protobuf installed in a system and without installed grpc. --- cmake/Modules/Findgrpc.cmake | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/cmake/Modules/Findgrpc.cmake b/cmake/Modules/Findgrpc.cmake index 2dde985c0f..a9fd9923e3 100644 --- a/cmake/Modules/Findgrpc.cmake +++ b/cmake/Modules/Findgrpc.cmake @@ -32,6 +32,12 @@ set(URL https://github.com/grpc/grpc) set(VERSION bd44e485f69d70ca4095cea92decd98de3892aa6) # Release 1.11.0 set_target_description(grpc "Remote Procedure Call library" ${URL} ${VERSION}) +if (NOT protobuf_FOUND) + set(PROTO_DEP -DgRPC_PROTOBUF_PACKAGE_TYPE=CONFIG -DProtobuf_DIR=${EP_PREFIX}/src/google_protobuf-build/lib/cmake/protobuf) +else () + set(PROTO_DEP -DgRPC_PROTOBUF_PACKAGE_TYPE=MODULE) +endif () + if (NOT grpc_FOUND) find_package(Git REQUIRED) externalproject_add(grpc_grpc @@ -39,8 +45,7 @@ if (NOT grpc_FOUND) GIT_TAG ${VERSION} CMAKE_ARGS -DgRPC_PROTOBUF_PROVIDER=package - -DgRPC_PROTOBUF_PACKAGE_TYPE=CONFIG - -DProtobuf_DIR=${EP_PREFIX}/src/google_protobuf-build/lib/cmake/protobuf + ${PROTO_DEP} -DgRPC_ZLIB_PROVIDER=package -DBUILD_SHARED_LIBS=ON BUILD_BYPRODUCTS From d87c20457668c9470f5677e5f5f2f7e460f79ee1 Mon Sep 17 00:00:00 2001 From: Anatoly Date: Tue, 3 Jul 2018 23:28:43 +0300 Subject: [PATCH 23/97] Revert "New CI pipeline (#1392)" (#1533) Signed-off-by: Anatoly --- .jenkinsci/build-coverage.groovy | 15 - .jenkinsci/debug-build.groovy | 160 +++--- .jenkinsci/docker-cleanup.groovy | 26 +- .jenkinsci/docker-pull-or-build.groovy | 44 +- .jenkinsci/enums.groovy | 12 - .jenkinsci/github-api.groovy | 137 ----- .jenkinsci/linux-post-step.groovy | 20 + .jenkinsci/mac-debug-build.groovy | 44 -- .jenkinsci/mac-release-build.groovy | 8 +- .jenkinsci/notifications.groovy | 61 --- .jenkinsci/post-step.groovy | 40 -- .jenkinsci/pre-build.groovy | 14 - .jenkinsci/release-build.groovy | 21 +- .jenkinsci/selected-branches-coverage.groovy | 13 + .jenkinsci/set-parallelism.groovy | 19 - .jenkinsci/test-launcher.groovy | 41 -- Jenkinsfile | 524 ++++++++----------- 17 files changed, 348 insertions(+), 851 deletions(-) delete mode 100644 .jenkinsci/build-coverage.groovy delete mode 100644 .jenkinsci/enums.groovy delete mode 100644 .jenkinsci/github-api.groovy create mode 100644 .jenkinsci/linux-post-step.groovy delete mode 100644 .jenkinsci/mac-debug-build.groovy delete mode 100644 .jenkinsci/notifications.groovy delete mode 100644 .jenkinsci/post-step.groovy delete mode 100644 .jenkinsci/pre-build.groovy create mode 100644 .jenkinsci/selected-branches-coverage.groovy delete mode 100644 .jenkinsci/set-parallelism.groovy delete mode 100644 .jenkinsci/test-launcher.groovy diff --git a/.jenkinsci/build-coverage.groovy b/.jenkinsci/build-coverage.groovy deleted file mode 100644 index 2e9a050694..0000000000 --- a/.jenkinsci/build-coverage.groovy +++ /dev/null @@ -1,15 +0,0 @@ -#!/usr/bin/env groovy - -def checkCoverageConditions() { - // trigger coverage if branch is master, or it is a open PR commit, or a merge request - def branch_coverage = ['master'] - - if ( params.coverage ) { - return true - } - else { - return env.GIT_LOCAL_BRANCH in branch_coverage || INITIAL_COMMIT_PR == "true" || MERGE_CONDITIONS_SATISFIED == "true" - } -} - -return this diff --git a/.jenkinsci/debug-build.groovy b/.jenkinsci/debug-build.groovy index 52ad96d383..47087c07eb 100644 --- a/.jenkinsci/debug-build.groovy +++ b/.jenkinsci/debug-build.groovy @@ -4,20 +4,26 @@ def doDebugBuild(coverageEnabled=false) { def dPullOrBuild = load ".jenkinsci/docker-pull-or-build.groovy" def manifest = load ".jenkinsci/docker-manifest.groovy" def pCommit = load ".jenkinsci/previous-commit.groovy" - def setter = load ".jenkinsci/set-parallelism.groovy" - def parallelism = setter.setParallelism(params.PARALLELISM) + def parallelism = params.PARALLELISM def platform = sh(script: 'uname -m', returnStdout: true).trim() - def prevCommit = pCommit.previousCommitOrCurrent() + def previousCommit = pCommit.previousCommitOrCurrent() // params are always null unless job is started // this is the case for the FIRST build only. // So just set this to same value as default. // This is a known bug. See https://issues.jenkins-ci.org/browse/JENKINS-41929 - + if (!parallelism) { + parallelism = 4 + } + if (env.NODE_NAME.contains('arm7')) { + parallelism = 1 + } + sh "docker network create ${env.IROHA_NETWORK}" def iC = dPullOrBuild.dockerPullOrUpdate("${platform}-develop-build", "${env.GIT_RAW_BASE_URL}/${env.GIT_COMMIT}/docker/develop/Dockerfile", - "${env.GIT_RAW_BASE_URL}/${prevCommit}/docker/develop/Dockerfile", + "${env.GIT_RAW_BASE_URL}/${previousCommit}/docker/develop/Dockerfile", "${env.GIT_RAW_BASE_URL}/develop/docker/develop/Dockerfile", ['PARALLELISM': parallelism]) + if (GIT_LOCAL_BRANCH == 'develop' && manifest.manifestSupportEnabled()) { manifest.manifestCreate("${DOCKER_REGISTRY_BASENAME}:develop-build", ["${DOCKER_REGISTRY_BASENAME}:x86_64-develop-build", @@ -36,61 +42,6 @@ def doDebugBuild(coverageEnabled=false) { manifest.manifestPush("${DOCKER_REGISTRY_BASENAME}:develop-build", login, password) } } - - iC.inside("" - + " -e IROHA_POSTGRES_HOST=${env.IROHA_POSTGRES_HOST}" - + " -e IROHA_POSTGRES_PORT=${env.IROHA_POSTGRES_PORT}" - + " -e IROHA_POSTGRES_USER=${env.IROHA_POSTGRES_USER}" - + " -e IROHA_POSTGRES_PASSWORD=${env.IROHA_POSTGRES_PASSWORD}" - + " -v ${CCACHE_DIR}:${CCACHE_DIR}") { - def scmVars = checkout scm - def cmakeOptions = "" - - if ( coverageEnabled ) { - cmakeOptions = " -DCOVERAGE=ON " - } - env.IROHA_VERSION = "0x${scmVars.GIT_COMMIT}" - env.IROHA_HOME = "/opt/iroha" - env.IROHA_BUILD = "${env.IROHA_HOME}/build" - - sh """ - ccache --version - ccache --show-stats - ccache --zero-stats - ccache --max-size=5G - """ - sh """ - cmake \ - -DTESTING=ON \ - -H. \ - -Bbuild \ - -DCMAKE_BUILD_TYPE=${params.build_type} \ - -DIROHA_VERSION=${env.IROHA_VERSION} \ - ${cmakeOptions} - """ - sh "cmake --build build -- -j${parallelism}" - sh "ccache --show-stats" - } -} - -// do not implement any checks as coverage runs only on x86_64 -def doPreCoverageStep() { - sh "docker load -i ${JENKINS_DOCKER_IMAGE_DIR}/${DOCKER_IMAGE_FILE}" - def iC = docker.image("${DOCKER_AGENT_IMAGE}") - iC.inside() { - sh "cmake --build build --target coverage.init.info" - } -} - -// run specified test categories -def doTestStep(testList) { - if (env.NODE_NAME.contains('x86_64')) { - sh "docker load -i ${JENKINS_DOCKER_IMAGE_DIR}/${DOCKER_IMAGE_FILE}" - } - - def iC = docker.image("${DOCKER_AGENT_IMAGE}") - sh "docker network create ${env.IROHA_NETWORK}" - docker.image('postgres:9.5').withRun("" + " -e POSTGRES_USER=${env.IROHA_POSTGRES_USER}" + " -e POSTGRES_PASSWORD=${env.IROHA_POSTGRES_PASSWORD}" @@ -101,44 +52,63 @@ def doTestStep(testList) { + " -e IROHA_POSTGRES_PORT=${env.IROHA_POSTGRES_PORT}" + " -e IROHA_POSTGRES_USER=${env.IROHA_POSTGRES_USER}" + " -e IROHA_POSTGRES_PASSWORD=${env.IROHA_POSTGRES_PASSWORD}" - + " --network=${env.IROHA_NETWORK}") { - def testExitCode = sh(script: """cd build && ctest --output-on-failure -R '${testList}' """, returnStatus: true) - if (testExitCode != 0) { - currentBuild.result = "UNSTABLE" + + " --network=${env.IROHA_NETWORK}" + + " -v /var/jenkins/ccache:${CCACHE_DIR}" + + " -v /tmp/${GIT_COMMIT}-${BUILD_NUMBER}:/tmp/${GIT_COMMIT}") { + + def scmVars = checkout scm + def cmakeOptions = "" + if ( coverageEnabled ) { + cmakeOptions = " -DCOVERAGE=ON " + } + env.IROHA_VERSION = "0x${scmVars.GIT_COMMIT}" + env.IROHA_HOME = "/opt/iroha" + env.IROHA_BUILD = "${env.IROHA_HOME}/build" + + sh """ + ccache --version + ccache --show-stats + ccache --zero-stats + ccache --max-size=5G + """ + sh """ + cmake \ + -DTESTING=ON \ + -H. \ + -Bbuild \ + -DCMAKE_BUILD_TYPE=Debug \ + -DIROHA_VERSION=${env.IROHA_VERSION} \ + ${cmakeOptions} + """ + sh "cmake --build build -- -j${parallelism}" + sh "ccache --show-stats" + if ( coverageEnabled ) { + sh "cmake --build build --target coverage.init.info" + } + def testExitCode = sh(script: """cd build && ctest --output-on-failure""", returnStatus: true) + if (testExitCode != 0) { + currentBuild.result = "UNSTABLE" + } + if ( coverageEnabled ) { + sh "cmake --build build --target cppcheck" + // Sonar + if (env.CHANGE_ID != null) { + sh """ + sonar-scanner \ + -Dsonar.github.disableInlineComments \ + -Dsonar.github.repository='${DOCKER_REGISTRY_BASENAME}' \ + -Dsonar.analysis.mode=preview \ + -Dsonar.login=${SONAR_TOKEN} \ + -Dsonar.projectVersion=${BUILD_TAG} \ + -Dsonar.github.oauth=${SORABOT_TOKEN} \ + -Dsonar.github.pullRequest=${CHANGE_ID} + """ } + sh "cmake --build build --target coverage.info" + sh "python /tmp/lcov_cobertura.py build/reports/coverage.info -o build/reports/coverage.xml" + cobertura autoUpdateHealth: false, autoUpdateStability: false, coberturaReportFile: '**/build/reports/coverage.xml', conditionalCoverageTargets: '75, 50, 0', failUnhealthy: false, failUnstable: false, lineCoverageTargets: '75, 50, 0', maxNumberOfBuilds: 50, methodCoverageTargets: '75, 50, 0', onlyStable: false, zoomCoverageChart: false } } -} - -// do cobertura analysis -// same as for doPreCoverageStep() -def doPostCoverageCoberturaStep() { - sh "docker load -i ${JENKINS_DOCKER_IMAGE_DIR}/${DOCKER_IMAGE_FILE}" - def iC = docker.image("${DOCKER_AGENT_IMAGE}") - iC.inside() { - sh "cmake --build build --target coverage.info" - sh "python /tmp/lcov_cobertura.py build/reports/coverage.info -o build/reports/coverage.xml" - cobertura autoUpdateHealth: false, autoUpdateStability: false, coberturaReportFile: '**/build/reports/coverage.xml', conditionalCoverageTargets: '75, 50, 0', failUnhealthy: false, failUnstable: false, lineCoverageTargets: '75, 50, 0', maxNumberOfBuilds: 50, methodCoverageTargets: '75, 50, 0', onlyStable: false, zoomCoverageChart: false - } -} - -// do cppcheck analysis -// same as for doPreCoverageStep() -def doPostCoverageSonarStep() { - sh "docker load -i ${JENKINS_DOCKER_IMAGE_DIR}/${DOCKER_IMAGE_FILE}" - def iC = docker.image("${DOCKER_AGENT_IMAGE}") - iC.inside() { - sh "cmake --build build --target cppcheck" - sh """ - sonar-scanner \ - -Dsonar.github.disableInlineComments \ - -Dsonar.github.repository='hyperledger/iroha' \ - -Dsonar.analysis.mode=preview \ - -Dsonar.login=${SONAR_TOKEN} \ - -Dsonar.projectVersion=${BUILD_TAG} \ - -Dsonar.github.oauth=${SORABOT_TOKEN} \ - -Dsonar.github.pullRequest=${CHANGE_ID} - """ } } diff --git a/.jenkinsci/docker-cleanup.groovy b/.jenkinsci/docker-cleanup.groovy index b4eda7a04c..c789383b28 100644 --- a/.jenkinsci/docker-cleanup.groovy +++ b/.jenkinsci/docker-cleanup.groovy @@ -1,23 +1,17 @@ #!/usr/bin/env groovy -// remove docker network and stale images def doDockerCleanup() { - sh """ - # Check whether the image is the last-standing man - # i.e., no other tags exist for this image - docker rmi \$(docker images --no-trunc --format '{{.Repository}}:{{.Tag}}\\t{{.ID}}' | grep \$(docker images --no-trunc --format '{{.ID}}' ${iC.id}) | head -n -1 | cut -f 1) || true - """ -} - -// cleanup docker network created for the test stage -def doDockerNetworkCleanup() { - sh "docker network rm ${env.IROHA_NETWORK}" -} -// cleanup docker images which weren't used for more that 20 days and image for this PR in case of successful PR -def doStaleDockerImagesCleanup() { - sh "find ${JENKINS_DOCKER_IMAGE_DIR} -type f -mtime +20 -exec rm -f {} \\;" - sh "rm -f ${JENKINS_DOCKER_IMAGE_DIR}/${DOCKER_IMAGE_FILE}" + sh """ + # Check whether the image is the last-standing man + # i.e., no other tags exist for this image + docker rmi \$(docker images --no-trunc --format '{{.Repository}}:{{.Tag}}\\t{{.ID}}' | grep \$(docker images --no-trunc --format '{{.ID}}' ${iC.id}) | head -n -1 | cut -f 1) || true + sleep 5 + docker network rm $IROHA_NETWORK || true + #remove folder with iroha.deb package and Dockerfiles + rm -rf /tmp/${env.GIT_COMMIT}-${BUILD_NUMBER} + rm -rf /tmp/${env.GIT_COMMIT} + """ } return this diff --git a/.jenkinsci/docker-pull-or-build.groovy b/.jenkinsci/docker-pull-or-build.groovy index 9d74931db8..d699ac40bb 100644 --- a/.jenkinsci/docker-pull-or-build.groovy +++ b/.jenkinsci/docker-pull-or-build.groovy @@ -19,52 +19,31 @@ def buildOptionsString(options) { def dockerPullOrUpdate(imageName, currentDockerfileURL, previousDockerfileURL, referenceDockerfileURL, buildOptions=null) { buildOptions = buildOptionsString(buildOptions) - def uploadExitCode = 0 - // GIT_PREVIOUS_COMMIT is null for first PR build def commit = sh(script: "echo ${GIT_LOCAL_BRANCH} | md5sum | cut -c 1-8", returnStdout: true).trim() - DOCKER_IMAGE_FILE = commit - if (remoteFilesDiffer(currentDockerfileURL, previousDockerfileURL)) { // Dockerfile has been changed compared to the previous commit // Worst case scenario. We cannot count on the local cache // because Dockerfile may contain apt-get entries that would try to update // from invalid (stale) addresses iC = docker.build("${DOCKER_REGISTRY_BASENAME}:${commit}-${BUILD_NUMBER}", "${buildOptions} --no-cache -f /tmp/${env.GIT_COMMIT}/f1 /tmp/${env.GIT_COMMIT}") - if (env.NODE_NAME.contains('x86_64')) { - sh "docker save -o ${JENKINS_DOCKER_IMAGE_DIR}/${DOCKER_IMAGE_FILE} ${DOCKER_REGISTRY_BASENAME}:${commit}-${BUILD_NUMBER}" - } } else { - // upload reference image (hyperledger/iroha:develop-build) (required in all use-cases in this execution branch) - if (env.NODE_NAME.contains('x86_64')) { - uploadExitCode = sh(script: "docker load -i ${JENKINS_DOCKER_IMAGE_DIR}/${imageName}", returnStatus: true) - if (uploadExitCode != 0) { - sh "echo 'Reference image ${DOCKER_REGISTRY_BASENAME}:${imageName} doesn't exist on the EFS" - } - } - def pullExitCode = sh(script: "docker pull ${DOCKER_REGISTRY_BASENAME}:${imageName}", returnStatus: true) - if (pullExitCode != 0) { - sh "echo 'Reference image ${DOCKER_REGISTRY_BASENAME}:${imageName} doesn't exist on the dockerhub" - } - else { - // save reference docker image into the file when it is doesn't exist on the EFS - if (uploadExitCode != 0) { - sh "docker save -o ${JENKINS_DOCKER_IMAGE_DIR}/${imageName} ${DOCKER_REGISTRY_BASENAME}:${imageName}" - } - } // first commit in this branch or Dockerfile modified if (remoteFilesDiffer(currentDockerfileURL, referenceDockerfileURL)) { // if we're lucky to build on the same agent, image will be built using cache - // for x86 download reference image and start build - iC = docker.build("${DOCKER_REGISTRY_BASENAME}:${commit}-${BUILD_NUMBER}", "${buildOptions} -f /tmp/${env.GIT_COMMIT}/f1 /tmp/${env.GIT_COMMIT}") - if (env.NODE_NAME.contains('x86_64')) { - sh "docker save -o /tmp/docker/${imageName} ${DOCKER_REGISTRY_BASENAME}:${commit}-${BUILD_NUMBER}" - } + iC = docker.build("${DOCKER_REGISTRY_BASENAME}:${commit}-${BUILD_NUMBER}", "$buildOptions -f /tmp/${env.GIT_COMMIT}/f1 /tmp/${env.GIT_COMMIT}") } else { - // now we get reference image from the file (pull from dockerhub) - iC = docker.image("${DOCKER_REGISTRY_BASENAME}:${imageName}") - DOCKER_IMAGE_FILE = imageName + // try pulling image from Dockerhub, probably image is already there + def testExitCode = sh(script: "docker pull ${DOCKER_REGISTRY_BASENAME}:${imageName}", returnStatus: true) + if (testExitCode != 0) { + // image does not (yet) exist on Dockerhub. Build it + iC = docker.build("${DOCKER_REGISTRY_BASENAME}:${commit}-${BUILD_NUMBER}", "$buildOptions --no-cache -f /tmp/${env.GIT_COMMIT}/f1 /tmp/${env.GIT_COMMIT}") + } + else { + // no difference found compared to both previous and reference Dockerfile + iC = docker.image("${DOCKER_REGISTRY_BASENAME}:${imageName}") + } } } if (GIT_LOCAL_BRANCH ==~ /develop|master/) { @@ -72,7 +51,6 @@ def dockerPullOrUpdate(imageName, currentDockerfileURL, previousDockerfileURL, r iC.push(imageName) } } - DOCKER_AGENT_IMAGE = iC.imageName() return iC } diff --git a/.jenkinsci/enums.groovy b/.jenkinsci/enums.groovy deleted file mode 100644 index 32e383c6e6..0000000000 --- a/.jenkinsci/enums.groovy +++ /dev/null @@ -1,12 +0,0 @@ -// types of tests provided by the developers (can be found at the CMakeLists.txt files) -enum TestTypes { - module(0), integration(1), system(2), cmake(3), regression(4), benchmark(5), framework(6) - TestTypes(int order) { - this.order = order - } - private final int order - int getOrder() { - order - } -} -return this diff --git a/.jenkinsci/github-api.groovy b/.jenkinsci/github-api.groovy deleted file mode 100644 index 9441daf68f..0000000000 --- a/.jenkinsci/github-api.groovy +++ /dev/null @@ -1,137 +0,0 @@ -#!/usr/bin/env groovy - -// list of the pull requests reviews status on github -enum GithubPRStatus { - APPROVED, CHANGES_REQUESTED, REVIEW_REQUESTED -} -// list of the possible merge strategies -enum MergeTarget { - merge, squash -} -// list of supportable target branches for automated merge (by CI) -enum ChangeTarget { - master, develop, trunk -} - -// map with user:review_status -pullRequestReviewers = [:] - -// merges pull request using GitHub API in case it meets the merging criteria -def mergePullRequest() { - if ( ! ( checkMergeAcceptance() ) ) { return false } - withCredentials([string(credentialsId: 'jenkins-integration-test', variable: 'sorabot')]) { - def slurper = new groovy.json.JsonSlurperClassic() - def commitTitle = "" - def commitMessage = "" - def mergeMethod = getMergeMethod() - def jsonResponseMerge = sh(script: """ - curl -H "Authorization: token ${sorabot}" \ - -H "Accept: application/vnd.github.v3+json" \ - -X PUT --data '{"commit_title":"${commitTitle}","commit_message":"${commitMessage}","sha":"${env.GIT_COMMIT}","merge_method":"${mergeMethod}"}' \ - -w "%{http_code}" https://api.github.com/repos/hyperledger/iroha/pulls/${CHANGE_ID}/merge""", returnStdout: true) - def githubResponse = sh(script:"""set +x; printf '%s\n' "${jsonResponseMerge}" | tail -n 1; set -x""", returnStdout: true).trim() - jsonResponseMerge = slurper.parseText(jsonResponseMerge) - if (jsonResponseMerge.merged != "true" || !(githubResponse ==~ "200")) { - echo jsonResponseMerge.message - return false - } - return true - } -} - -// check merge acceptance by: -// - at least 2 "approved and NO "changes_requested" in reviews -// - e-mail of the commit does not match Jenkins user who launched this build -def checkMergeAcceptance() { - def approvalsRequired = 0 - // fill the map of user:review_status - getPullRequestReviewers() - pullRequestReviewers.each{ user, review_status -> - if (review_status == GithubPRStatus.APPROVED.toString()) { - approvalsRequired += 1 - } - else if (review_status == GithubPRStatus.CHANGES_REQUESTED.toString()) { - return false - } - } - if (approvalsRequired < 2) { - sh "echo 'Merge failed. Get more PR approvals before merging'" - return false - } - return true -} - -// returns merge method based on target branch (squash&merge vs merge) -def getMergeMethod() { - if (env.CHANGE_TARGET == ChangeTarget.master.toString()) { - return MergeTarget.merge.toString() - } - else { - return MergeTarget.squash.toString() - } -} - -// fill the pullRequestReviews map with user:review status -def getPullRequestReviewers() { - def slurper = new groovy.json.JsonSlurperClassic() - // if there more than 1 page of "reviews" in PR (it happens due to huge amount of comments) - def reviewPaging = sh(script: """curl -I https://api.github.com/repos/hyperledger/iroha/pulls/1392/reviews | grep -E "^Link:" | wc -l""", returnStdout: true) - def reviewPagesCount = "1" - if (reviewPaging.toInteger()) { - reviewPagesCount = sh(script: """ curl -I https://api.github.com/repos/hyperledger/iroha/pulls/1392/reviews | grep -E "^Link:" | awk 'BEGIN { FS = "page" } { print \$NF }' | awk -F"=" '{print \$2}' | awk -F">" '{print \$1}' """, returnStdout: true) - } - // start the loop to request pages sequentially - for(pageID in (1..reviewPagesCount.toInteger())) { - def jsonResponseReview = sh(script: """ - curl https://api.github.com/repos/hyperledger/iroha/pulls/${CHANGE_ID}/reviews?page=${pageID} - """, returnStdout: true).trim() - // process returned reviews. add/update user:review_status to the map - jsonResponseReview = slurper.parseText(jsonResponseReview) - if (jsonResponseReview.size() > 0) { - jsonResponseReview.each { - if (it.state.toString() in [GithubPRStatus.APPROVED.toString(), GithubPRStatus.CHANGES_REQUESTED.toString()]) { - pullRequestReviewers[it.user.login.toString()] = it.state.toString() - } - } - } - } - // get requested reviewers (those who did not review this PR yet) - def jsonResponseReviewers = sh(script: """ - curl https://api.github.com/repos/hyperledger/iroha/pulls/${CHANGE_ID}/requested_reviewers - """, returnStdout: true).trim() - jsonResponseReviewers = slurper.parseText(jsonResponseReviewers) - if (jsonResponseReviewers.size() > 0) { - jsonResponseReviewers.users.each { - pullRequestReviewers[it.login] = GithubPRStatus.REVIEW_REQUESTED.toString() - } - } -} - -// returns PR reviewers in the form of "@reviewer1 @reviewer2 ... @reviewerN" to mention PR reviewers about build result -def getUsersMentionList() { - getPullRequestReviewers() - def ghUsersList = '' - pullRequestReviewers.each{ user, review_status -> ghUsersList = ["@${user}", ghUsersList].join(' ') } - return ghUsersList -} - -// post a comment on PR via GitHub API -def writePullRequestComment() { - def ghUsersList = getUsersMentionList() - withCredentials([string(credentialsId: 'jenkins-integration-test', variable: 'sorabot')]) { - def slurper = new groovy.json.JsonSlurperClassic() - def jsonResponseComment = sh(script: """ - curl -H "Authorization: token ${sorabot}" \ - -H "Accept: application/vnd.github.v3+json" \ - -X POST --data '{"body":"${ghUsersList} commit ${env.GIT_COMMIT} build status: ${currentBuild.currentResult}. build URL: ${BUILD_URL}"}' \ - -w "%{http_code}" https://api.github.com/repos/hyperledger/iroha/issues/${CHANGE_ID}/comments - """, returnStdout: true).trim() - def githubResponse = sh(script:"""set +x; printf '%s\n' "${jsonResponseComment}" | tail -n 1 ; set -x""", returnStdout: true).trim() - if (githubResponse ==~ "201") { - return true - } - } - return false -} - -return this diff --git a/.jenkinsci/linux-post-step.groovy b/.jenkinsci/linux-post-step.groovy new file mode 100644 index 0000000000..c28f6f6022 --- /dev/null +++ b/.jenkinsci/linux-post-step.groovy @@ -0,0 +1,20 @@ +def linuxPostStep() { + timeout(time: 600, unit: "SECONDS") { + try { + if (currentBuild.currentResult == "SUCCESS" && GIT_LOCAL_BRANCH ==~ /(master|develop)/) { + def artifacts = load ".jenkinsci/artifacts.groovy" + def commit = env.GIT_COMMIT + def platform = sh(script: 'uname -m', returnStdout: true).trim() + filePaths = [ '/tmp/${GIT_COMMIT}-${BUILD_NUMBER}/*' ] + artifacts.uploadArtifacts(filePaths, sprintf('/iroha/linux/%4$s/%1$s-%2$s-%3$s', [GIT_LOCAL_BRANCH, sh(script: 'date "+%Y%m%d"', returnStdout: true).trim(), commit.substring(0,6), platform])) + } + } + finally { + def cleanup = load ".jenkinsci/docker-cleanup.groovy" + cleanup.doDockerCleanup() + cleanWs() + } + } +} + +return this diff --git a/.jenkinsci/mac-debug-build.groovy b/.jenkinsci/mac-debug-build.groovy deleted file mode 100644 index 7eb118758b..0000000000 --- a/.jenkinsci/mac-debug-build.groovy +++ /dev/null @@ -1,44 +0,0 @@ -#!/usr/bin/env groovy - -def doDebugBuild() { - def setter = load ".jenkinsci/set-parallelism.groovy" - def parallelism = setter.setParallelism(params.PARALLELISM) - def scmVars = checkout scm - env.IROHA_VERSION = "0x${scmVars.GIT_COMMIT}" - env.IROHA_HOME = "/opt/iroha" - env.IROHA_BUILD = "${env.IROHA_HOME}/build" - - sh """ - ccache --version - ccache --show-stats - ccache --zero-stats - ccache --max-size=5G - """ - sh """ - cmake \ - -DTESTING=ON \ - -H. \ - -Bbuild \ - -DCMAKE_BUILD_TYPE=${params.build_type} \ - -DIROHA_VERSION=${env.IROHA_VERSION} - """ - sh "cmake --build build -- -j${parallelism}" - sh "ccache --show-stats" -} - -def doTestStep(testList) { - sh """ - export IROHA_POSTGRES_PASSWORD=${IROHA_POSTGRES_PASSWORD}; \ - export IROHA_POSTGRES_USER=${IROHA_POSTGRES_USER}; \ - mkdir -p /var/jenkins/${GIT_COMMIT}-${BUILD_NUMBER}; \ - initdb -D /var/jenkins/${GIT_COMMIT}-${BUILD_NUMBER}/ -U ${IROHA_POSTGRES_USER} --pwfile=<(echo ${IROHA_POSTGRES_PASSWORD}); \ - pg_ctl -D /var/jenkins/${GIT_COMMIT}-${BUILD_NUMBER}/ -o '-p 5433' -l /var/jenkins/${GIT_COMMIT}-${BUILD_NUMBER}/events.log start; \ - psql -h localhost -d postgres -p 5433 -U ${IROHA_POSTGRES_USER} --file=<(echo create database ${IROHA_POSTGRES_USER};) - """ - def testExitCode = sh(script: """cd build && IROHA_POSTGRES_HOST=localhost IROHA_POSTGRES_PORT=5433 ctest --output-on-failure -R '${testList}' """, returnStatus: true) - if (testExitCode != 0) { - currentBuild.result = "UNSTABLE" - } -} - -return this diff --git a/.jenkinsci/mac-release-build.groovy b/.jenkinsci/mac-release-build.groovy index 26552f4ed4..4ff20c6a95 100644 --- a/.jenkinsci/mac-release-build.groovy +++ b/.jenkinsci/mac-release-build.groovy @@ -1,8 +1,6 @@ #!/usr/bin/env groovy def doReleaseBuild(coverageEnabled=false) { - def setter = load ".jenkinsci/set-parallelism.groovy" - def parallelism = setter.setParallelism(params.PARALLELISM) def scmVars = checkout scm env.IROHA_VERSION = "0x${scmVars.GIT_COMMIT}" env.IROHA_HOME = "/opt/iroha" @@ -19,10 +17,10 @@ def doReleaseBuild(coverageEnabled=false) { -Bbuild \ -DCOVERAGE=OFF \ -DPACKAGE_TGZ=ON \ - -DCMAKE_BUILD_TYPE=${params.build_type} \ + -DCMAKE_BUILD_TYPE=Release \ -DIROHA_VERSION=${env.IROHA_VERSION} - - cmake --build build --target package -- -j${parallelism} + + cmake --build build --target package -- -j${params.PARALLELISM} mv ./build/iroha-${env.IROHA_VERSION}-*.tar.gz ./build/iroha.tar.gz ccache --show-stats """ diff --git a/.jenkinsci/notifications.groovy b/.jenkinsci/notifications.groovy deleted file mode 100644 index 2c527ef6d0..0000000000 --- a/.jenkinsci/notifications.groovy +++ /dev/null @@ -1,61 +0,0 @@ -#!/usr/bin/env groovy - -def notifyBuildResults() { - def mergeMessage = '' - def receivers = '' - // notify commiter in case of branch commit - if ( env.CHANGE_ID == null ) { - sendEmail(buildContent(mergeMessage), "${GIT_COMMITER_EMAIL}") - return - } - // merge commit build results - if ( params.merge_pr ) { - if ( currentBuild.currentResult == "SUCCESS" ) { - mergeMessage = "Merge status to ${env.CHANGE_TARGET}: true" - } - else { - mergeMessage = "Merge status to ${env.CHANGE_TARGET}: false" - } - - if ( env.CHANGE_TARGET == 'master' ) { - receivers = "iroha-maintainers@soramitsu.co.jp" - } - else if ( env.CHANGE_TARGET == 'develop' ) { - receivers = "andrei@soramitsu.co.jp, fyodor@soramitsu.co.jp, ${GIT_COMMITER_EMAIL}" - } - else { - receivers = "${GIT_COMMITER_EMAIL}" - } - - sendEmail(buildContent(mergeMessage), receivers) - } - else { - // write comment to the PR page on github if it is a pull request commit - def notify = load ".jenkinsci/github-api.groovy" - notify.writePullRequestComment() - } - return -} - -def buildContent(mergeMessage="") { - return """ -

This email informs you about the build results on Jenkins CI

-

Build status: ${currentBuild.currentResult}. ${mergeMessage}

-

-Check console output to view the results. -

-

-You can find the build log attached to this email -

-""" -} - -def sendEmail(content, to) { - emailext( subject: '$DEFAULT_SUBJECT', - body: "${content}", - attachLog: true, - compressLog: true, - to: "${to}" - ) -} -return this diff --git a/.jenkinsci/post-step.groovy b/.jenkinsci/post-step.groovy deleted file mode 100644 index e5eab76376..0000000000 --- a/.jenkinsci/post-step.groovy +++ /dev/null @@ -1,40 +0,0 @@ -#!/usr/bin/env groovy - -// upload artifacts in release builds -def postStep() { - def artifacts = load ".jenkinsci/artifacts.groovy" - def commit = env.GIT_COMMIT - def platform = sh(script: 'uname -m', returnStdout: true).trim() - filePaths = [ '/tmp/${GIT_COMMIT}-${BUILD_NUMBER}/*' ] - artifacts.uploadArtifacts(filePaths, sprintf('/iroha/linux/%4$s/%1$s-%2$s-%3$s', [GIT_LOCAL_BRANCH, sh(script: 'date "+%Y%m%d"', returnStdout: true).trim(), commit.substring(0,6), platform])) -} - -// upload artifacts in release builds (for mac) -def macPostStep() { - def artifacts = load ".jenkinsci/artifacts.groovy" - def commit = env.GIT_COMMIT - filePaths = [ '\$(pwd)/build/*.tar.gz' ] - artifacts.uploadArtifacts(filePaths, sprintf('/iroha/macos/%1$s-%2$s-%3$s', [GIT_LOCAL_BRANCH, sh(script: 'date "+%Y%m%d"', returnStdout: true).trim(), commit.substring(0,6)])) -} - -// clean folders after the build -def cleanUp() { - if ( ! env.NODE_NAME.contains('x86_64') ) { - sh """ - #remove folder with iroha.deb package and Dockerfiles - rm -rf /tmp/${env.GIT_COMMIT}-${BUILD_NUMBER} - rm -rf /tmp/${env.GIT_COMMIT} - """ - } - cleanWs() -} - -// stop postgres and remove workspace folder (for mac) -def macCleanUp() { - sh """ - rm -rf /var/jenkins/${GIT_COMMIT}-${BUILD_NUMBER}/ - """ - cleanWs() -} - -return this diff --git a/.jenkinsci/pre-build.groovy b/.jenkinsci/pre-build.groovy deleted file mode 100644 index a6875bc28f..0000000000 --- a/.jenkinsci/pre-build.groovy +++ /dev/null @@ -1,14 +0,0 @@ -#!/usr/bin/env groovy - -// replaces bunch of expressions in `when` block at the `stage` in Jenkinsfile -def prepare() { - def mergeBranches = env.CHANGE_TARGET ==~ /(master|develop|trunk)/ ? "true" : "false" - def docsPullRequestBranches = env.CHANGE_TARGET ==~ /(master|develop)/ ? "true" : "false" - def pullRequestCommit = env.CHANGE_ID && env.GIT_PREVIOUS_COMMIT ? "true" : "false" - INITIAL_COMMIT_PR = env.CHANGE_ID && env.GIT_PREVIOUS_COMMIT == null ? "true" : "false" - MERGE_CONDITIONS_SATISFIED = (mergeBranches == "true" && pullRequestCommit == "true" && params.merge_pr) ? "true" : "false" - REST_PR_CONDITIONS_SATISFIED = (docsPullRequestBranches == "true" && pullRequestCommit == "true" && params.merge_pr) ? "true" : "false" - GIT_COMMITER_EMAIL = sh(script: """git --no-pager show -s --format='%ae' ${env.GIT_COMMIT}""", returnStdout: true).trim() -} - -return this diff --git a/.jenkinsci/release-build.groovy b/.jenkinsci/release-build.groovy index 5403512933..d24d6cfb31 100644 --- a/.jenkinsci/release-build.groovy +++ b/.jenkinsci/release-build.groovy @@ -1,20 +1,25 @@ #!/usr/bin/env groovy def doReleaseBuild() { + def parallelism = params.PARALLELISM def manifest = load ".jenkinsci/docker-manifest.groovy" // params are always null unless job is started // this is the case for the FIRST build only. // So just set this to same value as default. // This is a known bug. See https://issues.jenkins-ci.org/browse/JENKINS-41929 - def setter = load ".jenkinsci/set-parallelism.groovy" - def parallelism = setter.setParallelism(params.PARALLELISM) + if (!parallelism) { + parallelism = 4 + } + if (env.NODE_NAME.contains('arm7')) { + parallelism = 1 + } def platform = sh(script: 'uname -m', returnStdout: true).trim() sh "mkdir /tmp/${env.GIT_COMMIT}-${BUILD_NUMBER} || true" iC = docker.image("${DOCKER_REGISTRY_BASENAME}:${platform}-develop-build") iC.pull() iC.inside("" + " -v /tmp/${GIT_COMMIT}-${BUILD_NUMBER}:/tmp/${GIT_COMMIT}" - + " -v ${CCACHE_RELEASE_DIR}:${CCACHE_DIR}") { + + " -v /var/jenkins/ccache:${CCACHE_RELEASE_DIR}") { def scmVars = checkout scm env.IROHA_VERSION = "0x${scmVars.GIT_COMMIT}" @@ -31,7 +36,7 @@ def doReleaseBuild() { cmake \ -H. \ -Bbuild \ - -DCMAKE_BUILD_TYPE=${params.build_type} \ + -DCMAKE_BUILD_TYPE=Release \ -DIROHA_VERSION=${env.IROHA_VERSION} \ -DPACKAGE_DEB=ON \ -DPACKAGE_TGZ=ON \ @@ -52,9 +57,7 @@ def doReleaseBuild() { sh "chmod +x /tmp/${env.GIT_COMMIT}/entrypoint.sh" iCRelease = docker.build("${DOCKER_REGISTRY_BASENAME}:${GIT_COMMIT}-${BUILD_NUMBER}-release", "--no-cache -f /tmp/${env.GIT_COMMIT}/Dockerfile /tmp/${env.GIT_COMMIT}") if (env.GIT_LOCAL_BRANCH == 'develop') { - withDockerRegistry([ credentialsId: "docker-hub-credentials", url: "" ]) { - iCRelease.push("${platform}-develop") - } + iCRelease.push("${platform}-develop") if (manifest.manifestSupportEnabled()) { manifest.manifestCreate("${DOCKER_REGISTRY_BASENAME}:develop", ["${DOCKER_REGISTRY_BASENAME}:x86_64-develop", @@ -75,9 +78,7 @@ def doReleaseBuild() { } } else if (env.GIT_LOCAL_BRANCH == 'master') { - withDockerRegistry([ credentialsId: "docker-hub-credentials", url: "" ]) { - iCRelease.push("${platform}-latest") - } + iCRelease.push("${platform}-latest") if (manifest.manifestSupportEnabled()) { manifest.manifestCreate("${DOCKER_REGISTRY_BASENAME}:latest", ["${DOCKER_REGISTRY_BASENAME}:x86_64-latest", diff --git a/.jenkinsci/selected-branches-coverage.groovy b/.jenkinsci/selected-branches-coverage.groovy new file mode 100644 index 0000000000..d6c3f95967 --- /dev/null +++ b/.jenkinsci/selected-branches-coverage.groovy @@ -0,0 +1,13 @@ +#!/usr/bin/env groovy + +def selectedBranchesCoverage(branches, PRCoverage=true) { + // trigger coverage if branch is either develop or master, or it is a PR + if (PRCoverage) { + return env.GIT_LOCAL_BRANCH in branches || env.CHANGE_ID != null + } + else { + return env.GIT_LOCAL_BRANCH in branches + } +} + +return this \ No newline at end of file diff --git a/.jenkinsci/set-parallelism.groovy b/.jenkinsci/set-parallelism.groovy deleted file mode 100644 index 581488f7ca..0000000000 --- a/.jenkinsci/set-parallelism.groovy +++ /dev/null @@ -1,19 +0,0 @@ -#!/usr/bin/env groovy - -def setParallelism(defaultParameter) { - if (!defaultParameter) { - return 4 - } - if (env.NODE_NAME.contains('arm7')) { - return 1 - } - if (env.NODE_NAME.contains('mac')) { - return 4 - } - if (env.NODE_NAME.contains('x86_64')) { - return 8 - } - return defaultParameter -} - -return this diff --git a/.jenkinsci/test-launcher.groovy b/.jenkinsci/test-launcher.groovy deleted file mode 100644 index 924b0cd6f4..0000000000 --- a/.jenkinsci/test-launcher.groovy +++ /dev/null @@ -1,41 +0,0 @@ -#!/usr/bin/env groovy - -// format the enum elements output like "(val1|val2|...|valN)*" -def printRange(start, end) { - def output = "" - def set = start..end - TestTypes.values().each { t -> - if (t.getOrder() in set) { - output = [output, (t.getOrder() != start ? "|" : ""), t.name()].join('') - } - } - return ["(", output, ")*"].join('') -} - -// return tests list regex that will be launched by ctest -def chooseTestType() { - if (params.merge_pr) { - if (env.NODE_NAME.contains('x86_64')) { - // choose module, integration, system, cmake, regression tests - return printRange(TestTypes.module.getOrder(), TestTypes.regression.getOrder()) - } - else { - // not to do any tests - return "" - } - } - if (params.nightly) { - if (env.NODE_NAME.contains('x86_64')) { - // choose all tests - return printRange(TestTypes.MIN_VALUE.getOrder(), TestTypes.MAX_VALUE.getOrder()) - } - else { - // choose module, integration, system, cmake, regression tests - return printRange(TestTypes.module.getOrder(), TestTypes.regression.getOrder()) - } - } - // just choose module tests - return [TestTypes.module.toString(), "*"].join('') -} - -return this diff --git a/Jenkinsfile b/Jenkinsfile index cfea416560..2fd2412580 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -1,14 +1,12 @@ properties([parameters([ + booleanParam(defaultValue: true, description: 'Build `iroha`', name: 'iroha'), + booleanParam(defaultValue: false, description: 'Build `bindings`', name: 'bindings'), booleanParam(defaultValue: true, description: '', name: 'x86_64_linux'), booleanParam(defaultValue: false, description: '', name: 'armv7_linux'), booleanParam(defaultValue: false, description: '', name: 'armv8_linux'), - booleanParam(defaultValue: false, description: '', name: 'x86_64_macos'), + booleanParam(defaultValue: true, description: '', name: 'x86_64_macos'), booleanParam(defaultValue: false, description: '', name: 'x86_64_win'), - booleanParam(defaultValue: false, description: 'Build coverage', name: 'coverage'), - booleanParam(defaultValue: false, description: 'Merge this PR to target after success build', name: 'merge_pr'), - booleanParam(defaultValue: false, description: 'Scheduled nightly build', name: 'nightly'), choice(choices: 'Debug\nRelease', description: 'Iroha build type', name: 'build_type'), - booleanParam(defaultValue: false, description: 'Build `bindings`', name: 'bindings'), booleanParam(defaultValue: false, description: 'Build Java bindings', name: 'JavaBindings'), choice(choices: 'Release\nDebug', description: 'Java bindings build type', name: 'JBBuildType'), booleanParam(defaultValue: false, description: 'Build Python bindings', name: 'PythonBindings'), @@ -21,6 +19,7 @@ properties([parameters([ booleanParam(defaultValue: false, description: 'Build docs', name: 'Doxygen'), string(defaultValue: '4', description: 'How much parallelism should we exploit. "4" is optimal for machines with modest amount of memory and at least 4 cores', name: 'PARALLELISM')])]) + pipeline { environment { CCACHE_DIR = '/opt/.ccache' @@ -29,21 +28,12 @@ pipeline { SONAR_TOKEN = credentials('SONAR_TOKEN') GIT_RAW_BASE_URL = "https://raw.githubusercontent.com/hyperledger/iroha" DOCKER_REGISTRY_BASENAME = "hyperledger/iroha" - JENKINS_DOCKER_IMAGE_DIR = '/tmp/docker' - GIT_COMMITER_EMAIL = '' IROHA_NETWORK = "iroha-0${CHANGE_ID}-${GIT_COMMIT}-${BUILD_NUMBER}" IROHA_POSTGRES_HOST = "pg-0${CHANGE_ID}-${GIT_COMMIT}-${BUILD_NUMBER}" IROHA_POSTGRES_USER = "pguser${GIT_COMMIT}" IROHA_POSTGRES_PASSWORD = "${GIT_COMMIT}" IROHA_POSTGRES_PORT = 5432 - - DOCKER_AGENT_IMAGE = '' - DOCKER_IMAGE_FILE = '' - WORKSPACE_PATH = '' - MERGE_CONDITIONS_SATISFIED = '' - REST_PR_CONDITIONS_SATISFIED = '' - INITIAL_COMMIT_PR = '' } options { @@ -53,13 +43,10 @@ pipeline { agent any stages { - stage ('Pre-build') { + stage ('Stop same job builds') { agent { label 'master' } steps { script { - load ".jenkinsci/enums.groovy" - def preBuildRoutine = load ".jenkinsci/pre-build.groovy" - preBuildRoutine.prepare() if (GIT_LOCAL_BRANCH != "develop") { def builds = load ".jenkinsci/cancel-builds-same-job.groovy" builds.cancelSameJobBuilds() @@ -67,37 +54,41 @@ pipeline { } } } - stage('Build') { + stage('Build Debug') { + when { + allOf { + expression { params.build_type == 'Debug' } + expression { return params.iroha } + } + } parallel { stage ('x86_64_linux') { when { beforeAgent true - anyOf { - expression { return params.x86_64_linux } - expression { return MERGE_CONDITIONS_SATISFIED == "true" } - } + expression { return params.x86_64_linux } } - agent { label 'x86_64_aws_build' } + agent { label 'x86_64' } steps { script { - def debugBuild = load ".jenkinsci/debug-build.groovy" - def coverage = load ".jenkinsci/build-coverage.groovy" - if (params.build_type == 'Debug') { - debugBuild.doDebugBuild(coverage.checkCoverageConditions()) + debugBuild = load ".jenkinsci/debug-build.groovy" + coverage = load ".jenkinsci/selected-branches-coverage.groovy" + if (coverage.selectedBranchesCoverage(['develop', 'master'])) { + debugBuild.doDebugBuild(true) } - if (params.build_type == 'Release') { - def releaseBuild = load ".jenkinsci/release-build.groovy" + else { + debugBuild.doDebugBuild() + } + if (GIT_LOCAL_BRANCH ==~ /(master|develop)/) { + releaseBuild = load ".jenkinsci/release-build.groovy" releaseBuild.doReleaseBuild() } } } post { - success { + always { script { - if (params.build_type == 'Release') { - def post = load ".jenkinsci/post-step.groovy" - post.postStep() - } + post = load ".jenkinsci/linux-post-step.groovy" + post.linuxPostStep() } } } @@ -105,31 +96,30 @@ pipeline { stage('armv7_linux') { when { beforeAgent true - anyOf { - expression { return params.armv7_linux } - // expression { return MERGE_CONDITIONS_SATISFIED == "true" } - } + expression { return params.armv7_linux } } agent { label 'armv7' } steps { script { - if (params.build_type == 'Debug') { - def debugBuild = load ".jenkinsci/debug-build.groovy" - debugBuild.doDebugBuild() + debugBuild = load ".jenkinsci/debug-build.groovy" + coverage = load ".jenkinsci/selected-branches-coverage.groovy" + if (!params.x86_64_linux && !params.armv8_linux && !params.x86_64_macos && (coverage.selectedBranchesCoverage(['develop', 'master']))) { + debugBuild.doDebugBuild(true) } else { - def releaseBuild = load ".jenkinsci/release-build.groovy" + debugBuild.doDebugBuild() + } + if (GIT_LOCAL_BRANCH ==~ /(master|develop)/) { + releaseBuild = load ".jenkinsci/release-build.groovy" releaseBuild.doReleaseBuild() } } } post { - success { + always { script { - if (params.build_type == 'Release') { - def post = load ".jenkinsci/post-step.groovy" - post.postStep() - } + post = load ".jenkinsci/linux-post-step.groovy" + post.linuxPostStep() } } } @@ -137,63 +127,129 @@ pipeline { stage('armv8_linux') { when { beforeAgent true - anyOf { - expression { return params.armv8_linux } - // expression { return MERGE_CONDITIONS_SATISFIED == "true" } - } + expression { return params.armv8_linux } } agent { label 'armv8' } steps { script { - if ( params.build_type == 'Debug') { - def debugBuild = load ".jenkinsci/debug-build.groovy" - debugBuild.doDebugBuild() + debugBuild = load ".jenkinsci/debug-build.groovy" + coverage = load ".jenkinsci/selected-branches-coverage.groovy" + if (!params.x86_64_linux && !params.x86_64_macos && (coverage.selectedBranchesCoverage(['develop', 'master']))) { + debugBuild.doDebugBuild(true) } else { - def releaseBuild = load ".jenkinsci/release-build.groovy" + debugBuild.doDebugBuild() + } + if (GIT_LOCAL_BRANCH ==~ /(master|develop)/) { + releaseBuild = load ".jenkinsci/release-build.groovy" releaseBuild.doReleaseBuild() } } } post { - success { + always { script { - if (params.build_type == 'Release') { - def post = load ".jenkinsci/post-step.groovy" - post.postStep() - } + post = load ".jenkinsci/linux-post-step.groovy" + post.linuxPostStep() } } } } - stage('x86_64_macos') { + stage('x86_64_macos'){ when { beforeAgent true - anyOf { - expression { return INITIAL_COMMIT_PR == "true" } - expression { return MERGE_CONDITIONS_SATISFIED == "true" } - expression { return params.x86_64_macos } - } + expression { return params.x86_64_macos } } agent { label 'mac' } steps { script { - if (params.build_type == 'Debug') { - def macDebugBuild = load ".jenkinsci/mac-debug-build.groovy" - macDebugBuild.doDebugBuild() + def coverageEnabled = false + def cmakeOptions = "" + coverage = load ".jenkinsci/selected-branches-coverage.groovy" + if (!params.x86_64_linux && (coverage.selectedBranchesCoverage(['develop', 'master']))) { + coverageEnabled = true + cmakeOptions = " -DCOVERAGE=ON " } - else { - def macReleaseBuild = load ".jenkinsci/mac-release-build.groovy" - macReleaseBuild.doReleaseBuild() + def scmVars = checkout scm + env.IROHA_VERSION = "0x${scmVars.GIT_COMMIT}" + env.IROHA_HOME = "/opt/iroha" + env.IROHA_BUILD = "${env.IROHA_HOME}/build" + + sh """ + ccache --version + ccache --show-stats + ccache --zero-stats + ccache --max-size=5G + """ + sh """ + cmake \ + -DTESTING=ON \ + -H. \ + -Bbuild \ + -DCMAKE_BUILD_TYPE=${params.build_type} \ + -DIROHA_VERSION=${env.IROHA_VERSION} \ + ${cmakeOptions} + """ + sh "cmake --build build -- -j${params.PARALLELISM}" + sh "ccache --show-stats" + if ( coverageEnabled ) { + sh "cmake --build build --target coverage.init.info" + } + sh """ + export IROHA_POSTGRES_PASSWORD=${IROHA_POSTGRES_PASSWORD}; \ + export IROHA_POSTGRES_USER=${IROHA_POSTGRES_USER}; \ + mkdir -p /var/jenkins/${GIT_COMMIT}-${BUILD_NUMBER}; \ + initdb -D /var/jenkins/${GIT_COMMIT}-${BUILD_NUMBER}/ -U ${IROHA_POSTGRES_USER} --pwfile=<(echo ${IROHA_POSTGRES_PASSWORD}); \ + pg_ctl -D /var/jenkins/${GIT_COMMIT}-${BUILD_NUMBER}/ -o '-p 5433' -l /var/jenkins/${GIT_COMMIT}-${BUILD_NUMBER}/events.log start; \ + psql -h localhost -d postgres -p 5433 -U ${IROHA_POSTGRES_USER} --file=<(echo create database ${IROHA_POSTGRES_USER};) + """ + def testExitCode = sh(script: """cd build && IROHA_POSTGRES_HOST=localhost IROHA_POSTGRES_PORT=5433 ctest --output-on-failure """, returnStatus: true) + if (testExitCode != 0) { + currentBuild.result = "UNSTABLE" + } + if ( coverageEnabled ) { + sh "cmake --build build --target cppcheck" + // Sonar + if (env.CHANGE_ID != null) { + sh """ + sonar-scanner \ + -Dsonar.github.disableInlineComments \ + -Dsonar.github.repository='hyperledger/iroha' \ + -Dsonar.analysis.mode=preview \ + -Dsonar.login=${SONAR_TOKEN} \ + -Dsonar.projectVersion=${BUILD_TAG} \ + -Dsonar.github.oauth=${SORABOT_TOKEN} + """ + } + sh "cmake --build build --target coverage.info" + sh "python /usr/local/bin/lcov_cobertura.py build/reports/coverage.info -o build/reports/coverage.xml" + cobertura autoUpdateHealth: false, autoUpdateStability: false, coberturaReportFile: '**/build/reports/coverage.xml', conditionalCoverageTargets: '75, 50, 0', failUnhealthy: false, failUnstable: false, lineCoverageTargets: '75, 50, 0', maxNumberOfBuilds: 50, methodCoverageTargets: '75, 50, 0', onlyStable: false, zoomCoverageChart: false + } + if (GIT_LOCAL_BRANCH ==~ /(master|develop)/) { + releaseBuild = load ".jenkinsci/mac-release-build.groovy" + releaseBuild.doReleaseBuild() } } } post { - success { + always { script { - if (params.build_type == 'Release') { - def post = load ".jenkinsci/post-step.groovy" - post.macPostStep() + timeout(time: 600, unit: "SECONDS") { + try { + if (currentBuild.currentResult == "SUCCESS" && GIT_LOCAL_BRANCH ==~ /(master|develop)/) { + def artifacts = load ".jenkinsci/artifacts.groovy" + def commit = env.GIT_COMMIT + filePaths = [ '\$(pwd)/build/*.tar.gz' ] + artifacts.uploadArtifacts(filePaths, sprintf('/iroha/macos/%1$s-%2$s-%3$s', [GIT_LOCAL_BRANCH, sh(script: 'date "+%Y%m%d"', returnStdout: true).trim(), commit.substring(0,6)])) + } + } + finally { + cleanWs() + sh """ + pg_ctl -D /var/jenkins/${GIT_COMMIT}-${BUILD_NUMBER}/ stop && \ + rm -rf /var/jenkins/${GIT_COMMIT}-${BUILD_NUMBER}/ + """ + } } } } @@ -201,56 +257,29 @@ pipeline { } } } - stage('Pre-Coverage') { - when { - beforeAgent true - anyOf { - expression { params.coverage } // by request - expression { return INITIAL_COMMIT_PR == "true" } - expression { return MERGE_CONDITIONS_SATISFIED == "true" } - allOf { - expression { return params.build_type == 'Debug' } - expression { return env.GIT_LOCAL_BRANCH ==~ /master/ } - } - } - } - agent { label 'x86_64_aws_cov'} - options { skipDefaultCheckout() } - steps { - script { - def coverage = load '.jenkinsci/debug-build.groovy' - coverage.doPreCoverageStep() - } - } - } - stage('Tests') { + stage('Build Release') { when { - beforeAgent true - expression { return params.build_type == "Debug" } + expression { params.build_type == 'Release' } + expression { return params.iroha } } parallel { stage('x86_64_linux') { when { beforeAgent true - anyOf { - expression { return params.x86_64_linux } - expression { return MERGE_CONDITIONS_SATISFIED == "true" } - } + expression { return params.x86_64_linux } } - agent { label 'x86_64_aws_test' } - options { skipDefaultCheckout() } + agent { label 'x86_64' } steps { script { - def debugBuild = load ".jenkinsci/debug-build.groovy" - def testSelect = load ".jenkinsci/test-launcher.groovy" - debugBuild.doTestStep(testSelect.chooseTestType()) + def releaseBuild = load ".jenkinsci/release-build.groovy" + releaseBuild.doReleaseBuild() } } post { - cleanup { + always { script { - def clean = load ".jenkinsci/docker-cleanup.groovy" - clean.doDockerNetworkCleanup() + post = load ".jenkinsci/linux-post-step.groovy" + post.linuxPostStep() } } } @@ -258,25 +287,20 @@ pipeline { stage('armv7_linux') { when { beforeAgent true - allOf { - expression { return params.armv7_linux } - // expression { return MERGE_CONDITIONS_SATISFIED == "false" } - } + expression { return params.armv7_linux } } agent { label 'armv7' } - options { skipDefaultCheckout() } steps { script { - def debugBuild = load ".jenkinsci/debug-build.groovy" - def testSelect = load ".jenkinsci/test-launcher.groovy" - debugBuild.doTestStep(testSelect.chooseTestType()) + def releaseBuild = load ".jenkinsci/release-build.groovy" + releaseBuild.doReleaseBuild() } } post { - cleanup { + always { script { - def clean = load ".jenkinsci/docker-cleanup.groovy" - clean.doDockerNetworkCleanup() + post = load ".jenkinsci/linux-post-step.groovy" + post.linuxPostStep() } } } @@ -284,25 +308,20 @@ pipeline { stage('armv8_linux') { when { beforeAgent true - allOf { - expression { return params.armv8_linux } - // expression { return MERGE_CONDITIONS_SATISFIED == "false" } - } + expression { return params.armv8_linux } } agent { label 'armv8' } - options { skipDefaultCheckout() } steps { script { - def debugBuild = load ".jenkinsci/debug-build.groovy" - def testSelect = load ".jenkinsci/test-launcher.groovy" - debugBuild.doTestStep(testSelect.chooseTestType()) + def releaseBuild = load ".jenkinsci/release-build.groovy" + releaseBuild.doReleaseBuild() } } post { - cleanup { + always { script { - def clean = load ".jenkinsci/docker-cleanup.groovy" - clean.doDockerNetworkCleanup() + post = load ".jenkinsci/linux-post-step.groovy" + post.linuxPostStep() } } } @@ -310,157 +329,97 @@ pipeline { stage('x86_64_macos') { when { beforeAgent true - allOf { - expression { return INITIAL_COMMIT_PR == "false" } - expression { return MERGE_CONDITIONS_SATISFIED == "false" } - expression { return params.x86_64_macos } - } + expression { return params.x86_64_macos } } agent { label 'mac' } - options { skipDefaultCheckout() } steps { script { - def macDebugBuild = load ".jenkinsci/mac-debug-build.groovy" - def testSelect = load ".jenkinsci/test-launcher.groovy" - macDebugBuild.doTestStep(testSelect.chooseTestType()) + def releaseBuild = load ".jenkinsci/mac-release-build.groovy" + releaseBuild.doReleaseBuild() } } post { - cleanup { + always { script { - sh "pg_ctl -D /var/jenkins/${GIT_COMMIT}-${BUILD_NUMBER}/ stop" + timeout(time: 600, unit: "SECONDS") { + try { + if (currentBuild.currentResult == "SUCCESS" && GIT_LOCAL_BRANCH ==~ /(master|develop)/) { + def artifacts = load ".jenkinsci/artifacts.groovy" + def commit = env.GIT_COMMIT + filePaths = [ '\$(pwd)/build/*.tar.gz' ] + artifacts.uploadArtifacts(filePaths, sprintf('/iroha/macos/%1$s-%2$s-%3$s', [GIT_LOCAL_BRANCH, sh(script: 'date "+%Y%m%d"', returnStdout: true).trim(), commit.substring(0,6)])) + } + } + finally { + cleanWs() + } + } } } } } } } - stage('Post-Coverage') { + stage('Build docs') { when { beforeAgent true - anyOf { - expression { params.coverage } // by request - expression { return INITIAL_COMMIT_PR == "true" } - expression { return MERGE_CONDITIONS_SATISFIED == "true" } - allOf { - expression { return params.build_type == 'Debug' } - expression { return env.GIT_LOCAL_BRANCH ==~ /master/ } - } + allOf { + expression { return params.Doxygen } + expression { GIT_LOCAL_BRANCH ==~ /(master|develop)/ } } } - parallel { - stage('lcov_cobertura') { - agent { label 'x86_64_aws_cov' } - options { skipDefaultCheckout() } - steps { - script { - def coverage = load '.jenkinsci/debug-build.groovy' - coverage.doPostCoverageCoberturaStep() - } - } - } - stage('sonarqube') { - agent { label 'x86_64_aws_cov' } - options { skipDefaultCheckout() } - steps { - script { - def coverage = load '.jenkinsci/debug-build.groovy' - coverage.doPostCoverageSonarStep() - } + // build docs on any vacant node. Prefer `x86_64` over + // others as nodes are more powerful + agent { label 'x86_64 || arm' } + steps { + script { + def doxygen = load ".jenkinsci/doxygen.groovy" + docker.image("${env.DOCKER_IMAGE}").inside { + def scmVars = checkout scm + doxygen.doDoxygen() } } } } - stage ('Build rest') { + stage('Build bindings') { + when { + beforeAgent true + expression { return params.bindings } + } parallel { - stage('linux_release') { - when { - beforeAgent true - anyOf { - allOf { - expression { return params.x86_64_linux } - expression { return params.build_type == 'Debug' } - expression { return env.GIT_LOCAL_BRANCH ==~ /(develop|master|trunk)/ } - } - expression { return MERGE_CONDITIONS_SATISFIED == "true" } - } - } - agent { label 'x86_64_aws_build' } - options { skipDefaultCheckout() } - steps { - script { - def releaseBuild = load '.jenkinsci/release-build.groovy' - releaseBuild.doReleaseBuild() - } - } - post { - success { - script { - if (params.build_type == 'Release') { - def post = load ".jenkinsci/post-step.groovy" - post.postStep() - } - } - } - } - } - stage('docs') { - when { - beforeAgent true - anyOf { - expression { return params.Doxygen } - expression { return REST_PR_CONDITIONS_SATISFIED == "true" } - } - } - agent { label 'x86_64_aws_docs' } - steps { - script { - def doxygen = load ".jenkinsci/doxygen.groovy" - sh "docker load -i ${JENKINS_DOCKER_IMAGE_DIR}/${DOCKER_IMAGE_FILE}" - def iC = docker.image("${DOCKER_AGENT_IMAGE}") - iC.inside { - def scmVars = checkout scm - doxygen.doDoxygen() - } - } - } - } - stage('bindings') { + stage('Linux bindings') { when { beforeAgent true - anyOf { - expression { return params.bindings } - expression { return REST_PR_CONDITIONS_SATISFIED == "true" } - } + expression { return params.x86_64_linux } } - agent { label 'x86_64_aws_bindings' } + agent { label 'x86_64' } environment { - JAVA_HOME = '/usr/lib/jvm/java-8-oracle' + JAVA_HOME = "/usr/lib/jvm/java-8-oracle" } steps { script { def bindings = load ".jenkinsci/bindings.groovy" def dPullOrBuild = load ".jenkinsci/docker-pull-or-build.groovy" def platform = sh(script: 'uname -m', returnStdout: true).trim() - if (params.JavaBindings || params.PythonBindings || REST_PR_CONDITIONS_SATISFIED == "true") { + if (params.JavaBindings || params.PythonBindings) { def iC = dPullOrBuild.dockerPullOrUpdate( "$platform-develop-build", "${env.GIT_RAW_BASE_URL}/${env.GIT_COMMIT}/docker/develop/Dockerfile", "${env.GIT_RAW_BASE_URL}/${env.GIT_PREVIOUS_COMMIT}/docker/develop/Dockerfile", "${env.GIT_RAW_BASE_URL}/develop/docker/develop/Dockerfile", ['PARALLELISM': params.PARALLELISM]) - if (params.JavaBindings || REST_PR_CONDITIONS_SATISFIED == "true") { - iC.inside("-v /tmp/${env.GIT_COMMIT}/bindings-artifact:/tmp/bindings-artifact --user root") { + if (params.JavaBindings) { + iC.inside("-v /tmp/${env.GIT_COMMIT}/bindings-artifact:/tmp/bindings-artifact") { bindings.doJavaBindings('linux', params.JBBuildType) } } - if (params.PythonBindings || REST_PR_CONDITIONS_SATISFIED == "true") { - iC.inside("-v /tmp/${env.GIT_COMMIT}/bindings-artifact:/tmp/bindings-artifact --user root") { + if (params.PythonBindings) { + iC.inside("-v /tmp/${env.GIT_COMMIT}/bindings-artifact:/tmp/bindings-artifact") { bindings.doPythonBindings('linux', params.PBBuildType) } } } - if (params.AndroidBindings || REST_PR_CONDITIONS_SATISFIED == "true") { + if (params.AndroidBindings) { def iC = dPullOrBuild.dockerPullOrUpdate( "android-${params.ABPlatform}-${params.ABBuildType}", "${env.GIT_RAW_BASE_URL}/${env.GIT_COMMIT}/docker/android/Dockerfile", @@ -469,7 +428,7 @@ pipeline { ['PARALLELISM': params.PARALLELISM, 'PLATFORM': params.ABPlatform, 'BUILD_TYPE': params.ABBuildType]) sh "curl -L -o /tmp/${env.GIT_COMMIT}/entrypoint.sh ${env.GIT_RAW_BASE_URL}/${env.GIT_COMMIT}/docker/android/entrypoint.sh" sh "chmod +x /tmp/${env.GIT_COMMIT}/entrypoint.sh" - iC.inside("-v /tmp/${env.GIT_COMMIT}/entrypoint.sh:/entrypoint.sh:ro -v /tmp/${env.GIT_COMMIT}/bindings-artifact:/tmp/bindings-artifact --user root") { + iC.inside("-v /tmp/${env.GIT_COMMIT}/entrypoint.sh:/entrypoint.sh:ro -v /tmp/${env.GIT_COMMIT}/bindings-artifact:/tmp/bindings-artifact") { bindings.doAndroidBindings(params.ABABIVersion) } } @@ -480,42 +439,39 @@ pipeline { script { def artifacts = load ".jenkinsci/artifacts.groovy" def commit = env.GIT_COMMIT - if (params.JavaBindings || REST_PR_CONDITIONS_SATISFIED == "true") { + if (params.JavaBindings) { javaBindingsFilePaths = [ '/tmp/${GIT_COMMIT}/bindings-artifact/java-bindings-*.zip' ] artifacts.uploadArtifacts(javaBindingsFilePaths, '/iroha/bindings/java') } - if (params.PythonBindings || REST_PR_CONDITIONS_SATISFIED == "true") { + if (params.PythonBindings) { pythonBindingsFilePaths = [ '/tmp/${GIT_COMMIT}/bindings-artifact/python-bindings-*.zip' ] artifacts.uploadArtifacts(pythonBindingsFilePaths, '/iroha/bindings/python') } - if (params.AndroidBindings || REST_PR_CONDITIONS_SATISFIED == "true") { + if (params.AndroidBindings) { androidBindingsFilePaths = [ '/tmp/${GIT_COMMIT}/bindings-artifact/android-bindings-*.zip' ] artifacts.uploadArtifacts(androidBindingsFilePaths, '/iroha/bindings/android') } } } cleanup { - sh "sudo rm -rf /tmp/${env.GIT_COMMIT}" + sh "rm -rf /tmp/${env.GIT_COMMIT}" cleanWs() } } } - stage ('windows_bindings') { + stage ('Windows bindings') { when { beforeAgent true - anyOf { - expression { return params.x86_64_win } - expression { return REST_PR_CONDITIONS_SATISFIED == "true" } - } + expression { return params.x86_64_win } } agent { label 'win' } steps { script { def bindings = load ".jenkinsci/bindings.groovy" - if (params.JavaBindings || REST_PR_CONDITIONS_SATISFIED == "true") { + if (params.JavaBindings) { bindings.doJavaBindings('windows', params.JBBuildType) } - if (params.PythonBindings || REST_PR_CONDITIONS_SATISFIED == "true") { + if (params.PythonBindings) { bindings.doPythonBindings('windows', params.PBBuildType) } } @@ -525,70 +481,20 @@ pipeline { script { def artifacts = load ".jenkinsci/artifacts.groovy" def commit = env.GIT_COMMIT - if (params.JavaBindings || REST_PR_CONDITIONS_SATISFIED == "true") { + if (params.JavaBindings) { javaBindingsFilePaths = [ '/tmp/${GIT_COMMIT}/bindings-artifact/java-bindings-*.zip' ] artifacts.uploadArtifacts(javaBindingsFilePaths, '/iroha/bindings/java') } - if (params.PythonBindings || REST_PR_CONDITIONS_SATISFIED == "true") { + if (params.PythonBindings) { pythonBindingsFilePaths = [ '/tmp/${GIT_COMMIT}/bindings-artifact/python-bindings-*.zip' ] artifacts.uploadArtifacts(pythonBindingsFilePaths, '/iroha/bindings/python') } } } - } - } - } - } - } - post { - success { - script { - // merge pull request if everything is ok and clean stale docker images stored on EFS - if ( params.merge_pr ) { - def merge = load ".jenkinsci/github-api.groovy" - if (merge.mergePullRequest()) { - currentBuild.result = "SUCCESS" - def clean = load ".jenkinsci/docker-cleanup.groovy" - clean.doStaleDockerImagesCleanup() - } - else { - currentBuild.result = "FAILURE" - } - } - } - } - cleanup { - script { - def post = load ".jenkinsci/post-step.groovy" - def clean = load ".jenkinsci/docker-cleanup.groovy" - def notify = load ".jenkinsci/notifications.groovy" - notify.notifyBuildResults() - - if (params.x86_64_linux || params.merge_pr) { - node ('x86_64_aws_test') { - post.cleanUp() - } - } - if (params.armv8_linux) { - node ('armv8') { - post.cleanUp() - clean.doDockerCleanup() - } - } - if (params.armv7_linux) { - node ('armv7') { - post.cleanUp() - clean.doDockerCleanup() - } - } - if (params.x86_64_macos || params.merge_pr) { - node ('mac') { - post.macCleanUp() - } - } - if (params.x86_64_win || params.merge_pr) { - node ('win') { - post.cleanUp() + cleanup { + sh "rm -rf /tmp/${env.GIT_COMMIT}" + cleanWs() + } } } } From f0e3e00e29c897206e54e8f5fe8edef45cf4d33f Mon Sep 17 00:00:00 2001 From: Sergei Date: Wed, 4 Jul 2018 18:34:23 +0700 Subject: [PATCH 24/97] Fix develop (#1534) Signed-off-by: Sergei --- .../validators/transaction_validator_test.cpp | 53 +++++++++++++++---- .../validators/validators_fixture.hpp | 2 +- 2 files changed, 45 insertions(+), 10 deletions(-) diff --git a/test/module/shared_model/validators/transaction_validator_test.cpp b/test/module/shared_model/validators/transaction_validator_test.cpp index 83c98ac511..34b72767aa 100644 --- a/test/module/shared_model/validators/transaction_validator_test.cpp +++ b/test/module/shared_model/validators/transaction_validator_test.cpp @@ -33,10 +33,10 @@ class TransactionValidatorTest : public ValidatorsTest { TestTransactionBuilder builder; auto tx = builder.creatorAccountId(creator_account_id) - .createdTime(created_time) - .quorum(1) - .build() - .getTransport(); + .createdTime(created_time) + .quorum(1) + .build() + .getTransport(); return tx; } shared_model::validation::DefaultTransactionValidator transaction_validator; @@ -111,13 +111,22 @@ TEST_F(TransactionValidatorTest, StatelessValidTest) { ASSERT_FALSE(answer.hasErrors()) << answer.reason(); } + /** - * @given transaction made of commands with valid fields - * @when commands validation is invoked - * @then answer has no errors + * @given Protobuf transaction object with unset command + * @when validate is called + * @then there is a error returned */ -TEST_F(TransactionValidatorTest, BatchValidTest) { - std::string creator_account_id = "admin@test"; +TEST_F(TransactionValidatorTest, UnsetCommand) { + iroha::protocol::Transaction tx = generateEmptyTransaction(); + tx.mutable_payload()->mutable_reduced_payload()->set_creator_account_id( + account_id); + tx.mutable_payload()->mutable_reduced_payload()->set_created_time( + created_time); + auto answer = transaction_validator.validate(proto::Transaction(tx)); + tx.mutable_payload()->mutable_reduced_payload()->add_commands(); + ASSERT_TRUE(answer.hasErrors()); +} /** * @given transaction made of commands with invalid fields @@ -155,3 +164,29 @@ TEST_F(TransactionValidatorTest, StatelessInvalidTest) { ASSERT_EQ(answer.getReasonsMap().size(), iroha::protocol::Command::descriptor()->field_count() + 1); } +/** + * @given transaction made of commands with valid fields + * @when commands validation is invoked + * @then answer has no errors + */ +TEST_F(TransactionValidatorTest, BatchValidTest) { + std::string creator_account_id = "admin@test"; + + TestTransactionBuilder builder; + auto tx = builder.creatorAccountId(creator_account_id) + .createdTime(created_time) + .quorum(1) + .batchMeta(interface::types::BatchType::ATOMIC, + std::vector()) + .createDomain("test", "test") + .build() + .getTransport(); + shared_model::validation::DefaultTransactionValidator transaction_validator; + auto result = proto::Transaction(iroha::protocol::Transaction(tx)); + auto answer = transaction_validator.validate(result); + + ASSERT_FALSE(answer.hasErrors()) << answer.reason(); + ASSERT_EQ(tx.payload().batch().type(), + static_cast(interface::types::BatchType::ATOMIC)); +} + diff --git a/test/module/shared_model/validators/validators_fixture.hpp b/test/module/shared_model/validators/validators_fixture.hpp index 5f69ce8259..04fd49aac9 100644 --- a/test/module/shared_model/validators/validators_fixture.hpp +++ b/test/module/shared_model/validators/validators_fixture.hpp @@ -160,7 +160,7 @@ class ValidatorsTest : public ::testing::Test { void *ptr = new std::shared_ptr(mcopy); std::shared_ptr *m = static_cast *>(ptr); - iterateContainerRecursive(*m, field_validators, field_op, validator); + this->iterateContainerRecursive(*m, field_validators, field_op, validator); } }); } From 2ea3efdf3aae5e6312ed60700a9d0f341facccf7 Mon Sep 17 00:00:00 2001 From: Akvinikym Date: Wed, 4 Jul 2018 16:34:55 +0300 Subject: [PATCH 25/97] Python example fix (#1529) Fixed a Python example, which caused exception when running. Signed-off-by: Akvinikym --- example/python/tx-example.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/example/python/tx-example.py b/example/python/tx-example.py index f2b9b8e80f..99106a0520 100644 --- a/example/python/tx-example.py +++ b/example/python/tx-example.py @@ -169,7 +169,7 @@ def grant_admin_to_add_detail_to_userone(): """ tx = tx_builder.creatorAccountId("userone@domain") \ .createdTime(current_time) \ - .grantPermission(creator, "can_set_my_account_detail") \ + .grantPermission(creator, iroha.Grantable_kSetMyAccountDetail) \ .build() send_tx(tx, user1_kp) From 631d3daa376a8eaae3ada2a0581d4fa3867ff862 Mon Sep 17 00:00:00 2001 From: Akvinikym Date: Wed, 4 Jul 2018 18:18:43 +0300 Subject: [PATCH 26/97] Stateful response (#1508) Stateful error responses are now written to the log and given to the client. Signed-off-by: Akvinikym --- .../impl/interactive_status_cli.cpp | 9 +- irohad/ametsuchi/impl/temporary_wsv_impl.cpp | 64 ++++----- irohad/ametsuchi/impl/temporary_wsv_impl.hpp | 4 +- irohad/ametsuchi/temporary_wsv.hpp | 14 +- irohad/execution/impl/command_executor.cpp | 3 +- irohad/main/application.cpp | 4 +- .../impl/peer_communication_service_impl.cpp | 13 +- .../impl/peer_communication_service_impl.hpp | 10 +- irohad/network/peer_communication_service.hpp | 10 ++ irohad/simulator/impl/simulator.cpp | 20 ++- irohad/simulator/impl/simulator.hpp | 5 +- .../simulator/verified_proposal_creator.hpp | 3 +- irohad/torii/impl/command_service.cpp | 16 ++- .../impl/transaction_processor_impl.cpp | 111 ++++++++++----- .../processor/transaction_processor_impl.hpp | 7 +- .../impl/stateful_validator_impl.cpp | 131 +++++++++--------- .../validation/stateful_validator_common.hpp | 38 +++-- .../proto_transaction_status_builder.cpp | 6 + .../proto_transaction_status_builder.hpp | 2 + .../transaction_status_builder.hpp | 6 + test/module/iroha-cli/client_test.cpp | 64 +++++++++ test/module/irohad/network/network_mocks.hpp | 5 + .../irohad/simulator/simulator_test.cpp | 83 ++++++++++- .../processor/transaction_processor_test.cpp | 44 ++++-- .../irohad/torii/torii_service_test.cpp | 49 ++++++- 25 files changed, 512 insertions(+), 209 deletions(-) diff --git a/iroha-cli/interactive/impl/interactive_status_cli.cpp b/iroha-cli/interactive/impl/interactive_status_cli.cpp index 6aedf781b4..2c14377bda 100644 --- a/iroha-cli/interactive/impl/interactive_status_cli.cpp +++ b/iroha-cli/interactive/impl/interactive_status_cli.cpp @@ -140,15 +140,20 @@ namespace iroha_cli { } auto status = iroha::protocol::TxStatus::NOT_RECEIVED; + iroha::protocol::ToriiResponse answer; if (iroha::hexstringToBytestring(txHash_)) { - status = CliClient(address.value().first, address.value().second) + answer = CliClient(address.value().first, address.value().second) .getTxStatus(*iroha::hexstringToBytestring(txHash_)) - .answer.tx_status(); + .answer; + status = answer.tx_status(); } std::string message; try { message = userMessageMap.at(status); + if (not answer.error_message().empty()) { + message += " " + answer.error_message(); + } } catch (const std::out_of_range &e) { message = "A problem detected while retrieving transaction status. Please " diff --git a/irohad/ametsuchi/impl/temporary_wsv_impl.cpp b/irohad/ametsuchi/impl/temporary_wsv_impl.cpp index 5045bf09df..e368dabd00 100644 --- a/irohad/ametsuchi/impl/temporary_wsv_impl.cpp +++ b/irohad/ametsuchi/impl/temporary_wsv_impl.cpp @@ -38,62 +38,58 @@ namespace iroha { transaction_->exec("BEGIN;"); } - expected::Result - TemporaryWsvImpl::apply( + expected::Result TemporaryWsvImpl::apply( const shared_model::interface::Transaction &tx, - std::function( + std::function( const shared_model::interface::Transaction &, WsvQuery &)> apply_function) { const auto &tx_creator = tx.creatorAccountId(); command_executor_->setCreatorAccountId(tx_creator); command_validator_->setCreatorAccountId(tx_creator); auto execute_command = [this](auto &command, size_t command_index) - -> expected::Result { + -> expected::Result { // Validate command - return expected::map_error( + return expected::map_error( boost::apply_visitor(*command_validator_, command.get()), [command_index](CommandError &error) { - return validation::CommandNameAndError{ - error.command_name, - (boost::format("stateful validation error: could " - "not validate " - "command with index %d: %s") - % command_index % error.toString()) - .str()}; + return validation::CommandError{error.command_name, + error.toString(), + true, + command_index}; }) - // Execute commands - .and_res(expected::map_error( + // Execute command + .and_res(expected::map_error( boost::apply_visitor(*command_executor_, command.get()), [command_index](CommandError &error) { - return validation::CommandNameAndError{ - error.command_name, - (boost::format("stateful validation error: could not " - "execute command with index %d: %s") - % command_index % error.toString()) - .str()}; + return validation::CommandError{error.command_name, + error.toString(), + true, + command_index}; })); }; transaction_->exec("SAVEPOINT savepoint_;"); - return apply_function(tx, *wsv_) | [this, &execute_command, &tx]() - -> expected::Result { - // check transaction's commands validness + return apply_function(tx, *wsv_) | + [this, + &execute_command, + &tx]() -> expected::Result { + // check transaction's commands validity const auto &commands = tx.commands(); - validation::CommandNameAndError cmd_name_error; + validation::CommandError cmd_error; for (size_t i = 0; i < commands.size(); ++i) { // in case of failed command, rollback and return - if (not execute_command(commands[i], i) - .match( - [](expected::Value &) { return true; }, - [&cmd_name_error]( - expected::Error - &error) { - cmd_name_error = error.error; - return false; - })) { + auto cmd_is_valid = + execute_command(commands[i], i) + .match([](expected::Value &) { return true; }, + [&cmd_error]( + expected::Error &error) { + cmd_error = error.error; + return false; + }); + if (not cmd_is_valid) { transaction_->exec("ROLLBACK TO SAVEPOINT savepoint_;"); - return expected::makeError(cmd_name_error); + return expected::makeError(cmd_error); } } // success diff --git a/irohad/ametsuchi/impl/temporary_wsv_impl.hpp b/irohad/ametsuchi/impl/temporary_wsv_impl.hpp index c4c27c5a4e..6a336ac096 100644 --- a/irohad/ametsuchi/impl/temporary_wsv_impl.hpp +++ b/irohad/ametsuchi/impl/temporary_wsv_impl.hpp @@ -33,9 +33,9 @@ namespace iroha { TemporaryWsvImpl(std::unique_ptr connection, std::unique_ptr transaction); - expected::Result apply( + expected::Result apply( const shared_model::interface::Transaction &, - std::function( + std::function( const shared_model::interface::Transaction &, WsvQuery &)> function) override; diff --git a/irohad/ametsuchi/temporary_wsv.hpp b/irohad/ametsuchi/temporary_wsv.hpp index f7ae5a6ef8..92157d7bea 100644 --- a/irohad/ametsuchi/temporary_wsv.hpp +++ b/irohad/ametsuchi/temporary_wsv.hpp @@ -20,9 +20,9 @@ #include +#include "validation/stateful_validator_common.hpp" #include "ametsuchi/wsv_command.hpp" #include "ametsuchi/wsv_query.hpp" -#include "validation/stateful_validator_common.hpp" namespace shared_model { namespace interface { @@ -45,17 +45,11 @@ namespace iroha { * @param transaction Transaction to be applied * @param function Function that specifies the logic used to apply the * transaction - * Function parameters: - * - Transaction @see transaction - * - WsvQuery - world state view query interface for temporary storage - * Function returns void result value, if transaction is successfully - * applied, and string result error otherwise - * @return void result value, if transaction was successfully applied, and - * vector of strings with errors of all failed command otherwise + * @return True if transaction was successfully applied, false otherwise */ - virtual expected::Result apply( + virtual expected::Result apply( const shared_model::interface::Transaction &, - std::function( + std::function( const shared_model::interface::Transaction &, WsvQuery &)> function) = 0; diff --git a/irohad/execution/impl/command_executor.cpp b/irohad/execution/impl/command_executor.cpp index b1b32f83c4..3d395c8b6e 100644 --- a/irohad/execution/impl/command_executor.cpp +++ b/irohad/execution/impl/command_executor.cpp @@ -36,7 +36,7 @@ namespace iroha { expected::Error makeCommandError( const std::string &error_message, - const std::string command_name) noexcept { + const std::string &command_name) noexcept { return expected::makeError(CommandError{command_name, error_message}); } @@ -1158,7 +1158,6 @@ namespace iroha { // Amount is formed wrong if (command.amount().precision() > asset.value()->precision()) { return makeCommandError( - "is valid command validation failed: account " + command.srcAccountId() + ", precision of command's " diff --git a/irohad/main/application.cpp b/irohad/main/application.cpp index 210428a2c9..e23db789bf 100644 --- a/irohad/main/application.cpp +++ b/irohad/main/application.cpp @@ -225,8 +225,8 @@ void Irohad::initSynchronizer() { * Initializing peer communication service */ void Irohad::initPeerCommunicationService() { - pcs = std::make_shared(ordering_gate, - synchronizer); + pcs = std::make_shared( + ordering_gate, synchronizer, simulator); pcs->on_proposal().subscribe( [this](auto) { log_->info("~~~~~~~~~| PROPOSAL ^_^ |~~~~~~~~~ "); }); diff --git a/irohad/network/impl/peer_communication_service_impl.cpp b/irohad/network/impl/peer_communication_service_impl.cpp index 27776e156a..58a029a000 100644 --- a/irohad/network/impl/peer_communication_service_impl.cpp +++ b/irohad/network/impl/peer_communication_service_impl.cpp @@ -20,9 +20,12 @@ namespace iroha { namespace network { PeerCommunicationServiceImpl::PeerCommunicationServiceImpl( std::shared_ptr ordering_gate, - std::shared_ptr synchronizer) + std::shared_ptr synchronizer, + std::shared_ptr + proposal_creator) : ordering_gate_(std::move(ordering_gate)), - synchronizer_(std::move(synchronizer)) { + synchronizer_(std::move(synchronizer)), + proposal_creator_(std::move(proposal_creator)) { log_ = logger::log("PCS"); } @@ -38,6 +41,12 @@ namespace iroha { return ordering_gate_->on_proposal(); } + rxcpp::observable< + std::shared_ptr> + PeerCommunicationServiceImpl::on_verified_proposal() const { + return proposal_creator_->on_verified_proposal(); + } + rxcpp::observable PeerCommunicationServiceImpl::on_commit() const { return synchronizer_->on_commit_chain(); } diff --git a/irohad/network/impl/peer_communication_service_impl.hpp b/irohad/network/impl/peer_communication_service_impl.hpp index 09e25613bf..8ac586efde 100644 --- a/irohad/network/impl/peer_communication_service_impl.hpp +++ b/irohad/network/impl/peer_communication_service_impl.hpp @@ -20,7 +20,9 @@ #include "network/ordering_gate.hpp" #include "network/peer_communication_service.hpp" +#include "simulator/verified_proposal_creator.hpp" #include "synchronizer/synchronizer.hpp" +#include "validation/stateful_validator_common.hpp" #include "logger/logger.hpp" @@ -30,7 +32,8 @@ namespace iroha { public: PeerCommunicationServiceImpl( std::shared_ptr ordering_gate, - std::shared_ptr synchronizer); + std::shared_ptr synchronizer, + std::shared_ptr proposal_creator); void propagate_transaction( std::shared_ptr @@ -39,11 +42,16 @@ namespace iroha { rxcpp::observable> on_proposal() const override; + rxcpp::observable< + std::shared_ptr> + on_verified_proposal() const override; + rxcpp::observable on_commit() const override; private: std::shared_ptr ordering_gate_; std::shared_ptr synchronizer_; + std::shared_ptr proposal_creator_; logger::Logger log_; }; } // namespace network diff --git a/irohad/network/peer_communication_service.hpp b/irohad/network/peer_communication_service.hpp index 9c5c76d75c..be4243abc1 100644 --- a/irohad/network/peer_communication_service.hpp +++ b/irohad/network/peer_communication_service.hpp @@ -20,6 +20,8 @@ #include +#include "validation/stateful_validator_common.hpp" + namespace shared_model { namespace interface { class Block; @@ -57,6 +59,14 @@ namespace iroha { std::shared_ptr> on_proposal() const = 0; + /** + * Event is triggered when verified proposal arrives + * @return verified proposal and list of stateful validation errors + */ + virtual rxcpp::observable< + std::shared_ptr> + on_verified_proposal() const = 0; + /** * Event is triggered when commit block arrives. * @return observable with sequence of committed blocks. diff --git a/irohad/simulator/impl/simulator.cpp b/irohad/simulator/impl/simulator.cpp index db68d96fde..7748126efc 100644 --- a/irohad/simulator/impl/simulator.cpp +++ b/irohad/simulator/impl/simulator.cpp @@ -48,9 +48,10 @@ namespace iroha { notifier_.get_observable().subscribe( verified_proposal_subscription_, - [this](const std::shared_ptr - &verified_proposal) { - this->process_verified_proposal(*verified_proposal); + [this](std::shared_ptr + verified_proposal_and_errors) { + this->process_verified_proposal( + *verified_proposal_and_errors->first); }); } @@ -59,7 +60,8 @@ namespace iroha { verified_proposal_subscription_.unsubscribe(); } - rxcpp::observable> + rxcpp::observable< + std::shared_ptr> Simulator::on_verified_proposal() { return notifier_.get_observable(); } @@ -94,14 +96,10 @@ namespace iroha { [&](expected::Value> &temporaryStorage) { auto validated_proposal_and_errors = - validator_->validate(proposal, *temporaryStorage.value); - // Temporary variant; errors are lost now, but then they are going - // to be handled upwards + std::make_shared( + validator_->validate(proposal, *temporaryStorage.value)); notifier_.get_subscriber().on_next( - validated_proposal_and_errors.first); - for (const auto &transaction_and_error: validated_proposal_and_errors.second) { - log_->error(transaction_and_error.first.second); - } + std::move(validated_proposal_and_errors)); }, [&](expected::Error &error) { log_->error(error.error); diff --git a/irohad/simulator/impl/simulator.hpp b/irohad/simulator/impl/simulator.hpp index 6709ec8c1c..59524fb4f4 100644 --- a/irohad/simulator/impl/simulator.hpp +++ b/irohad/simulator/impl/simulator.hpp @@ -49,7 +49,8 @@ namespace iroha { void process_proposal( const shared_model::interface::Proposal &proposal) override; - rxcpp::observable> + rxcpp::observable< + std::shared_ptr> on_verified_proposal() override; void process_verified_proposal( @@ -61,7 +62,7 @@ namespace iroha { private: // internal rxcpp::subjects::subject< - std::shared_ptr> + std::shared_ptr> notifier_; rxcpp::subjects::subject block_notifier_; diff --git a/irohad/simulator/verified_proposal_creator.hpp b/irohad/simulator/verified_proposal_creator.hpp index b8f7ac7fde..2fb8aae3d7 100644 --- a/irohad/simulator/verified_proposal_creator.hpp +++ b/irohad/simulator/verified_proposal_creator.hpp @@ -19,6 +19,7 @@ #define IROHA_VERIFIED_PROPOSAL_CREATOR_HPP #include +#include "validation/stateful_validator_common.hpp" namespace shared_model { namespace interface { @@ -46,7 +47,7 @@ namespace iroha { * @return */ virtual rxcpp::observable< - std::shared_ptr> + std::shared_ptr> on_verified_proposal() = 0; virtual ~VerifiedProposalCreator() = default; diff --git a/irohad/torii/impl/command_service.cpp b/irohad/torii/impl/command_service.cpp index 607c8431ee..5a682f9932 100644 --- a/irohad/torii/impl/command_service.cpp +++ b/irohad/torii/impl/command_service.cpp @@ -47,11 +47,18 @@ namespace torii { log_(logger::log("CommandService")) { // Notifier for all clients tx_processor_->transactionNotifier().subscribe([this](auto iroha_response) { - // Find response in cache + // find response for this tx in cache; if status of received response + // isn't "greater" than cached one, dismiss received one auto proto_response = std::static_pointer_cast( iroha_response); auto tx_hash = proto_response->transactionHash(); + auto cached_tx_state = cache_->findItem(tx_hash); + if (cached_tx_state + and proto_response->getTransport().tx_status() + <= cached_tx_state->tx_status()) { + return; + } cache_->addItem(tx_hash, proto_response->getTransport()); }); } @@ -174,7 +181,8 @@ namespace torii { }) .subscribe( subscription, - [&](std::shared_ptr + [&, finished]( + std::shared_ptr iroha_response) { auto proto_response = std::static_pointer_cast< shared_model::proto::TransactionResponse>(iroha_response); @@ -207,7 +215,7 @@ namespace torii { log_->debug("StatusStream waiting finish, hash: {}", request_hash->hex()); - if (not *finished) { + if (not*finished) { resp = cache_->findItem(shared_model::crypto::Hash(request.tx_hash())); if (not resp) { log_->warn("StatusStream request processing timeout, hash: {}", @@ -228,7 +236,7 @@ namespace torii { cv->wait_for(lock, 2 * proposal_delay_); /// status can be in the cache if it was finalized before we subscribed - if (not *finished) { + if (not*finished) { log_->debug("Transaction {} still not finished", request_hash->hex()); resp = cache_->findItem(shared_model::crypto::Hash(request.tx_hash())); diff --git a/irohad/torii/processor/impl/transaction_processor_impl.cpp b/irohad/torii/processor/impl/transaction_processor_impl.cpp index 2e118fc33d..f53c994dcd 100644 --- a/irohad/torii/processor/impl/transaction_processor_impl.cpp +++ b/irohad/torii/processor/impl/transaction_processor_impl.cpp @@ -17,26 +17,46 @@ #include "torii/processor/transaction_processor_impl.hpp" +#include + #include "backend/protobuf/transaction.hpp" #include "interfaces/iroha_internal/block.hpp" #include "interfaces/iroha_internal/proposal.hpp" +#include "validation/stateful_validator_common.hpp" namespace iroha { namespace torii { using network::PeerCommunicationService; + static std::string composeErrorMessage( + const validation::TransactionError &tx_error) { + if (not tx_error.first.tx_passed_initial_validation) { + return (boost::format("Stateful validation error: transaction %s " + "did not pass initial verification: " + "checking '%s', error message '%s'") + % tx_error.second.hex() % tx_error.first.name + % tx_error.first.error) + .str(); + } + return (boost::format("Stateful validation error in transaction %s: " + "command '%s' with index '%d' did not pass " + "verification with error '%s'") + % tx_error.second.hex() % tx_error.first.name + % tx_error.first.index % tx_error.first.error) + .str(); + } + TransactionProcessorImpl::TransactionProcessorImpl( std::shared_ptr pcs, std::shared_ptr mst_processor) : pcs_(std::move(pcs)), mst_processor_(std::move(mst_processor)) { log_ = logger::log("TxProcessor"); - // insert all txs from proposal to proposal set + // notify about stateless success pcs_->on_proposal().subscribe([this](auto model_proposal) { for (const auto &tx : model_proposal->transactions()) { - auto hash = tx.hash(); - proposal_set_.insert(hash); + const auto &hash = tx.hash(); log_->info("on proposal stateless success: {}", hash.hex()); // different on_next() calls (this one and below) can happen in // different threads and we don't expect emitting them concurrently @@ -49,49 +69,63 @@ namespace iroha { } }); - // move commited txs from proposal to candidate map + // process stateful validation results + pcs_->on_verified_proposal().subscribe( + [this](std::shared_ptr + proposal_and_errors) { + // notify about failed txs + const auto &errors = proposal_and_errors->second; + std::lock_guard lock(notifier_mutex_); + for (const auto &tx_error : errors) { + auto error_msg = composeErrorMessage(tx_error); + log_->info(error_msg); + notifier_.get_subscriber().on_next( + shared_model::builder::DefaultTransactionStatusBuilder() + .statefulValidationFailed() + .txHash(tx_error.second) + .errorMsg(error_msg) + .build()); + } + // notify about success txs + for (const auto &successful_tx : + proposal_and_errors->first->transactions()) { + log_->info("on stateful validation success: {}", + successful_tx.hash().hex()); + notifier_.get_subscriber().on_next( + shared_model::builder::DefaultTransactionStatusBuilder() + .statefulValidationSuccess() + .txHash(successful_tx.hash()) + .build()); + } + }); + + // commit transactions pcs_->on_commit().subscribe([this](Commit blocks) { blocks.subscribe( - // on next.. + // on next [this](auto model_block) { - for (const auto &tx : model_block->transactions()) { - auto hash = tx.hash(); - if (this->proposal_set_.find(hash) != proposal_set_.end()) { - proposal_set_.erase(hash); - candidate_set_.insert(hash); - log_->info("on commit stateful success: {}", hash.hex()); - std::lock_guard lock(notifier_mutex_); - notifier_.get_subscriber().on_next( - shared_model::builder::DefaultTransactionStatusBuilder() - .statefulValidationSuccess() - .txHash(hash) - .build()); - } - } + current_txs_hashes_.reserve(model_block->transactions().size()); + std::transform(model_block->transactions().begin(), + model_block->transactions().end(), + std::back_inserter(current_txs_hashes_), + [](const auto &tx) { return tx.hash(); }); }, // on complete [this]() { - for (auto &tx_hash : proposal_set_) { - log_->info("on commit stateful failed: {}", tx_hash.hex()); + if (current_txs_hashes_.empty()) { + log_->info("there are no transactions to be committed"); + } else { std::lock_guard lock(notifier_mutex_); - notifier_.get_subscriber().on_next( - shared_model::builder::DefaultTransactionStatusBuilder() - .statefulValidationFailed() - .txHash(tx_hash) - .build()); - } - proposal_set_.clear(); - - for (auto &tx_hash : candidate_set_) { - log_->info("on commit committed: {}", tx_hash.hex()); - std::lock_guard lock(notifier_mutex_); - notifier_.get_subscriber().on_next( - shared_model::builder::DefaultTransactionStatusBuilder() - .committed() - .txHash(tx_hash) - .build()); + for (const auto &tx_hash : current_txs_hashes_) { + log_->info("on commit committed: {}", tx_hash.hex()); + notifier_.get_subscriber().on_next( + shared_model::builder::DefaultTransactionStatusBuilder() + .committed() + .txHash(tx_hash) + .build()); + } } - candidate_set_.clear(); + current_txs_hashes_.clear(); }); }); @@ -129,5 +163,6 @@ namespace iroha { TransactionProcessorImpl::transactionNotifier() { return notifier_.get_observable(); } + } // namespace torii } // namespace iroha diff --git a/irohad/torii/processor/transaction_processor_impl.hpp b/irohad/torii/processor/transaction_processor_impl.hpp index 031e0736fa..6f8915ce7a 100644 --- a/irohad/torii/processor/transaction_processor_impl.hpp +++ b/irohad/torii/processor/transaction_processor_impl.hpp @@ -52,12 +52,7 @@ namespace iroha { // processing std::shared_ptr mst_processor_; - std::unordered_set - proposal_set_; - std::unordered_set - candidate_set_; + std::vector current_txs_hashes_; // internal rxcpp::subjects::subject< diff --git a/irohad/validation/impl/stateful_validator_impl.cpp b/irohad/validation/impl/stateful_validator_impl.cpp index bcce329c06..6c9b8ad659 100644 --- a/irohad/validation/impl/stateful_validator_impl.cpp +++ b/irohad/validation/impl/stateful_validator_impl.cpp @@ -22,6 +22,7 @@ #include #include #include + #include "builders/protobuf/proposal.hpp" #include "common/result.hpp" #include "validation/utils.hpp" @@ -66,82 +67,82 @@ namespace iroha { log_->info("transactions in proposal: {}", proposal.transactions().size()); auto checking_transaction = [](const auto &tx, auto &queries) { - return expected::Result( + return expected::Result( [&]() -> expected::Result< - std::shared_ptr, - validation::CommandNameAndError> { + std::shared_ptr, + validation::CommandError> { // Check if tx creator has account auto account = queries.getAccount(tx.creatorAccountId()); if (account) { return expected::makeValue(*account); } - return expected::makeError(validation::CommandNameAndError{ - "Initial transaction verification: no such account", - (boost::format("stateful validator error: could not fetch " - "account with id %s") - % tx.creatorAccountId()) - .str()}); + return expected::makeError( + validation::CommandError{"looking up tx creator's account", + (boost::format("could not fetch " + "account with id %s") + % tx.creatorAccountId()) + .str(), + false}); }() | [&](const auto &account) - -> expected::Result< - std::vector< - shared_model::interface::types::PubkeyType>, - validation::CommandNameAndError> { - // Check if account has signatories and quorum to execute - // transaction - if (boost::size(tx.signatures()) >= account->quorum()) { - auto signatories = - queries.getSignatories(tx.creatorAccountId()); - if (signatories) { - return expected::makeValue(*signatories); - } - return expected::makeError(validation::CommandNameAndError{ - "Initial transaction verification: could not fetch " - "signatories", - (boost::format("stateful validator error: could not fetch " - "signatories of " - "account %s") - % tx.creatorAccountId()) - .str()}); - } - return expected::makeError(validation::CommandNameAndError{ - "Initial transaction verification: not enough signatures", - (boost::format( - "stateful validator error: not enough " - "signatures in transaction; account's quorum %d, " - "transaction's " - "signatures amount %d") - % account->quorum() % boost::size(tx.signatures())) - .str()}); - } | [&tx](const auto &signatories) - -> expected::Result { + -> expected::Result< + std::vector< + shared_model::interface::types::PubkeyType>, + validation::CommandError> { + // Check if account has signatories and quorum to execute + // transaction + if (boost::size(tx.signatures()) >= account->quorum()) { + auto signatories = + queries.getSignatories(tx.creatorAccountId()); + if (signatories) { + return expected::makeValue(*signatories); + } + return expected::makeError(validation::CommandError{ + "looking up tx creator's signatories", + (boost::format("could not fetch " + "signatories of " + "account %s") + % tx.creatorAccountId()) + .str(), + false}); + } + return expected::makeError(validation::CommandError{ + "comparing number of tx signatures to account's quorum", + (boost::format( + "not enough " + "signatures in transaction; account's quorum %d, " + "transaction's " + "signatures amount %d") + % account->quorum() % boost::size(tx.signatures())) + .str(), + false}); + } | [&tx](const auto &signatories) + -> expected::Result { // Check if signatures in transaction are in account // signatory if (signaturesSubset(tx.signatures(), signatories)) { return {}; } - return expected::makeError(validation::CommandNameAndError{ - "Initial transaction verification: signatures are not " - "account's signatories", - formSignaturesErrorMsg(tx.signatures(), signatories)}); + return expected::makeError(validation::CommandError{ + "signatures are a subset of signatories", + formSignaturesErrorMsg(tx.signatures(), signatories), + false}); }); }; // Filter only valid transactions and accumulate errors auto transactions_errors_log = validation::TransactionsErrors{}; auto filter = [&temporaryWsv, - checking_transaction, - &transactions_errors_log](auto &tx) { + checking_transaction, + &transactions_errors_log](auto &tx) { return temporaryWsv.apply(tx, checking_transaction) - .match( - [](expected::Value &) { return true; }, - [&transactions_errors_log, - &tx](expected::Error &error) { - transactions_errors_log.push_back( - std::make_pair(error.error, tx.hash())); - return false; - }); + .match([](expected::Value &) { return true; }, + [&transactions_errors_log, + &tx](expected::Error &error) { + transactions_errors_log.push_back( + std::make_pair(error.error, tx.hash())); + return false; + }); }; // TODO: kamilsa IR-1010 20.02.2018 rework validation logic, so that this @@ -149,21 +150,21 @@ namespace iroha { // transport auto valid_proto_txs = proposal.transactions() | boost::adaptors::filtered(filter) - | boost::adaptors::transformed([](auto &tx) { - return static_cast(tx); - }); + | boost::adaptors::transformed([](auto &tx) { + return static_cast(tx); + }); auto validated_proposal = shared_model::proto::ProposalBuilder() - .createdTime(proposal.createdTime()) - .height(proposal.height()) - .transactions(valid_proto_txs) - .createdTime(proposal.createdTime()) - .build(); + .createdTime(proposal.createdTime()) + .height(proposal.height()) + .transactions(valid_proto_txs) + .createdTime(proposal.createdTime()) + .build(); log_->info("transactions in verified proposal: {}", validated_proposal.transactions().size()); return std::make_pair(std::make_shared( - validated_proposal.getTransport()), + validated_proposal.getTransport()), transactions_errors_log); } diff --git a/irohad/validation/stateful_validator_common.hpp b/irohad/validation/stateful_validator_common.hpp index af57a695c1..6f3ac85518 100644 --- a/irohad/validation/stateful_validator_common.hpp +++ b/irohad/validation/stateful_validator_common.hpp @@ -6,32 +6,42 @@ #ifndef IROHA_STATEFUL_VALIDATOR_COMMON_HPP #define IROHA_STATEFUL_VALIDATOR_COMMON_HPP -#include "common/types.hpp" +#include + +#include "interfaces/common_objects/types.hpp" namespace shared_model { namespace interface { class Proposal; } -} +} // namespace shared_model namespace iroha { namespace validation { - /// Name of the failed command - using CommandName = std::string; + /// Type of command error report + struct CommandError { + /// Name of the failed command + std::string name; + + /// Error, with which the command failed + std::string error; + + /// Shows, if transaction has passed initial validation + bool tx_passed_initial_validation; - /// Error, with which the command failed - using CommandError = std::string; + /// Position of the failed command in transaction + size_t index = 0; + }; - /// Failed command's name and error - using CommandNameAndError = std::pair; + /// Type of transaction error, which appeared during validation + /// process; contains names of commands, commands errors themselves, + /// commands indices and transaction hashes + using TransactionError = + std::pair; - /// Type of per-transaction errors, which appeared during validation - /// process; contains names of commands, commands errors themselves and - /// transaction hashes - using TransactionsErrors = - std::vector>; + /// Collection of transactions errors + using TransactionsErrors = std::vector; /// Type of verified proposal and errors appeared in the process; first /// dimension of errors vector is transaction, second is error itself with diff --git a/shared_model/builders/protobuf/transaction_responses/proto_transaction_status_builder.cpp b/shared_model/builders/protobuf/transaction_responses/proto_transaction_status_builder.cpp index d364563413..fd7feeefbb 100644 --- a/shared_model/builders/protobuf/transaction_responses/proto_transaction_status_builder.cpp +++ b/shared_model/builders/protobuf/transaction_responses/proto_transaction_status_builder.cpp @@ -88,5 +88,11 @@ namespace shared_model { return copy; } + TransactionStatusBuilder TransactionStatusBuilder::errorMsg(const std::string &msg) { + TransactionStatusBuilder copy(*this); + copy.tx_response_.set_error_message(msg); + return copy; + } + } // namespace proto } // namespace shared_model diff --git a/shared_model/builders/protobuf/transaction_responses/proto_transaction_status_builder.hpp b/shared_model/builders/protobuf/transaction_responses/proto_transaction_status_builder.hpp index 06d7dcf973..4c4765bb49 100644 --- a/shared_model/builders/protobuf/transaction_responses/proto_transaction_status_builder.hpp +++ b/shared_model/builders/protobuf/transaction_responses/proto_transaction_status_builder.hpp @@ -44,6 +44,8 @@ namespace shared_model { TransactionStatusBuilder txHash(const crypto::Hash &hash); + TransactionStatusBuilder errorMsg(const std::string &msg); + private: iroha::protocol::ToriiResponse tx_response_; }; diff --git a/shared_model/builders/transaction_responses/transaction_status_builder.hpp b/shared_model/builders/transaction_responses/transaction_status_builder.hpp index b08b7c271c..58f8592965 100644 --- a/shared_model/builders/transaction_responses/transaction_status_builder.hpp +++ b/shared_model/builders/transaction_responses/transaction_status_builder.hpp @@ -85,6 +85,12 @@ namespace shared_model { return copy; } + TransactionStatusBuilder errorMsg(const std::string &msg) { + TransactionStatusBuilder copy(*this); + copy.builder_ = this->builder_.errorMsg(msg); + return copy; + } + private: BuilderImpl builder_; }; diff --git a/test/module/iroha-cli/client_test.cpp b/test/module/iroha-cli/client_test.cpp index 2f43a186ca..cabda937bc 100644 --- a/test/module/iroha-cli/client_test.cpp +++ b/test/module/iroha-cli/client_test.cpp @@ -16,11 +16,13 @@ */ #include "builders/protobuf/common_objects/proto_account_builder.hpp" +#include "builders/protobuf/proposal.hpp" #include "model/sha3_hash.hpp" #include "module/irohad/ametsuchi/ametsuchi_mocks.hpp" #include "module/irohad/multi_sig_transactions/mst_mocks.hpp" #include "module/irohad/network/network_mocks.hpp" #include "module/irohad/validation/validation_mocks.hpp" +#include "module/shared_model/builders/protobuf/test_proposal_builder.hpp" #include "module/shared_model/builders/protobuf/test_query_builder.hpp" #include "module/shared_model/builders/protobuf/test_transaction_builder.hpp" @@ -74,6 +76,8 @@ class ClientServerTest : public testing::Test { .WillRepeatedly(Return(prop_notifier.get_observable())); EXPECT_CALL(*pcsMock, on_commit()) .WillRepeatedly(Return(commit_notifier.get_observable())); + EXPECT_CALL(*pcsMock, on_verified_proposal()) + .WillRepeatedly(Return(verified_prop_notifier.get_observable())); EXPECT_CALL(*mst, onPreparedTransactionsImpl()) .WillRepeatedly(Return(mst_prepared_notifier.get_observable())); @@ -119,6 +123,9 @@ class ClientServerTest : public testing::Test { std::shared_ptr pcsMock; std::shared_ptr mst; + rxcpp::subjects::subject< + std::shared_ptr> + verified_prop_notifier; rxcpp::subjects::subject mst_prepared_notifier; rxcpp::subjects::subject mst_expired_notifier; @@ -188,6 +195,63 @@ TEST_F(ClientServerTest, SendTxWhenStatelessInvalid) { ASSERT_NE(res.answer.error_message().size(), 0); } +/** + * This test checks, if tx, which did not pass stateful validation, is shown to + * client with a corresponding status and error message + * + * @given real client and mocked pcs + * @when sending a stateless valid transaction @and failing it at stateful + * validation + * @then ensure that client sees: + * - status of this transaction as STATEFUL_VALIDATION_FAILED + * - error message is the same, as the one with which transaction was + * failed + */ +TEST_F(ClientServerTest, SendTxWhenStatefulInvalid) { + iroha_cli::CliClient client(ip, port); + EXPECT_CALL(*pcsMock, propagate_transaction(_)).Times(1); + + // creating stateful invalid tx + auto tx = TransactionBuilder() + .creatorAccountId("some@account") + .createdTime(iroha::time::now()) + .transferAsset("some@account", + "another@account", + "doge#doge", + "some transfer", + "100.0") + .quorum(1) + .build() + .signAndAddSignature( + shared_model::crypto::DefaultCryptoAlgorithmType:: + generateKeypair()) + .finish(); + ASSERT_EQ(client.sendTx(tx).answer, iroha_cli::CliClient::OK); + + // fail the tx + auto verified_proposal = std::make_shared( + TestProposalBuilder().height(0).createdTime(iroha::time::now()).build()); + verified_prop_notifier.get_subscriber().on_next( + std::make_shared( + std::make_pair(verified_proposal, + iroha::validation::TransactionsErrors{std::make_pair( + iroha::validation::CommandError{ + "CommandName", "CommandError", true, 2}, + tx.hash())}))); + auto stringified_error = "Stateful validation error in transaction " + + tx.hash().hex() + ": command 'CommandName' with " + "index '2' did not pass verification with " + "error 'CommandError'"; + + // check it really failed with specific message + auto answer = + client.getTxStatus(shared_model::crypto::toBinaryString(tx.hash())) + .answer; + ASSERT_EQ(answer.tx_status(), + iroha::protocol::TxStatus::STATEFUL_VALIDATION_FAILED); + ASSERT_EQ(answer.error_message(), stringified_error); +} + TEST_F(ClientServerTest, SendQueryWhenInvalidJson) { iroha_cli::CliClient client(ip, port); // Must not call stateful validation since json is invalid and shouldn't be diff --git a/test/module/irohad/network/network_mocks.hpp b/test/module/irohad/network/network_mocks.hpp index 9c7e0bcb01..62c540a10b 100644 --- a/test/module/irohad/network/network_mocks.hpp +++ b/test/module/irohad/network/network_mocks.hpp @@ -44,6 +44,11 @@ namespace iroha { std::shared_ptr>()); MOCK_CONST_METHOD0(on_commit, rxcpp::observable()); + + MOCK_CONST_METHOD0( + on_verified_proposal, + rxcpp::observable< + std::shared_ptr>()); }; class MockBlockLoader : public BlockLoader { diff --git a/test/module/irohad/simulator/simulator_test.cpp b/test/module/irohad/simulator/simulator_test.cpp index 926067beae..b8ee71a404 100644 --- a/test/module/irohad/simulator/simulator_test.cpp +++ b/test/module/irohad/simulator/simulator_test.cpp @@ -157,8 +157,10 @@ TEST_F(SimulatorTest, ValidWhenPreviousBlock) { auto proposal_wrapper = make_test_subscriber(simulator->on_verified_proposal(), 1); proposal_wrapper.subscribe([&proposal](auto verified_proposal) { - ASSERT_EQ(verified_proposal->height(), proposal->height()); - ASSERT_EQ(verified_proposal->transactions(), proposal->transactions()); + ASSERT_EQ(verified_proposal->first->height(), proposal->height()); + ASSERT_EQ(verified_proposal->first->transactions(), + proposal->transactions()); + ASSERT_TRUE(verified_proposal->second.empty()); }); auto block_wrapper = @@ -250,3 +252,80 @@ TEST_F(SimulatorTest, FailWhenSameAsProposalHeight) { ASSERT_TRUE(proposal_wrapper.validate()); ASSERT_TRUE(block_wrapper.validate()); } + +/** + * Checks, that after failing a certain number of transactions in a proposal, + * returned verified proposal will have only valid transactions + * + * @given proposal consisting of several transactions + * @when failing some of the transactions in that proposal + * @then verified proposal consists of txs we did not fail + */ +TEST_F(SimulatorTest, RightNumberOfFailedTxs) { + // create a 3-height proposal, but validator returns only a 2-height verified + // proposal + auto tx = shared_model::proto::TransactionBuilder() + .createdTime(iroha::time::now()) + .creatorAccountId("admin@ru") + .addAssetQuantity("coin#coin", "1.0") + .quorum(1) + .build() + .signAndAddSignature( + shared_model::crypto::DefaultCryptoAlgorithmType:: + generateKeypair()) + .finish(); + + std::vector txs = {tx, tx, tx}; + auto proposal = std::make_shared( + shared_model::proto::ProposalBuilder() + .height(3) + .createdTime(iroha::time::now()) + .transactions(txs) + .build()); + auto verified_proposal = std::make_shared( + shared_model::proto::ProposalBuilder() + .height(2) + .createdTime(iroha::time::now()) + .transactions(std::vector{tx}) + .build()); + auto tx_errors = iroha::validation::TransactionsErrors{ + std::make_pair(CommandError{"SomeCommand", "SomeError", true}, + shared_model::crypto::Hash(std::string(32, '0'))), + std::make_pair(CommandError{"SomeCommand", "SomeError", true}, + shared_model::crypto::Hash(std::string(32, '0')))}; + shared_model::proto::Block block = makeBlock(proposal->height() - 1); + + EXPECT_CALL(*factory, createTemporaryWsv()).Times(1); + EXPECT_CALL(*query, getTopBlock()) + .WillOnce(Return(expected::makeValue(wBlock(clone(block))))); + + EXPECT_CALL(*query, getTopBlockHeight()).WillOnce(Return(2)); + + EXPECT_CALL(*validator, validate(_, _)) + .WillOnce(Return(std::make_pair(verified_proposal, tx_errors))); + + EXPECT_CALL(*ordering_gate, on_proposal()) + .WillOnce(Return(rxcpp::observable<>::empty< + std::shared_ptr>())); + + EXPECT_CALL(*shared_model::crypto::crypto_signer_expecter, + sign(A())) + .Times(1); + + init(); + + auto proposal_wrapper = + make_test_subscriber(simulator->on_verified_proposal(), 1); + proposal_wrapper.subscribe([&verified_proposal, + &tx_errors](auto verified_proposal_) { + // assure that txs in verified proposal do not include failed ones + ASSERT_EQ(verified_proposal_->first->height(), verified_proposal->height()); + ASSERT_EQ(verified_proposal_->first->transactions(), + verified_proposal->transactions()); + ASSERT_TRUE(verified_proposal_->second.size() == tx_errors.size()); + }); + + simulator->process_proposal(*proposal); + + ASSERT_TRUE(proposal_wrapper.validate()); +} diff --git a/test/module/irohad/torii/processor/transaction_processor_test.cpp b/test/module/irohad/torii/processor/transaction_processor_test.cpp index c231bf36d3..157923f328 100644 --- a/test/module/irohad/torii/processor/transaction_processor_test.cpp +++ b/test/module/irohad/torii/processor/transaction_processor_test.cpp @@ -35,6 +35,8 @@ class TransactionProcessorTest : public ::testing::Test { .WillRepeatedly(Return(prop_notifier.get_observable())); EXPECT_CALL(*pcs, on_commit()) .WillRepeatedly(Return(commit_notifier.get_observable())); + EXPECT_CALL(*pcs, on_verified_proposal()) + .WillRepeatedly(Return(verified_prop_notifier.get_observable())); EXPECT_CALL(*mp, onPreparedTransactionsImpl()) .WillRepeatedly(Return(mst_prepared_notifier.get_observable())); @@ -70,8 +72,7 @@ class TransactionProcessorTest : public ::testing::Test { auto tx_status = status_map.find(tx.hash()); ASSERT_NE(tx_status, status_map.end()); ASSERT_NO_THROW(boost::apply_visitor( - framework::SpecifiedVisitor(), - tx_status->second->get())); + framework::SpecifiedVisitor(), tx_status->second->get())); } } @@ -90,6 +91,9 @@ class TransactionProcessorTest : public ::testing::Test { rxcpp::subjects::subject> prop_notifier; rxcpp::subjects::subject commit_notifier; + rxcpp::subjects::subject< + std::shared_ptr> + verified_prop_notifier; const size_t proposal_size = 5; const size_t block_size = 3; @@ -174,6 +178,11 @@ TEST_F(TransactionProcessorTest, TransactionProcessorBlockCreatedTest) { prop_notifier.get_subscriber().on_next(proposal); prop_notifier.get_subscriber().on_completed(); + // empty transactions errors - all txs are valid + verified_prop_notifier.get_subscriber().on_next( + std::make_shared( + std::make_pair(proposal, iroha::validation::TransactionsErrors{}))); + auto block = TestBlockBuilder().transactions(txs).build(); // 2. Create block and notify transaction processor about it @@ -233,6 +242,11 @@ TEST_F(TransactionProcessorTest, TransactionProcessorOnCommitTest) { prop_notifier.get_subscriber().on_next(proposal); prop_notifier.get_subscriber().on_completed(); + // empty transactions errors - all txs are valid + verified_prop_notifier.get_subscriber().on_next( + std::make_shared( + std::make_pair(proposal, iroha::validation::TransactionsErrors{}))); + auto block = TestBlockBuilder().transactions(txs).build(); // 2. Create block and notify transaction processor about it @@ -250,9 +264,10 @@ TEST_F(TransactionProcessorTest, TransactionProcessorOnCommitTest) { * @given transaction processor * @when transactions compose proposal which is sent to peer * communication service @and some transactions became part of block, while some - * were not committed - * @then for every transaction from block COMMIT status is returned @and for - * every transaction not from block STATEFUL_INVALID_STATUS was returned + * were not committed, failing stateful validation + * @then for every transaction from block COMMIT status is returned @and + * for every transaction, which failed stateful validation, + * STATEFUL_INVALID_STATUS status is returned */ TEST_F(TransactionProcessorTest, TransactionProcessorInvalidTxsTest) { std::vector block_txs; @@ -263,9 +278,8 @@ TEST_F(TransactionProcessorTest, TransactionProcessorInvalidTxsTest) { status_builder.notReceived().txHash(tx.hash()).build(); } - std::vector - invalid_txs; // transactions will be stateful invalid if appeared - // in proposal but didn't appear in block + std::vector invalid_txs; + for (size_t i = block_size; i < proposal_size; i++) { auto &&tx = TestTransactionBuilder().createdTime(i).build(); invalid_txs.push_back(tx); @@ -296,6 +310,20 @@ TEST_F(TransactionProcessorTest, TransactionProcessorInvalidTxsTest) { prop_notifier.get_subscriber().on_next(proposal); prop_notifier.get_subscriber().on_completed(); + // trigger the verified event with txs, which we want to fail, as errors + auto verified_proposal = std::make_shared( + TestProposalBuilder().transactions(block_txs).build()); + auto txs_errors = iroha::validation::TransactionsErrors{}; + for (size_t i = 0; i < invalid_txs.size(); ++i) { + txs_errors.push_back(std::make_pair( + iroha::validation::CommandError{ + "SomeCommandName", "SomeCommandError", true, i}, + invalid_txs[i].hash())); + } + verified_prop_notifier.get_subscriber().on_next( + std::make_shared( + std::make_pair(verified_proposal, txs_errors))); + auto block = TestBlockBuilder().transactions(block_txs).build(); Commit single_commit = rxcpp::observable<>::just( diff --git a/test/module/irohad/torii/torii_service_test.cpp b/test/module/irohad/torii/torii_service_test.cpp index d0a735406c..d791895ca8 100644 --- a/test/module/irohad/torii/torii_service_test.cpp +++ b/test/module/irohad/torii/torii_service_test.cpp @@ -51,8 +51,13 @@ class CustomPeerCommunicationServiceMock : public PeerCommunicationService { CustomPeerCommunicationServiceMock( rxcpp::subjects::subject< std::shared_ptr> prop_notifier, - rxcpp::subjects::subject commit_notifier) - : prop_notifier_(prop_notifier), commit_notifier_(commit_notifier){}; + rxcpp::subjects::subject commit_notifier, + rxcpp::subjects::subject< + std::shared_ptr> + verified_prop_notifier) + : prop_notifier_(prop_notifier), + commit_notifier_(commit_notifier), + verified_prop_notifier_(verified_prop_notifier){}; void propagate_transaction( std::shared_ptr transaction) @@ -66,10 +71,19 @@ class CustomPeerCommunicationServiceMock : public PeerCommunicationService { return commit_notifier_.get_observable(); } + rxcpp::observable< + std::shared_ptr> + on_verified_proposal() const override { + return verified_prop_notifier_.get_observable(); + }; + private: rxcpp::subjects::subject> prop_notifier_; rxcpp::subjects::subject commit_notifier_; + rxcpp::subjects::subject< + std::shared_ptr> + verified_prop_notifier_; }; class ToriiServiceTest : public testing::Test { @@ -79,7 +93,7 @@ class ToriiServiceTest : public testing::Test { // ----------- Command Service -------------- pcsMock = std::make_shared( - prop_notifier_, commit_notifier_); + prop_notifier_, commit_notifier_, verified_prop_notifier_); mst = std::make_shared(); wsv_query = std::make_shared(); block_query = std::make_shared(); @@ -122,6 +136,9 @@ class ToriiServiceTest : public testing::Test { rxcpp::subjects::subject> prop_notifier_; rxcpp::subjects::subject commit_notifier_; + rxcpp::subjects::subject< + std::shared_ptr> + verified_prop_notifier_; rxcpp::subjects::subject mst_prepared_notifier; rxcpp::subjects::subject mst_expired_notifier; @@ -254,6 +271,7 @@ TEST_F(ToriiServiceTest, StatusWhenBlocking) { } // create block from the all transactions but the last one + auto failed_tx_hash = txs.back().hash(); txs.pop_back(); auto block = clone(TestBlockBuilder() @@ -263,6 +281,27 @@ TEST_F(ToriiServiceTest, StatusWhenBlocking) { .prevHash(shared_model::crypto::Hash(std::string(32, '0'))) .build()); + // notify the verified proposal event about txs, which passed stateful + // validation + auto verified_proposal = std::make_shared( + TestProposalBuilder() + .height(1) + .createdTime(iroha::time::now()) + .transactions(txs) + .build()); + auto errors = iroha::validation::TransactionsErrors{std::make_pair( + iroha::validation::CommandError{ + "FailedCommand", "stateful validation failed", true, 2}, + failed_tx_hash)}; + auto stringified_error = "Stateful validation error in transaction " + + failed_tx_hash.hex() + ": " + "command 'FailedCommand' with index '2' " + "did not pass verification with error 'stateful " + "validation failed'"; + verified_prop_notifier_.get_subscriber().on_next( + std::make_shared( + std::make_pair(verified_proposal, errors))); + // create commit from block notifier's observable rxcpp::subjects::subject> block_notifier_; @@ -309,6 +348,7 @@ TEST_F(ToriiServiceTest, StatusWhenBlocking) { client5.Status(last_tx_request, stful_invalid_response); ASSERT_EQ(stful_invalid_response.tx_status(), iroha::protocol::TxStatus::STATEFUL_VALIDATION_FAILED); + ASSERT_EQ(stful_invalid_response.error_message(), stringified_error); } /** @@ -385,6 +425,9 @@ TEST_F(ToriiServiceTest, StreamingFullPipelineTest) { .height(1) .build()); prop_notifier_.get_subscriber().on_next(proposal); + verified_prop_notifier_.get_subscriber().on_next( + std::make_shared( + std::make_pair(proposal, iroha::validation::TransactionsErrors{}))); auto block = clone(proto::BlockBuilder() .height(1) From 5a1e1188851cd7518c7d01923d1d47a9f4cf7814 Mon Sep 17 00:00:00 2001 From: Andrei Lebedev Date: Fri, 6 Jul 2018 12:21:52 +0300 Subject: [PATCH 27/97] Refactor Temporary Wsv command application (#1544) Use bind in TemporaryWsv apply This allows to avoid early evaluation of execution result Signed-off-by: Andrei Lebedev --- irohad/ametsuchi/impl/temporary_wsv_impl.cpp | 34 +++++++------------- 1 file changed, 12 insertions(+), 22 deletions(-) diff --git a/irohad/ametsuchi/impl/temporary_wsv_impl.cpp b/irohad/ametsuchi/impl/temporary_wsv_impl.cpp index e368dabd00..310e14599a 100644 --- a/irohad/ametsuchi/impl/temporary_wsv_impl.cpp +++ b/irohad/ametsuchi/impl/temporary_wsv_impl.cpp @@ -46,26 +46,14 @@ namespace iroha { const auto &tx_creator = tx.creatorAccountId(); command_executor_->setCreatorAccountId(tx_creator); command_validator_->setCreatorAccountId(tx_creator); - auto execute_command = [this](auto &command, size_t command_index) - -> expected::Result { + auto execute_command = + [this](auto &command) -> expected::Result { // Validate command - return expected::map_error( - boost::apply_visitor(*command_validator_, command.get()), - [command_index](CommandError &error) { - return validation::CommandError{error.command_name, - error.toString(), - true, - command_index}; - }) + return boost::apply_visitor(*command_validator_, command.get()) // Execute command - .and_res(expected::map_error( - boost::apply_visitor(*command_executor_, command.get()), - [command_index](CommandError &error) { - return validation::CommandError{error.command_name, - error.toString(), - true, - command_index}; - })); + | [this, &command] { + return boost::apply_visitor(*command_executor_, command.get()); + }; }; transaction_->exec("SAVEPOINT savepoint_;"); @@ -80,11 +68,13 @@ namespace iroha { for (size_t i = 0; i < commands.size(); ++i) { // in case of failed command, rollback and return auto cmd_is_valid = - execute_command(commands[i], i) + execute_command(commands[i]) .match([](expected::Value &) { return true; }, - [&cmd_error]( - expected::Error &error) { - cmd_error = error.error; + [i, &cmd_error](expected::Error &error) { + cmd_error = {error.error.command_name, + error.error.toString(), + true, + i}; return false; }); if (not cmd_is_valid) { From 07d96c874b895616a40322cb2c8168570a7ba780 Mon Sep 17 00:00:00 2001 From: Sergei Date: Fri, 6 Jul 2018 18:09:41 +0700 Subject: [PATCH 28/97] fix genesis (#1545) Signed-off-by: Sergei --- example/genesis.block | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/example/genesis.block b/example/genesis.block index e9802e6a94..2f10b25aca 100644 --- a/example/genesis.block +++ b/example/genesis.block @@ -13,30 +13,6 @@ } } }, - { - "addPeer":{ - "peer":{ - "address":"0.0.0.0:20001", - "peerKey":"D1Faq417+ORUHAU9clPBphAJxj6Bn1nJhhQf4eCuMj8=" - } - } - }, - { - "addPeer":{ - "peer":{ - "address":"0.0.0.0:30001", - "peerKey":"MMpE7CK12zY3K/0cvfCuupow/y0euzaF4frTJTCiQ10=" - } - } - }, - { - "addPeer":{ - "peer":{ - "address":"0.0.0.0:40001", - "peerKey":"C65E+6pjV8bQP4VYovMU8Yb6Q3G9UWGUH5THnIvKCEo=" - } - } - }, { "createRole":{ "roleName":"admin", From 862ca79f2c9592d8aa01d11136b4c24b9868bed6 Mon Sep 17 00:00:00 2001 From: Nikita Alekseev Date: Fri, 6 Jul 2018 14:32:23 +0300 Subject: [PATCH 29/97] block and proposal creation benchmark (#1539) Block and proposal are benchmarked for their copy and move Signed-off-by: Nikita Alekseev --- test/benchmark/CMakeLists.txt | 14 +++ test/benchmark/bm_proto_creation.cpp | 160 +++++++++++++++++++++++++++ 2 files changed, 174 insertions(+) create mode 100644 test/benchmark/bm_proto_creation.cpp diff --git a/test/benchmark/CMakeLists.txt b/test/benchmark/CMakeLists.txt index 47a2ab44b9..607a74641c 100644 --- a/test/benchmark/CMakeLists.txt +++ b/test/benchmark/CMakeLists.txt @@ -11,3 +11,17 @@ target_link_libraries(benchmark_example benchmark shared_model_stateless_validation ) + +add_executable(bm_proto_creation + bm_proto_creation.cpp + ) + +target_include_directories(bm_proto_creation PUBLIC + ${PROJECT_SOURCE_DIR}/test + ) + +target_link_libraries(bm_proto_creation + benchmark + shared_model_proto_backend + ) + diff --git a/test/benchmark/bm_proto_creation.cpp b/test/benchmark/bm_proto_creation.cpp new file mode 100644 index 0000000000..a0cdcf9d15 --- /dev/null +++ b/test/benchmark/bm_proto_creation.cpp @@ -0,0 +1,160 @@ +/** + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * When passing through the pipeline, we need to form proposal + * out of transactions and then make blocks out of it. This results in several + * transformations of underlying transport implementation, + * which can be visibly slow. + * + * The purpose of this benchmark is to keep track of performance costs related + * to blocks and proposals copying/moving. + * + * Each benchmark runs transaction() and commands() call to + * initialize possibly lazy fields. + */ + +#include + +#include "backend/protobuf/block.hpp" +#include "datetime/time.hpp" +#include "module/shared_model/builders/protobuf/test_block_builder.hpp" +#include "module/shared_model/builders/protobuf/test_proposal_builder.hpp" +#include "module/shared_model/builders/protobuf/test_transaction_builder.hpp" + +/// number of commands in a single transaction +constexpr int number_of_commands = 5; + +/// number of transactions in a single block +constexpr int number_of_txs = 100; + +class BlockBenchmark : public benchmark::Fixture { + public: + // Block cannot be copy-assigned, that's why the state is kept in a builder + TestBlockBuilder complete_builder; + + /** + * Initialize block builder for benchmarks + */ + void SetUp(benchmark::State &st) override { + TestBlockBuilder builder; + TestTransactionBuilder txbuilder; + + auto base_tx = txbuilder.createdTime(iroha::time::now()).quorum(1); + + for (int i = 0; i < number_of_commands; i++) { + base_tx.transferAsset("player@one", "player@two", "coin", "", "5.00"); + } + + std::vector txs; + + for (int i = 0; i < number_of_txs; i++) { + txs.push_back(base_tx.build()); + } + + complete_builder = + builder.createdTime(iroha::time::now()).height(1).transactions(txs); + } +}; + +class ProposalBenchmark : public benchmark::Fixture { + public: + // Block cannot be copy-assigned, that's why state is kept in a builder + TestProposalBuilder complete_builder; + + void SetUp(benchmark::State &st) override { + TestProposalBuilder builder; + TestTransactionBuilder txbuilder; + + auto base_tx = txbuilder.createdTime(iroha::time::now()).quorum(1); + + for (int i = 0; i < number_of_commands; i++) { + base_tx.transferAsset("player@one", "player@two", "coin", "", "5.00"); + } + + std::vector txs; + + for (int i = 0; i < number_of_txs; i++) { + txs.push_back(base_tx.build()); + } + + complete_builder = + builder.createdTime(iroha::time::now()).height(1).transactions(txs); + } +}; + +/** + * calls getters of a given object (block or proposal), + * so that lazy fields are initialized. + * @param obj - Block or Proposal + */ +template +void checkLoop(const T &obj) { + for (const auto &tx : obj.transactions()) { + benchmark::DoNotOptimize(tx.commands()); + } +} + +BENCHMARK_F(BlockBenchmark, CopyTest)(benchmark::State &st) { + auto block = complete_builder.build(); + + while (st.KeepRunning()) { + shared_model::proto::Block copy(block.getTransport()); + + checkLoop(copy); + } +} + +BENCHMARK_F(BlockBenchmark, MoveTest)(benchmark::State &st) { + auto block = complete_builder.build(); + + while (st.KeepRunning()) { + shared_model::proto::Block copy(std::move(block.getTransport())); + + checkLoop(copy); + } +} + +BENCHMARK_F(BlockBenchmark, CloneTest)(benchmark::State &st) { + auto block = complete_builder.build(); + + while (st.KeepRunning()) { + auto copy = clone(block); + + checkLoop(*copy); + } +} + +BENCHMARK_F(ProposalBenchmark, CopyTest)(benchmark::State &st) { + auto proposal = complete_builder.build(); + + while (st.KeepRunning()) { + shared_model::proto::Proposal copy(proposal.getTransport()); + + checkLoop(copy); + } +} + +BENCHMARK_F(ProposalBenchmark, MoveTest)(benchmark::State &state) { + auto proposal = complete_builder.build(); + + while (state.KeepRunning()) { + shared_model::proto::Proposal copy(std::move(proposal.getTransport())); + + checkLoop(copy); + } +} + +BENCHMARK_F(ProposalBenchmark, CloneTest)(benchmark::State &st) { + auto proposal = complete_builder.build(); + + while (st.KeepRunning()) { + auto copy = clone(proposal); + + checkLoop(*copy); + } +} + +BENCHMARK_MAIN(); From df5062fcf122a313e92cc8c72c146f16376f1d10 Mon Sep 17 00:00:00 2001 From: Igor Egorov Date: Fri, 6 Jul 2018 19:48:57 +0300 Subject: [PATCH 30/97] Binary Testing Framework (#1485) Signed-off-by: Igor Egorov --- example/python/.gitignore | 2 + .../python/permissions/can_add_asset_qty.py | 40 ++++ .../permissions/can_add_my_signatory.py | 53 +++++ example/python/permissions/can_add_peer.py | 40 ++++ .../python/permissions/can_add_signatory.py | 40 ++++ example/python/permissions/can_append_role.py | 48 ++++ .../python/permissions/can_create_account.py | 42 ++++ .../python/permissions/can_create_asset.py | 39 ++++ .../python/permissions/can_create_domain.py | 40 ++++ example/python/permissions/can_create_role.py | 44 ++++ example/python/permissions/can_detach_role.py | 39 ++++ .../python/permissions/can_get_all_acc_ast.py | 40 ++++ .../permissions/can_get_all_acc_ast_txs.py | 47 ++++ .../permissions/can_get_all_acc_detail.py | 40 ++++ .../python/permissions/can_get_all_acc_txs.py | 40 ++++ .../permissions/can_get_all_accounts.py | 40 ++++ .../permissions/can_get_all_signatories.py | 40 ++++ example/python/permissions/can_get_all_txs.py | 72 ++++++ .../permissions/can_get_domain_acc_ast.py | 39 ++++ .../permissions/can_get_domain_acc_ast_txs.py | 42 ++++ .../permissions/can_get_domain_acc_detail.py | 39 ++++ .../permissions/can_get_domain_acc_txs.py | 39 ++++ .../permissions/can_get_domain_accounts.py | 39 ++++ .../permissions/can_get_domain_signatories.py | 39 ++++ .../python/permissions/can_get_my_acc_ast.py | 39 ++++ .../permissions/can_get_my_acc_ast_txs.py | 42 ++++ .../permissions/can_get_my_acc_detail.py | 39 ++++ .../python/permissions/can_get_my_acc_txs.py | 39 ++++ .../python/permissions/can_get_my_account.py | 39 ++++ .../permissions/can_get_my_signatories.py | 39 ++++ example/python/permissions/can_get_my_txs.py | 76 +++++++ example/python/permissions/can_get_roles.py | 52 +++++ .../can_grant_can_add_my_signatory.py | 41 ++++ .../can_grant_can_remove_my_signatory.py | 41 ++++ .../can_grant_can_set_my_account_detail.py | 41 ++++ .../can_grant_can_set_my_quorum.py | 41 ++++ .../can_grant_can_transfer_my_assets.py | 48 ++++ example/python/permissions/can_read_assets.py | 40 ++++ example/python/permissions/can_receive.py | 47 ++++ .../permissions/can_remove_my_signatory.py | 57 +++++ .../permissions/can_remove_signatory.py | 41 ++++ example/python/permissions/can_set_detail.py | 39 ++++ .../permissions/can_set_my_account_detail.py | 54 +++++ .../python/permissions/can_set_my_quorum.py | 57 +++++ example/python/permissions/can_set_quorum.py | 42 ++++ .../permissions/can_subtract_asset_qty.py | 42 ++++ example/python/permissions/can_transfer.py | 11 + .../permissions/can_transfer_my_assets.py | 59 +++++ example/python/permissions/commons.py | 79 +++++++ test/integration/CMakeLists.txt | 1 + test/integration/binary/CMakeLists.txt | 40 ++++ test/integration/binary/binaries_test.cpp | 204 +++++++++++++++++ .../binary/binaries_test_fixture.hpp | 205 ++++++++++++++++++ test/integration/binary/launchers.cpp | 102 +++++++++ test/integration/binary/launchers.hpp | 83 +++++++ 55 files changed, 2763 insertions(+) create mode 100644 example/python/permissions/can_add_asset_qty.py create mode 100644 example/python/permissions/can_add_my_signatory.py create mode 100644 example/python/permissions/can_add_peer.py create mode 100644 example/python/permissions/can_add_signatory.py create mode 100644 example/python/permissions/can_append_role.py create mode 100644 example/python/permissions/can_create_account.py create mode 100644 example/python/permissions/can_create_asset.py create mode 100644 example/python/permissions/can_create_domain.py create mode 100644 example/python/permissions/can_create_role.py create mode 100644 example/python/permissions/can_detach_role.py create mode 100644 example/python/permissions/can_get_all_acc_ast.py create mode 100644 example/python/permissions/can_get_all_acc_ast_txs.py create mode 100644 example/python/permissions/can_get_all_acc_detail.py create mode 100644 example/python/permissions/can_get_all_acc_txs.py create mode 100644 example/python/permissions/can_get_all_accounts.py create mode 100644 example/python/permissions/can_get_all_signatories.py create mode 100644 example/python/permissions/can_get_all_txs.py create mode 100644 example/python/permissions/can_get_domain_acc_ast.py create mode 100644 example/python/permissions/can_get_domain_acc_ast_txs.py create mode 100644 example/python/permissions/can_get_domain_acc_detail.py create mode 100644 example/python/permissions/can_get_domain_acc_txs.py create mode 100644 example/python/permissions/can_get_domain_accounts.py create mode 100644 example/python/permissions/can_get_domain_signatories.py create mode 100644 example/python/permissions/can_get_my_acc_ast.py create mode 100644 example/python/permissions/can_get_my_acc_ast_txs.py create mode 100644 example/python/permissions/can_get_my_acc_detail.py create mode 100644 example/python/permissions/can_get_my_acc_txs.py create mode 100644 example/python/permissions/can_get_my_account.py create mode 100644 example/python/permissions/can_get_my_signatories.py create mode 100644 example/python/permissions/can_get_my_txs.py create mode 100644 example/python/permissions/can_get_roles.py create mode 100644 example/python/permissions/can_grant_can_add_my_signatory.py create mode 100644 example/python/permissions/can_grant_can_remove_my_signatory.py create mode 100644 example/python/permissions/can_grant_can_set_my_account_detail.py create mode 100644 example/python/permissions/can_grant_can_set_my_quorum.py create mode 100644 example/python/permissions/can_grant_can_transfer_my_assets.py create mode 100644 example/python/permissions/can_read_assets.py create mode 100644 example/python/permissions/can_receive.py create mode 100644 example/python/permissions/can_remove_my_signatory.py create mode 100644 example/python/permissions/can_remove_signatory.py create mode 100644 example/python/permissions/can_set_detail.py create mode 100644 example/python/permissions/can_set_my_account_detail.py create mode 100644 example/python/permissions/can_set_my_quorum.py create mode 100644 example/python/permissions/can_set_quorum.py create mode 100644 example/python/permissions/can_subtract_asset_qty.py create mode 100644 example/python/permissions/can_transfer.py create mode 100644 example/python/permissions/can_transfer_my_assets.py create mode 100644 example/python/permissions/commons.py create mode 100644 test/integration/binary/CMakeLists.txt create mode 100644 test/integration/binary/binaries_test.cpp create mode 100644 test/integration/binary/binaries_test_fixture.hpp create mode 100644 test/integration/binary/launchers.cpp create mode 100644 test/integration/binary/launchers.hpp diff --git a/example/python/.gitignore b/example/python/.gitignore index dcadf0aed8..568b860e77 100644 --- a/example/python/.gitignore +++ b/example/python/.gitignore @@ -3,5 +3,7 @@ !.gitignore !tx-example.py !blocks-query.py +!permissions/ +!permissions/* !prepare.sh !README.md diff --git a/example/python/permissions/can_add_asset_qty.py b/example/python/permissions/can_add_asset_qty.py new file mode 100644 index 0000000000..afdb8d3521 --- /dev/null +++ b/example/python/permissions/can_add_asset_qty.py @@ -0,0 +1,40 @@ +# +# Copyright Soramitsu Co., Ltd. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +# + +import iroha +import commons + +admin = commons.new_user('admin@test') +alice = commons.new_user('alice@test') + + +@commons.hex +def genesis_tx(): + test_permissions = iroha.RolePermissionSet([iroha.Role_kAddAssetQty]) + tx = iroha.ModelTransactionBuilder() \ + .createdTime(commons.now()) \ + .creatorAccountId(admin['id']) \ + .addPeer('0.0.0.0:50541', admin['key'].publicKey()) \ + .createRole('admin_role', commons.all_permissions()) \ + .createRole('test_role', test_permissions) \ + .createDomain('test', 'test_role') \ + .createAccount('admin', 'test', admin['key'].publicKey()) \ + .createAccount('alice', 'test', alice['key'].publicKey()) \ + .appendRole(admin['id'], 'admin_role') \ + .createAsset('coin', 'test', 2) \ + .build() + return iroha.ModelProtoTransaction(tx) \ + .signAndAddSignature(admin['key']).finish() + + +@commons.hex +def add_asset_tx(): + tx = iroha.ModelTransactionBuilder() \ + .createdTime(commons.now()) \ + .creatorAccountId(alice['id']) \ + .addAssetQuantity('coin#test', '5000.99') \ + .build() + return iroha.ModelProtoTransaction(tx) \ + .signAndAddSignature(alice['key']).finish() diff --git a/example/python/permissions/can_add_my_signatory.py b/example/python/permissions/can_add_my_signatory.py new file mode 100644 index 0000000000..2b23f0a63d --- /dev/null +++ b/example/python/permissions/can_add_my_signatory.py @@ -0,0 +1,53 @@ +# +# Copyright Soramitsu Co., Ltd. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +# + +import iroha +import commons + +admin = commons.new_user('admin@test') +alice = commons.new_user('alice@test') +bob = commons.new_user('bob@test') + + +@commons.hex +def genesis_tx(): + test_permissions = iroha.RolePermissionSet([iroha.Role_kAddMySignatory]) + tx = iroha.ModelTransactionBuilder() \ + .createdTime(commons.now()) \ + .creatorAccountId(admin['id']) \ + .addPeer('0.0.0.0:50541', admin['key'].publicKey()) \ + .createRole('admin_role', commons.all_permissions()) \ + .createRole('test_role', test_permissions) \ + .createDomain('test', 'test_role') \ + .createAccount('admin', 'test', admin['key'].publicKey()) \ + .createAccount('alice', 'test', alice['key'].publicKey()) \ + .createAccount('bob', 'test', bob['key'].publicKey()) \ + .appendRole(admin['id'], 'admin_role') \ + .build() + return iroha.ModelProtoTransaction(tx) \ + .signAndAddSignature(admin['key']).finish() + + +@commons.hex +def grant_can_add_my_signatory_tx(): + tx = iroha.ModelTransactionBuilder() \ + .createdTime(commons.now()) \ + .creatorAccountId(alice['id']) \ + .grantPermission(bob['id'], iroha.Grantable_kAddMySignatory) \ + .build() + return iroha.ModelProtoTransaction(tx) \ + .signAndAddSignature(alice['key']).finish() + + +@commons.hex +def add_signatory_tx(): + extra_key = iroha.ModelCrypto().generateKeypair() + tx = iroha.ModelTransactionBuilder() \ + .createdTime(commons.now()) \ + .creatorAccountId(bob['id']) \ + .addSignatory(alice['id'], extra_key.publicKey()) \ + .build() + return iroha.ModelProtoTransaction(tx) \ + .signAndAddSignature(bob['key']).finish() diff --git a/example/python/permissions/can_add_peer.py b/example/python/permissions/can_add_peer.py new file mode 100644 index 0000000000..98c238f7c1 --- /dev/null +++ b/example/python/permissions/can_add_peer.py @@ -0,0 +1,40 @@ +# +# Copyright Soramitsu Co., Ltd. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +# + +import iroha +import commons + +admin = commons.new_user('admin@test') +alice = commons.new_user('alice@test') + + +@commons.hex +def genesis_tx(): + test_permissions = iroha.RolePermissionSet([iroha.Role_kAddPeer]) + tx = iroha.ModelTransactionBuilder() \ + .createdTime(commons.now()) \ + .creatorAccountId(admin['id']) \ + .addPeer('0.0.0.0:50541', admin['key'].publicKey()) \ + .createRole('admin_role', commons.all_permissions()) \ + .createRole('test_role', test_permissions) \ + .createDomain('test', 'test_role') \ + .createAccount('admin', 'test', admin['key'].publicKey()) \ + .createAccount('alice', 'test', alice['key'].publicKey()) \ + .appendRole(admin['id'], 'admin_role') \ + .build() + return iroha.ModelProtoTransaction(tx) \ + .signAndAddSignature(admin['key']).finish() + + +@commons.hex +def add_peer_tx(): + peer_key = iroha.ModelCrypto().generateKeypair() + tx = iroha.ModelTransactionBuilder() \ + .createdTime(commons.now()) \ + .creatorAccountId(alice['id']) \ + .addPeer('192.168.10.10:50541', peer_key.publicKey()) \ + .build() + return iroha.ModelProtoTransaction(tx) \ + .signAndAddSignature(alice['key']).finish() diff --git a/example/python/permissions/can_add_signatory.py b/example/python/permissions/can_add_signatory.py new file mode 100644 index 0000000000..1adcff3e9a --- /dev/null +++ b/example/python/permissions/can_add_signatory.py @@ -0,0 +1,40 @@ +# +# Copyright Soramitsu Co., Ltd. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +# + +import iroha +import commons + +admin = commons.new_user('admin@test') +alice = commons.new_user('alice@test') + + +@commons.hex +def genesis_tx(): + test_permissions = iroha.RolePermissionSet([iroha.Role_kAddSignatory]) + tx = iroha.ModelTransactionBuilder() \ + .createdTime(commons.now()) \ + .creatorAccountId(admin['id']) \ + .addPeer('0.0.0.0:50541', admin['key'].publicKey()) \ + .createRole('admin_role', commons.all_permissions()) \ + .createRole('test_role', test_permissions) \ + .createDomain('test', 'test_role') \ + .createAccount('admin', 'test', admin['key'].publicKey()) \ + .createAccount('alice', 'test', alice['key'].publicKey()) \ + .appendRole(admin['id'], 'admin_role') \ + .build() + return iroha.ModelProtoTransaction(tx) \ + .signAndAddSignature(admin['key']).finish() + + +@commons.hex +def add_signatory_tx(): + extra_key = iroha.ModelCrypto().generateKeypair() + tx = iroha.ModelTransactionBuilder() \ + .createdTime(commons.now()) \ + .creatorAccountId(alice['id']) \ + .addSignatory(alice['id'], extra_key.publicKey()) \ + .build() + return iroha.ModelProtoTransaction(tx) \ + .signAndAddSignature(alice['key']).finish() diff --git a/example/python/permissions/can_append_role.py b/example/python/permissions/can_append_role.py new file mode 100644 index 0000000000..0c9bbf778e --- /dev/null +++ b/example/python/permissions/can_append_role.py @@ -0,0 +1,48 @@ +# +# Copyright Soramitsu Co., Ltd. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +# + +import iroha +import commons + +admin = commons.new_user('admin@test') +alice = commons.new_user('alice@test') +bob = commons.new_user('bob@test') + + +@commons.hex +def genesis_tx(): + test_permissions = iroha.RolePermissionSet( + [iroha.Role_kAppendRole, iroha.Role_kAddPeer] + ) + second_role = iroha.RolePermissionSet([iroha.Role_kAddPeer]) + tx = iroha.ModelTransactionBuilder() \ + .createdTime(commons.now()) \ + .creatorAccountId(admin['id']) \ + .addPeer('0.0.0.0:50541', admin['key'].publicKey()) \ + .createRole('admin_role', commons.all_permissions()) \ + .createRole('test_role', test_permissions) \ + .createRole('second_role', second_role) \ + .createDomain('test', 'test_role') \ + .createAccount('admin', 'test', admin['key'].publicKey()) \ + .createAccount('alice', 'test', alice['key'].publicKey()) \ + .createAccount('bob', 'test', bob['key'].publicKey()) \ + .appendRole(admin['id'], 'admin_role') \ + .appendRole(alice['id'], 'second_role') \ + .build() + return iroha.ModelProtoTransaction(tx) \ + .signAndAddSignature(admin['key']).finish() + + +@commons.hex +def append_role_tx(): + # Note that you can append only that role that has + # lesser or the same set of permissions as transaction creator. + tx = iroha.ModelTransactionBuilder() \ + .createdTime(commons.now()) \ + .creatorAccountId(alice['id']) \ + .appendRole(bob['id'], 'second_role') \ + .build() + return iroha.ModelProtoTransaction(tx) \ + .signAndAddSignature(alice['key']).finish() diff --git a/example/python/permissions/can_create_account.py b/example/python/permissions/can_create_account.py new file mode 100644 index 0000000000..bd13821bec --- /dev/null +++ b/example/python/permissions/can_create_account.py @@ -0,0 +1,42 @@ +# +# Copyright Soramitsu Co., Ltd. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +# + +import iroha +import commons + +admin = commons.new_user('admin@test') +alice = commons.new_user('alice@test') +bob = commons.new_user('bob@test') + + +@commons.hex +def genesis_tx(): + test_permissions = iroha.RolePermissionSet( + [iroha.Role_kCreateAccount] + ) + tx = iroha.ModelTransactionBuilder() \ + .createdTime(commons.now()) \ + .creatorAccountId(admin['id']) \ + .addPeer('0.0.0.0:50541', admin['key'].publicKey()) \ + .createRole('admin_role', commons.all_permissions()) \ + .createRole('test_role', test_permissions) \ + .createDomain('test', 'test_role') \ + .createAccount('admin', 'test', admin['key'].publicKey()) \ + .createAccount('alice', 'test', alice['key'].publicKey()) \ + .appendRole(admin['id'], 'admin_role') \ + .build() + return iroha.ModelProtoTransaction(tx) \ + .signAndAddSignature(admin['key']).finish() + + +@commons.hex +def create_account_tx(): + tx = iroha.ModelTransactionBuilder() \ + .createdTime(commons.now()) \ + .creatorAccountId(alice['id']) \ + .createAccount('bob', 'test', bob['key'].publicKey()) \ + .build() + return iroha.ModelProtoTransaction(tx) \ + .signAndAddSignature(alice['key']).finish() diff --git a/example/python/permissions/can_create_asset.py b/example/python/permissions/can_create_asset.py new file mode 100644 index 0000000000..6d52788dbc --- /dev/null +++ b/example/python/permissions/can_create_asset.py @@ -0,0 +1,39 @@ +# +# Copyright Soramitsu Co., Ltd. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +# + +import iroha +import commons + +admin = commons.new_user('admin@test') +alice = commons.new_user('alice@test') + + +@commons.hex +def genesis_tx(): + test_permissions = iroha.RolePermissionSet([iroha.Role_kCreateAsset]) + tx = iroha.ModelTransactionBuilder() \ + .createdTime(commons.now()) \ + .creatorAccountId(admin['id']) \ + .addPeer('0.0.0.0:50541', admin['key'].publicKey()) \ + .createRole('admin_role', commons.all_permissions()) \ + .createRole('test_role', test_permissions) \ + .createDomain('test', 'test_role') \ + .createAccount('admin', 'test', admin['key'].publicKey()) \ + .createAccount('alice', 'test', alice['key'].publicKey()) \ + .appendRole(admin['id'], 'admin_role') \ + .build() + return iroha.ModelProtoTransaction(tx) \ + .signAndAddSignature(admin['key']).finish() + + +@commons.hex +def create_asset_tx(): + tx = iroha.ModelTransactionBuilder() \ + .createdTime(commons.now()) \ + .creatorAccountId(alice['id']) \ + .createAsset('coin', 'test', 2) \ + .build() + return iroha.ModelProtoTransaction(tx) \ + .signAndAddSignature(alice['key']).finish() diff --git a/example/python/permissions/can_create_domain.py b/example/python/permissions/can_create_domain.py new file mode 100644 index 0000000000..06d6032edf --- /dev/null +++ b/example/python/permissions/can_create_domain.py @@ -0,0 +1,40 @@ +# +# Copyright Soramitsu Co., Ltd. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +# + +import iroha +import commons + +admin = commons.new_user('admin@test') +alice = commons.new_user('alice@test') + + +@commons.hex +def genesis_tx(): + test_permissions = iroha.RolePermissionSet([iroha.Role_kCreateDomain]) + tx = iroha.ModelTransactionBuilder() \ + .createdTime(commons.now()) \ + .creatorAccountId(admin['id']) \ + .addPeer('0.0.0.0:50541', admin['key'].publicKey()) \ + .createRole('admin_role', commons.all_permissions()) \ + .createRole('test_role', test_permissions) \ + .createDomain('test', 'test_role') \ + .createAccount('admin', 'test', admin['key'].publicKey()) \ + .createAccount('alice', 'test', alice['key'].publicKey()) \ + .appendRole(admin['id'], 'admin_role') \ + .build() + return iroha.ModelProtoTransaction(tx) \ + .signAndAddSignature(admin['key']).finish() + + +@commons.hex +def create_domain_tx(): + # 'test_role' was created in genesis transaction + tx = iroha.ModelTransactionBuilder() \ + .createdTime(commons.now()) \ + .creatorAccountId(alice['id']) \ + .createDomain('another-domain', 'test_role') \ + .build() + return iroha.ModelProtoTransaction(tx) \ + .signAndAddSignature(alice['key']).finish() diff --git a/example/python/permissions/can_create_role.py b/example/python/permissions/can_create_role.py new file mode 100644 index 0000000000..474e4f2a2f --- /dev/null +++ b/example/python/permissions/can_create_role.py @@ -0,0 +1,44 @@ +# +# Copyright Soramitsu Co., Ltd. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +# + +import iroha +import commons + +admin = commons.new_user('admin@test') +alice = commons.new_user('alice@test') + + +@commons.hex +def genesis_tx(): + test_permissions = iroha.RolePermissionSet( + [iroha.Role_kCreateRole, iroha.Role_kCreateDomain] + ) + tx = iroha.ModelTransactionBuilder() \ + .createdTime(commons.now()) \ + .creatorAccountId(admin['id']) \ + .addPeer('0.0.0.0:50541', admin['key'].publicKey()) \ + .createRole('admin_role', commons.all_permissions()) \ + .createRole('test_role', test_permissions) \ + .createDomain('test', 'test_role') \ + .createAccount('admin', 'test', admin['key'].publicKey()) \ + .createAccount('alice', 'test', alice['key'].publicKey()) \ + .appendRole(admin['id'], 'admin_role') \ + .build() + return iroha.ModelProtoTransaction(tx) \ + .signAndAddSignature(admin['key']).finish() + + +@commons.hex +def create_role_tx(): + # You can pick only those permissions that + # already belong to account of transaction creator. + role_permissions = iroha.RolePermissionSet([iroha.Role_kCreateDomain]) + tx = iroha.ModelTransactionBuilder() \ + .createdTime(commons.now()) \ + .creatorAccountId(alice['id']) \ + .createRole('newrole', role_permissions) \ + .build() + return iroha.ModelProtoTransaction(tx) \ + .signAndAddSignature(alice['key']).finish() diff --git a/example/python/permissions/can_detach_role.py b/example/python/permissions/can_detach_role.py new file mode 100644 index 0000000000..4b5cee6463 --- /dev/null +++ b/example/python/permissions/can_detach_role.py @@ -0,0 +1,39 @@ +# +# Copyright Soramitsu Co., Ltd. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +# + +import iroha +import commons + +admin = commons.new_user('admin@test') +alice = commons.new_user('alice@test') + + +@commons.hex +def genesis_tx(): + test_permissions = iroha.RolePermissionSet([iroha.Role_kDetachRole]) + tx = iroha.ModelTransactionBuilder() \ + .createdTime(commons.now()) \ + .creatorAccountId(admin['id']) \ + .addPeer('0.0.0.0:50541', admin['key'].publicKey()) \ + .createRole('admin_role', commons.all_permissions()) \ + .createRole('test_role', test_permissions) \ + .createDomain('test', 'test_role') \ + .createAccount('admin', 'test', admin['key'].publicKey()) \ + .createAccount('alice', 'test', alice['key'].publicKey()) \ + .appendRole(admin['id'], 'admin_role') \ + .build() + return iroha.ModelProtoTransaction(tx) \ + .signAndAddSignature(admin['key']).finish() + + +@commons.hex +def detach_role_tx(): + tx = iroha.ModelTransactionBuilder() \ + .createdTime(commons.now()) \ + .creatorAccountId(alice['id']) \ + .detachRole(admin['id'], 'test_role') \ + .build() + return iroha.ModelProtoTransaction(tx) \ + .signAndAddSignature(alice['key']).finish() diff --git a/example/python/permissions/can_get_all_acc_ast.py b/example/python/permissions/can_get_all_acc_ast.py new file mode 100644 index 0000000000..96aaa55e8f --- /dev/null +++ b/example/python/permissions/can_get_all_acc_ast.py @@ -0,0 +1,40 @@ +# +# Copyright Soramitsu Co., Ltd. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +# + +import iroha +import commons + +admin = commons.new_user('admin@first') +alice = commons.new_user('alice@second') + + +@commons.hex +def genesis_tx(): + test_permissions = iroha.RolePermissionSet([iroha.Role_kGetAllAccAst]) + tx = iroha.ModelTransactionBuilder() \ + .createdTime(commons.now()) \ + .creatorAccountId(admin['id']) \ + .addPeer('0.0.0.0:50541', admin['key'].publicKey()) \ + .createRole('admin_role', commons.all_permissions()) \ + .createRole('test_role', test_permissions) \ + .createDomain('first', 'admin_role') \ + .createDomain('second', 'test_role') \ + .createAccount('admin', 'first', admin['key'].publicKey()) \ + .createAccount('alice', 'second', alice['key'].publicKey()) \ + .build() + return iroha.ModelProtoTransaction(tx) \ + .signAndAddSignature(admin['key']).finish() + + +@commons.hex +def account_assets_query(): + tx = iroha.ModelQueryBuilder() \ + .createdTime(commons.now()) \ + .queryCounter(1) \ + .creatorAccountId(alice['id']) \ + .getAccountAssets(admin['id']) \ + .build() + return iroha.ModelProtoQuery(tx) \ + .signAndAddSignature(alice['key']).finish() diff --git a/example/python/permissions/can_get_all_acc_ast_txs.py b/example/python/permissions/can_get_all_acc_ast_txs.py new file mode 100644 index 0000000000..af6f3d29e5 --- /dev/null +++ b/example/python/permissions/can_get_all_acc_ast_txs.py @@ -0,0 +1,47 @@ +# +# Copyright Soramitsu Co., Ltd. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +# + +import iroha +import commons + +admin = commons.new_user('admin@first') +alice = commons.new_user('alice@second') + + +@commons.hex +def genesis_tx(): + test_permissions = iroha.RolePermissionSet([ + iroha.Role_kGetAllAccAstTxs, + iroha.Role_kReceive, + iroha.Role_kTransfer + ]) + tx = iroha.ModelTransactionBuilder() \ + .createdTime(commons.now()) \ + .creatorAccountId(admin['id']) \ + .addPeer('0.0.0.0:50541', admin['key'].publicKey()) \ + .createRole('admin_role', commons.all_permissions()) \ + .createRole('test_role', test_permissions) \ + .createDomain('first', 'admin_role') \ + .createDomain('second', 'test_role') \ + .createAccount('admin', 'first', admin['key'].publicKey()) \ + .createAccount('alice', 'second', alice['key'].publicKey()) \ + .createAsset('coin', 'first', 2) \ + .addAssetQuantity('coin#first', '300.00') \ + .transferAsset(admin['id'], alice['id'], 'coin#first', 'top up', '200.00') \ + .build() + return iroha.ModelProtoTransaction(tx) \ + .signAndAddSignature(admin['key']).finish() + + +@commons.hex +def account_asset_transactions_query(): + tx = iroha.ModelQueryBuilder() \ + .createdTime(commons.now()) \ + .queryCounter(1) \ + .creatorAccountId(alice['id']) \ + .getAccountAssetTransactions(admin['id'], 'coin#first') \ + .build() + return iroha.ModelProtoQuery(tx) \ + .signAndAddSignature(alice['key']).finish() diff --git a/example/python/permissions/can_get_all_acc_detail.py b/example/python/permissions/can_get_all_acc_detail.py new file mode 100644 index 0000000000..0a62055576 --- /dev/null +++ b/example/python/permissions/can_get_all_acc_detail.py @@ -0,0 +1,40 @@ +# +# Copyright Soramitsu Co., Ltd. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +# + +import iroha +import commons + +admin = commons.new_user('admin@first') +alice = commons.new_user('alice@second') + + +@commons.hex +def genesis_tx(): + test_permissions = iroha.RolePermissionSet([iroha.Role_kGetAllAccDetail]) + tx = iroha.ModelTransactionBuilder() \ + .createdTime(commons.now()) \ + .creatorAccountId(admin['id']) \ + .addPeer('0.0.0.0:50541', admin['key'].publicKey()) \ + .createRole('admin_role', commons.all_permissions()) \ + .createRole('test_role', test_permissions) \ + .createDomain('first', 'admin_role') \ + .createDomain('second', 'test_role') \ + .createAccount('admin', 'first', admin['key'].publicKey()) \ + .createAccount('alice', 'second', alice['key'].publicKey()) \ + .build() + return iroha.ModelProtoTransaction(tx) \ + .signAndAddSignature(admin['key']).finish() + + +@commons.hex +def account_detail_query(): + tx = iroha.ModelQueryBuilder() \ + .createdTime(commons.now()) \ + .queryCounter(1) \ + .creatorAccountId(alice['id']) \ + .getAccountDetail(admin['id']) \ + .build() + return iroha.ModelProtoQuery(tx) \ + .signAndAddSignature(alice['key']).finish() diff --git a/example/python/permissions/can_get_all_acc_txs.py b/example/python/permissions/can_get_all_acc_txs.py new file mode 100644 index 0000000000..18c41042f7 --- /dev/null +++ b/example/python/permissions/can_get_all_acc_txs.py @@ -0,0 +1,40 @@ +# +# Copyright Soramitsu Co., Ltd. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +# + +import iroha +import commons + +admin = commons.new_user('admin@first') +alice = commons.new_user('alice@second') + + +@commons.hex +def genesis_tx(): + test_permissions = iroha.RolePermissionSet([iroha.Role_kGetAllAccTxs]) + tx = iroha.ModelTransactionBuilder() \ + .createdTime(commons.now()) \ + .creatorAccountId(admin['id']) \ + .addPeer('0.0.0.0:50541', admin['key'].publicKey()) \ + .createRole('admin_role', commons.all_permissions()) \ + .createRole('test_role', test_permissions) \ + .createDomain('first', 'admin_role') \ + .createDomain('second', 'test_role') \ + .createAccount('admin', 'first', admin['key'].publicKey()) \ + .createAccount('alice', 'second', alice['key'].publicKey()) \ + .build() + return iroha.ModelProtoTransaction(tx) \ + .signAndAddSignature(admin['key']).finish() + + +@commons.hex +def account_transactions_query(): + tx = iroha.ModelQueryBuilder() \ + .createdTime(commons.now()) \ + .queryCounter(1) \ + .creatorAccountId(alice['id']) \ + .getAccountTransactions(admin['id']) \ + .build() + return iroha.ModelProtoQuery(tx) \ + .signAndAddSignature(alice['key']).finish() diff --git a/example/python/permissions/can_get_all_accounts.py b/example/python/permissions/can_get_all_accounts.py new file mode 100644 index 0000000000..3adeec05a4 --- /dev/null +++ b/example/python/permissions/can_get_all_accounts.py @@ -0,0 +1,40 @@ +# +# Copyright Soramitsu Co., Ltd. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +# + +import iroha +import commons + +admin = commons.new_user('admin@first') +alice = commons.new_user('alice@second') + + +@commons.hex +def genesis_tx(): + test_permissions = iroha.RolePermissionSet([iroha.Role_kGetAllAccounts]) + tx = iroha.ModelTransactionBuilder() \ + .createdTime(commons.now()) \ + .creatorAccountId(admin['id']) \ + .addPeer('0.0.0.0:50541', admin['key'].publicKey()) \ + .createRole('admin_role', commons.all_permissions()) \ + .createRole('test_role', test_permissions) \ + .createDomain('first', 'admin_role') \ + .createDomain('second', 'test_role') \ + .createAccount('admin', 'first', admin['key'].publicKey()) \ + .createAccount('alice', 'second', alice['key'].publicKey()) \ + .build() + return iroha.ModelProtoTransaction(tx) \ + .signAndAddSignature(admin['key']).finish() + + +@commons.hex +def account_query(): + tx = iroha.ModelQueryBuilder() \ + .createdTime(commons.now()) \ + .queryCounter(1) \ + .creatorAccountId(alice['id']) \ + .getAccount(admin['id']) \ + .build() + return iroha.ModelProtoQuery(tx) \ + .signAndAddSignature(alice['key']).finish() diff --git a/example/python/permissions/can_get_all_signatories.py b/example/python/permissions/can_get_all_signatories.py new file mode 100644 index 0000000000..bf4b3b7c54 --- /dev/null +++ b/example/python/permissions/can_get_all_signatories.py @@ -0,0 +1,40 @@ +# +# Copyright Soramitsu Co., Ltd. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +# + +import iroha +import commons + +admin = commons.new_user('admin@first') +alice = commons.new_user('alice@second') + + +@commons.hex +def genesis_tx(): + test_permissions = iroha.RolePermissionSet([iroha.Role_kGetAllSignatories]) + tx = iroha.ModelTransactionBuilder() \ + .createdTime(commons.now()) \ + .creatorAccountId(admin['id']) \ + .addPeer('0.0.0.0:50541', admin['key'].publicKey()) \ + .createRole('admin_role', commons.all_permissions()) \ + .createRole('test_role', test_permissions) \ + .createDomain('first', 'admin_role') \ + .createDomain('second', 'test_role') \ + .createAccount('admin', 'first', admin['key'].publicKey()) \ + .createAccount('alice', 'second', alice['key'].publicKey()) \ + .build() + return iroha.ModelProtoTransaction(tx) \ + .signAndAddSignature(admin['key']).finish() + + +@commons.hex +def signatories_query(): + tx = iroha.ModelQueryBuilder() \ + .createdTime(commons.now()) \ + .queryCounter(1) \ + .creatorAccountId(alice['id']) \ + .getSignatories(admin['id']) \ + .build() + return iroha.ModelProtoQuery(tx) \ + .signAndAddSignature(alice['key']).finish() diff --git a/example/python/permissions/can_get_all_txs.py b/example/python/permissions/can_get_all_txs.py new file mode 100644 index 0000000000..b17e76fea9 --- /dev/null +++ b/example/python/permissions/can_get_all_txs.py @@ -0,0 +1,72 @@ +# +# Copyright Soramitsu Co., Ltd. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +# + +import iroha +import commons + +admin = commons.new_user('admin@first') +alice = commons.new_user('alice@second') + +admin_tx1_hash = None +admin_tx2_hash_blob = None + + +@commons.hex +def genesis_tx(): + test_permissions = iroha.RolePermissionSet([iroha.Role_kGetAllTxs]) + tx = iroha.ModelTransactionBuilder() \ + .createdTime(commons.now()) \ + .creatorAccountId(admin['id']) \ + .addPeer('0.0.0.0:50541', admin['key'].publicKey()) \ + .createRole('admin_role', commons.all_permissions()) \ + .createRole('test_role', test_permissions) \ + .createDomain('first', 'admin_role') \ + .createDomain('second', 'test_role') \ + .createAccount('admin', 'first', admin['key'].publicKey()) \ + .createAccount('alice', 'second', alice['key'].publicKey()) \ + .build() + return iroha.ModelProtoTransaction(tx) \ + .signAndAddSignature(admin['key']).finish() + + +@commons.hex +def admin_action_1_tx(): + global admin_tx1_hash + tx = iroha.ModelTransactionBuilder() \ + .createdTime(commons.now()) \ + .creatorAccountId(admin['id']) \ + .createAsset('coin', 'second', 2) \ + .build() + admin_tx1_hash = tx.hash() + return iroha.ModelProtoTransaction(tx) \ + .signAndAddSignature(admin['key']).finish() + + +@commons.hex +def admin_action_2_tx(): + global admin_tx2_hash_blob + tx = iroha.ModelTransactionBuilder() \ + .createdTime(commons.now()) \ + .creatorAccountId(admin['id']) \ + .setAccountDetail(admin['id'], 'hyperledger', 'iroha') \ + .build() + admin_tx2_hash_blob = tx.hash().blob() + return iroha.ModelProtoTransaction(tx) \ + .signAndAddSignature(admin['key']).finish() + + +@commons.hex +def transactions_query(): + hashes = iroha.HashVector() + hashes.append(admin_tx1_hash) + hashes.append(iroha.Hash(iroha.Blob(admin_tx2_hash_blob))) + tx = iroha.ModelQueryBuilder() \ + .createdTime(commons.now()) \ + .queryCounter(1) \ + .creatorAccountId(alice['id']) \ + .getTransactions(hashes) \ + .build() + return iroha.ModelProtoQuery(tx) \ + .signAndAddSignature(alice['key']).finish() diff --git a/example/python/permissions/can_get_domain_acc_ast.py b/example/python/permissions/can_get_domain_acc_ast.py new file mode 100644 index 0000000000..67cddb006c --- /dev/null +++ b/example/python/permissions/can_get_domain_acc_ast.py @@ -0,0 +1,39 @@ +# +# Copyright Soramitsu Co., Ltd. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +# + +import iroha +import commons + +admin = commons.new_user('admin@test') +alice = commons.new_user('alice@test') + + +@commons.hex +def genesis_tx(): + test_permissions = iroha.RolePermissionSet([iroha.Role_kGetDomainAccAst]) + tx = iroha.ModelTransactionBuilder() \ + .createdTime(commons.now()) \ + .creatorAccountId(admin['id']) \ + .addPeer('0.0.0.0:50541', admin['key'].publicKey()) \ + .createRole('admin_role', commons.all_permissions()) \ + .createRole('test_role', test_permissions) \ + .createDomain('test', 'test_role') \ + .createAccount('admin', 'test', admin['key'].publicKey()) \ + .createAccount('alice', 'test', alice['key'].publicKey()) \ + .build() + return iroha.ModelProtoTransaction(tx) \ + .signAndAddSignature(admin['key']).finish() + + +@commons.hex +def account_assets_query(): + tx = iroha.ModelQueryBuilder() \ + .createdTime(commons.now()) \ + .queryCounter(1) \ + .creatorAccountId(alice['id']) \ + .getAccountAssets(admin['id']) \ + .build() + return iroha.ModelProtoQuery(tx) \ + .signAndAddSignature(alice['key']).finish() diff --git a/example/python/permissions/can_get_domain_acc_ast_txs.py b/example/python/permissions/can_get_domain_acc_ast_txs.py new file mode 100644 index 0000000000..2feeab8334 --- /dev/null +++ b/example/python/permissions/can_get_domain_acc_ast_txs.py @@ -0,0 +1,42 @@ +# +# Copyright Soramitsu Co., Ltd. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +# + +import iroha +import commons + +admin = commons.new_user('admin@test') +alice = commons.new_user('alice@test') + + +@commons.hex +def genesis_tx(): + test_permissions = iroha.RolePermissionSet([iroha.Role_kGetDomainAccAstTxs]) + tx = iroha.ModelTransactionBuilder() \ + .createdTime(commons.now()) \ + .creatorAccountId(admin['id']) \ + .addPeer('0.0.0.0:50541', admin['key'].publicKey()) \ + .createRole('admin_role', commons.all_permissions()) \ + .createRole('test_role', test_permissions) \ + .createDomain('test', 'test_role') \ + .createAccount('admin', 'test', admin['key'].publicKey()) \ + .createAccount('alice', 'test', alice['key'].publicKey()) \ + .createAsset('coin', 'test', 2) \ + .addAssetQuantity('coin#test', '500.69') \ + .transferAsset(admin['id'], alice['id'], 'coin#test', 'top up', '10.00') \ + .build() + return iroha.ModelProtoTransaction(tx) \ + .signAndAddSignature(admin['key']).finish() + + +@commons.hex +def account_asset_transactions_query(): + tx = iroha.ModelQueryBuilder() \ + .createdTime(commons.now()) \ + .queryCounter(1) \ + .creatorAccountId(alice['id']) \ + .getAccountAssetTransactions(admin['id'], 'coin#test') \ + .build() + return iroha.ModelProtoQuery(tx) \ + .signAndAddSignature(alice['key']).finish() diff --git a/example/python/permissions/can_get_domain_acc_detail.py b/example/python/permissions/can_get_domain_acc_detail.py new file mode 100644 index 0000000000..b3612a62c2 --- /dev/null +++ b/example/python/permissions/can_get_domain_acc_detail.py @@ -0,0 +1,39 @@ +# +# Copyright Soramitsu Co., Ltd. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +# + +import iroha +import commons + +admin = commons.new_user('admin@test') +alice = commons.new_user('alice@test') + + +@commons.hex +def genesis_tx(): + test_permissions = iroha.RolePermissionSet([iroha.Role_kGetDomainAccDetail]) + tx = iroha.ModelTransactionBuilder() \ + .createdTime(commons.now()) \ + .creatorAccountId(admin['id']) \ + .addPeer('0.0.0.0:50541', admin['key'].publicKey()) \ + .createRole('admin_role', commons.all_permissions()) \ + .createRole('test_role', test_permissions) \ + .createDomain('test', 'test_role') \ + .createAccount('admin', 'test', admin['key'].publicKey()) \ + .createAccount('alice', 'test', alice['key'].publicKey()) \ + .build() + return iroha.ModelProtoTransaction(tx) \ + .signAndAddSignature(admin['key']).finish() + + +@commons.hex +def account_detail_query(): + tx = iroha.ModelQueryBuilder() \ + .createdTime(commons.now()) \ + .queryCounter(1) \ + .creatorAccountId(alice['id']) \ + .getAccountDetail(admin['id']) \ + .build() + return iroha.ModelProtoQuery(tx) \ + .signAndAddSignature(alice['key']).finish() diff --git a/example/python/permissions/can_get_domain_acc_txs.py b/example/python/permissions/can_get_domain_acc_txs.py new file mode 100644 index 0000000000..ea67addb14 --- /dev/null +++ b/example/python/permissions/can_get_domain_acc_txs.py @@ -0,0 +1,39 @@ +# +# Copyright Soramitsu Co., Ltd. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +# + +import iroha +import commons + +admin = commons.new_user('admin@test') +alice = commons.new_user('alice@test') + + +@commons.hex +def genesis_tx(): + test_permissions = iroha.RolePermissionSet([iroha.Role_kGetDomainAccTxs]) + tx = iroha.ModelTransactionBuilder() \ + .createdTime(commons.now()) \ + .creatorAccountId(admin['id']) \ + .addPeer('0.0.0.0:50541', admin['key'].publicKey()) \ + .createRole('admin_role', commons.all_permissions()) \ + .createRole('test_role', test_permissions) \ + .createDomain('test', 'test_role') \ + .createAccount('admin', 'test', admin['key'].publicKey()) \ + .createAccount('alice', 'test', alice['key'].publicKey()) \ + .build() + return iroha.ModelProtoTransaction(tx) \ + .signAndAddSignature(admin['key']).finish() + + +@commons.hex +def account_transactions_query(): + tx = iroha.ModelQueryBuilder() \ + .createdTime(commons.now()) \ + .queryCounter(1) \ + .creatorAccountId(alice['id']) \ + .getAccountTransactions(admin['id']) \ + .build() + return iroha.ModelProtoQuery(tx) \ + .signAndAddSignature(alice['key']).finish() diff --git a/example/python/permissions/can_get_domain_accounts.py b/example/python/permissions/can_get_domain_accounts.py new file mode 100644 index 0000000000..d043998732 --- /dev/null +++ b/example/python/permissions/can_get_domain_accounts.py @@ -0,0 +1,39 @@ +# +# Copyright Soramitsu Co., Ltd. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +# + +import iroha +import commons + +admin = commons.new_user('admin@test') +alice = commons.new_user('alice@test') + + +@commons.hex +def genesis_tx(): + test_permissions = iroha.RolePermissionSet([iroha.Role_kGetDomainAccounts]) + tx = iroha.ModelTransactionBuilder() \ + .createdTime(commons.now()) \ + .creatorAccountId(admin['id']) \ + .addPeer('0.0.0.0:50541', admin['key'].publicKey()) \ + .createRole('admin_role', commons.all_permissions()) \ + .createRole('test_role', test_permissions) \ + .createDomain('test', 'test_role') \ + .createAccount('admin', 'test', admin['key'].publicKey()) \ + .createAccount('alice', 'test', alice['key'].publicKey()) \ + .build() + return iroha.ModelProtoTransaction(tx) \ + .signAndAddSignature(admin['key']).finish() + + +@commons.hex +def account_query(): + tx = iroha.ModelQueryBuilder() \ + .createdTime(commons.now()) \ + .queryCounter(1) \ + .creatorAccountId(alice['id']) \ + .getAccount(admin['id']) \ + .build() + return iroha.ModelProtoQuery(tx) \ + .signAndAddSignature(alice['key']).finish() diff --git a/example/python/permissions/can_get_domain_signatories.py b/example/python/permissions/can_get_domain_signatories.py new file mode 100644 index 0000000000..ffbbe909dd --- /dev/null +++ b/example/python/permissions/can_get_domain_signatories.py @@ -0,0 +1,39 @@ +# +# Copyright Soramitsu Co., Ltd. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +# + +import iroha +import commons + +admin = commons.new_user('admin@test') +alice = commons.new_user('alice@test') + + +@commons.hex +def genesis_tx(): + test_permissions = iroha.RolePermissionSet([iroha.Role_kGetDomainSignatories]) + tx = iroha.ModelTransactionBuilder() \ + .createdTime(commons.now()) \ + .creatorAccountId(admin['id']) \ + .addPeer('0.0.0.0:50541', admin['key'].publicKey()) \ + .createRole('admin_role', commons.all_permissions()) \ + .createRole('test_role', test_permissions) \ + .createDomain('test', 'test_role') \ + .createAccount('admin', 'test', admin['key'].publicKey()) \ + .createAccount('alice', 'test', alice['key'].publicKey()) \ + .build() + return iroha.ModelProtoTransaction(tx) \ + .signAndAddSignature(admin['key']).finish() + + +@commons.hex +def signatories_query(): + tx = iroha.ModelQueryBuilder() \ + .createdTime(commons.now()) \ + .queryCounter(1) \ + .creatorAccountId(alice['id']) \ + .getSignatories(admin['id']) \ + .build() + return iroha.ModelProtoQuery(tx) \ + .signAndAddSignature(alice['key']).finish() diff --git a/example/python/permissions/can_get_my_acc_ast.py b/example/python/permissions/can_get_my_acc_ast.py new file mode 100644 index 0000000000..bb08774303 --- /dev/null +++ b/example/python/permissions/can_get_my_acc_ast.py @@ -0,0 +1,39 @@ +# +# Copyright Soramitsu Co., Ltd. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +# + +import iroha +import commons + +admin = commons.new_user('admin@test') +alice = commons.new_user('alice@test') + + +@commons.hex +def genesis_tx(): + test_permissions = iroha.RolePermissionSet([iroha.Role_kGetMyAccAst]) + tx = iroha.ModelTransactionBuilder() \ + .createdTime(commons.now()) \ + .creatorAccountId(admin['id']) \ + .addPeer('0.0.0.0:50541', admin['key'].publicKey()) \ + .createRole('admin_role', commons.all_permissions()) \ + .createRole('test_role', test_permissions) \ + .createDomain('test', 'test_role') \ + .createAccount('admin', 'test', admin['key'].publicKey()) \ + .createAccount('alice', 'test', alice['key'].publicKey()) \ + .build() + return iroha.ModelProtoTransaction(tx) \ + .signAndAddSignature(admin['key']).finish() + + +@commons.hex +def account_assets_query(): + tx = iroha.ModelQueryBuilder() \ + .createdTime(commons.now()) \ + .queryCounter(1) \ + .creatorAccountId(alice['id']) \ + .getAccountAssets(alice['id']) \ + .build() + return iroha.ModelProtoQuery(tx) \ + .signAndAddSignature(alice['key']).finish() diff --git a/example/python/permissions/can_get_my_acc_ast_txs.py b/example/python/permissions/can_get_my_acc_ast_txs.py new file mode 100644 index 0000000000..c9170f4a35 --- /dev/null +++ b/example/python/permissions/can_get_my_acc_ast_txs.py @@ -0,0 +1,42 @@ +# +# Copyright Soramitsu Co., Ltd. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +# + +import iroha +import commons + +admin = commons.new_user('admin@test') +alice = commons.new_user('alice@test') + + +@commons.hex +def genesis_tx(): + test_permissions = iroha.RolePermissionSet([iroha.Role_kGetMyAccAstTxs]) + tx = iroha.ModelTransactionBuilder() \ + .createdTime(commons.now()) \ + .creatorAccountId(admin['id']) \ + .addPeer('0.0.0.0:50541', admin['key'].publicKey()) \ + .createRole('admin_role', commons.all_permissions()) \ + .createRole('test_role', test_permissions) \ + .createDomain('test', 'test_role') \ + .createAccount('admin', 'test', admin['key'].publicKey()) \ + .createAccount('alice', 'test', alice['key'].publicKey()) \ + .createAsset('coin', 'test', 2) \ + .addAssetQuantity('coin#test', '500.69') \ + .transferAsset(admin['id'], alice['id'], 'coin#test', 'top up', '10.00') \ + .build() + return iroha.ModelProtoTransaction(tx) \ + .signAndAddSignature(admin['key']).finish() + + +@commons.hex +def account_asset_transactions_query(): + tx = iroha.ModelQueryBuilder() \ + .createdTime(commons.now()) \ + .queryCounter(1) \ + .creatorAccountId(alice['id']) \ + .getAccountAssetTransactions(alice['id'], 'coin#test') \ + .build() + return iroha.ModelProtoQuery(tx) \ + .signAndAddSignature(alice['key']).finish() diff --git a/example/python/permissions/can_get_my_acc_detail.py b/example/python/permissions/can_get_my_acc_detail.py new file mode 100644 index 0000000000..0e24b3e0f0 --- /dev/null +++ b/example/python/permissions/can_get_my_acc_detail.py @@ -0,0 +1,39 @@ +# +# Copyright Soramitsu Co., Ltd. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +# + +import iroha +import commons + +admin = commons.new_user('admin@test') +alice = commons.new_user('alice@test') + + +@commons.hex +def genesis_tx(): + test_permissions = iroha.RolePermissionSet([iroha.Role_kGetMyAccDetail]) + tx = iroha.ModelTransactionBuilder() \ + .createdTime(commons.now()) \ + .creatorAccountId(admin['id']) \ + .addPeer('0.0.0.0:50541', admin['key'].publicKey()) \ + .createRole('admin_role', commons.all_permissions()) \ + .createRole('test_role', test_permissions) \ + .createDomain('test', 'test_role') \ + .createAccount('admin', 'test', admin['key'].publicKey()) \ + .createAccount('alice', 'test', alice['key'].publicKey()) \ + .build() + return iroha.ModelProtoTransaction(tx) \ + .signAndAddSignature(admin['key']).finish() + + +@commons.hex +def account_detail_query(): + tx = iroha.ModelQueryBuilder() \ + .createdTime(commons.now()) \ + .queryCounter(1) \ + .creatorAccountId(alice['id']) \ + .getAccountDetail(alice['id']) \ + .build() + return iroha.ModelProtoQuery(tx) \ + .signAndAddSignature(alice['key']).finish() diff --git a/example/python/permissions/can_get_my_acc_txs.py b/example/python/permissions/can_get_my_acc_txs.py new file mode 100644 index 0000000000..b1611fd23d --- /dev/null +++ b/example/python/permissions/can_get_my_acc_txs.py @@ -0,0 +1,39 @@ +# +# Copyright Soramitsu Co., Ltd. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +# + +import iroha +import commons + +admin = commons.new_user('admin@test') +alice = commons.new_user('alice@test') + + +@commons.hex +def genesis_tx(): + test_permissions = iroha.RolePermissionSet([iroha.Role_kGetMyAccTxs]) + tx = iroha.ModelTransactionBuilder() \ + .createdTime(commons.now()) \ + .creatorAccountId(admin['id']) \ + .addPeer('0.0.0.0:50541', admin['key'].publicKey()) \ + .createRole('admin_role', commons.all_permissions()) \ + .createRole('test_role', test_permissions) \ + .createDomain('test', 'test_role') \ + .createAccount('admin', 'test', admin['key'].publicKey()) \ + .createAccount('alice', 'test', alice['key'].publicKey()) \ + .build() + return iroha.ModelProtoTransaction(tx) \ + .signAndAddSignature(admin['key']).finish() + + +@commons.hex +def account_transactions_query(): + tx = iroha.ModelQueryBuilder() \ + .createdTime(commons.now()) \ + .queryCounter(1) \ + .creatorAccountId(alice['id']) \ + .getAccountTransactions(alice['id']) \ + .build() + return iroha.ModelProtoQuery(tx) \ + .signAndAddSignature(alice['key']).finish() diff --git a/example/python/permissions/can_get_my_account.py b/example/python/permissions/can_get_my_account.py new file mode 100644 index 0000000000..522099d4df --- /dev/null +++ b/example/python/permissions/can_get_my_account.py @@ -0,0 +1,39 @@ +# +# Copyright Soramitsu Co., Ltd. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +# + +import iroha +import commons + +admin = commons.new_user('admin@test') +alice = commons.new_user('alice@test') + + +@commons.hex +def genesis_tx(): + test_permissions = iroha.RolePermissionSet([iroha.Role_kGetMyAccount]) + tx = iroha.ModelTransactionBuilder() \ + .createdTime(commons.now()) \ + .creatorAccountId(admin['id']) \ + .addPeer('0.0.0.0:50541', admin['key'].publicKey()) \ + .createRole('admin_role', commons.all_permissions()) \ + .createRole('test_role', test_permissions) \ + .createDomain('test', 'test_role') \ + .createAccount('admin', 'test', admin['key'].publicKey()) \ + .createAccount('alice', 'test', alice['key'].publicKey()) \ + .build() + return iroha.ModelProtoTransaction(tx) \ + .signAndAddSignature(admin['key']).finish() + + +@commons.hex +def account_query(): + tx = iroha.ModelQueryBuilder() \ + .createdTime(commons.now()) \ + .queryCounter(1) \ + .creatorAccountId(alice['id']) \ + .getAccount(alice['id']) \ + .build() + return iroha.ModelProtoQuery(tx) \ + .signAndAddSignature(alice['key']).finish() diff --git a/example/python/permissions/can_get_my_signatories.py b/example/python/permissions/can_get_my_signatories.py new file mode 100644 index 0000000000..ec8f5b4499 --- /dev/null +++ b/example/python/permissions/can_get_my_signatories.py @@ -0,0 +1,39 @@ +# +# Copyright Soramitsu Co., Ltd. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +# + +import iroha +import commons + +admin = commons.new_user('admin@test') +alice = commons.new_user('alice@test') + + +@commons.hex +def genesis_tx(): + test_permissions = iroha.RolePermissionSet([iroha.Role_kGetMySignatories]) + tx = iroha.ModelTransactionBuilder() \ + .createdTime(commons.now()) \ + .creatorAccountId(admin['id']) \ + .addPeer('0.0.0.0:50541', admin['key'].publicKey()) \ + .createRole('admin_role', commons.all_permissions()) \ + .createRole('test_role', test_permissions) \ + .createDomain('test', 'test_role') \ + .createAccount('admin', 'test', admin['key'].publicKey()) \ + .createAccount('alice', 'test', alice['key'].publicKey()) \ + .build() + return iroha.ModelProtoTransaction(tx) \ + .signAndAddSignature(admin['key']).finish() + + +@commons.hex +def signatories_query(): + tx = iroha.ModelQueryBuilder() \ + .createdTime(commons.now()) \ + .queryCounter(1) \ + .creatorAccountId(alice['id']) \ + .getSignatories(alice['id']) \ + .build() + return iroha.ModelProtoQuery(tx) \ + .signAndAddSignature(alice['key']).finish() diff --git a/example/python/permissions/can_get_my_txs.py b/example/python/permissions/can_get_my_txs.py new file mode 100644 index 0000000000..4c281a96a1 --- /dev/null +++ b/example/python/permissions/can_get_my_txs.py @@ -0,0 +1,76 @@ +# +# Copyright Soramitsu Co., Ltd. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +# + +import iroha +import commons + +admin = commons.new_user('admin@first') +alice = commons.new_user('alice@second') + +alice_tx1_hash = None +alice_tx2_hash_blob = None + + +@commons.hex +def genesis_tx(): + test_permissions = iroha.RolePermissionSet([ + iroha.Role_kGetMyTxs, + iroha.Role_kAddAssetQty, + iroha.Role_kCreateAsset + ]) + tx = iroha.ModelTransactionBuilder() \ + .createdTime(commons.now()) \ + .creatorAccountId(admin['id']) \ + .addPeer('0.0.0.0:50541', admin['key'].publicKey()) \ + .createRole('admin_role', commons.all_permissions()) \ + .createRole('test_role', test_permissions) \ + .createDomain('first', 'admin_role') \ + .createDomain('second', 'test_role') \ + .createAccount('admin', 'first', admin['key'].publicKey()) \ + .createAccount('alice', 'second', alice['key'].publicKey()) \ + .build() + return iroha.ModelProtoTransaction(tx) \ + .signAndAddSignature(admin['key']).finish() + + +@commons.hex +def alice_action_1_tx(): + global alice_tx1_hash + tx = iroha.ModelTransactionBuilder() \ + .createdTime(commons.now()) \ + .creatorAccountId(alice['id']) \ + .createAsset('coin', 'first', 2) \ + .build() + alice_tx1_hash = tx.hash() + return iroha.ModelProtoTransaction(tx) \ + .signAndAddSignature(alice['key']).finish() + + +@commons.hex +def alice_action_2_tx(): + global alice_tx2_hash_blob + tx = iroha.ModelTransactionBuilder() \ + .createdTime(commons.now()) \ + .creatorAccountId(alice['id']) \ + .addAssetQuantity('coin#first', '600.30') \ + .build() + alice_tx2_hash_blob = tx.hash().blob() + return iroha.ModelProtoTransaction(tx) \ + .signAndAddSignature(alice['key']).finish() + + +@commons.hex +def transactions_query(): + hashes = iroha.HashVector() + hashes.append(alice_tx1_hash) + hashes.append(iroha.Hash(iroha.Blob(alice_tx2_hash_blob))) + tx = iroha.ModelQueryBuilder() \ + .createdTime(commons.now()) \ + .queryCounter(1) \ + .creatorAccountId(alice['id']) \ + .getTransactions(hashes) \ + .build() + return iroha.ModelProtoQuery(tx) \ + .signAndAddSignature(alice['key']).finish() diff --git a/example/python/permissions/can_get_roles.py b/example/python/permissions/can_get_roles.py new file mode 100644 index 0000000000..defcc3de61 --- /dev/null +++ b/example/python/permissions/can_get_roles.py @@ -0,0 +1,52 @@ +# +# Copyright Soramitsu Co., Ltd. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +# + +import iroha +import commons + +admin = commons.new_user('admin@test') +alice = commons.new_user('alice@test') + + +@commons.hex +def genesis_tx(): + test_permissions = iroha.RolePermissionSet([iroha.Role_kGetRoles]) + tx = iroha.ModelTransactionBuilder() \ + .createdTime(commons.now()) \ + .creatorAccountId(admin['id']) \ + .addPeer('0.0.0.0:50541', admin['key'].publicKey()) \ + .createRole('admin_role', commons.all_permissions()) \ + .createRole('test_role', test_permissions) \ + .createDomain('test', 'test_role') \ + .createAccount('admin', 'test', admin['key'].publicKey()) \ + .createAccount('alice', 'test', alice['key'].publicKey()) \ + .createAsset('coin', 'test', 2) \ + .build() + return iroha.ModelProtoTransaction(tx) \ + .signAndAddSignature(admin['key']).finish() + + +@commons.hex +def get_system_roles_query(): + tx = iroha.ModelQueryBuilder() \ + .createdTime(commons.now()) \ + .queryCounter(1) \ + .creatorAccountId(alice['id']) \ + .getRoles() \ + .build() + return iroha.ModelProtoQuery(tx) \ + .signAndAddSignature(alice['key']).finish() + + +@commons.hex +def get_role_permissions_query(): + tx = iroha.ModelQueryBuilder() \ + .createdTime(commons.now()) \ + .queryCounter(2) \ + .creatorAccountId(alice['id']) \ + .getRolePermissions('admin_role') \ + .build() + return iroha.ModelProtoQuery(tx) \ + .signAndAddSignature(alice['key']).finish() diff --git a/example/python/permissions/can_grant_can_add_my_signatory.py b/example/python/permissions/can_grant_can_add_my_signatory.py new file mode 100644 index 0000000000..20db1278f7 --- /dev/null +++ b/example/python/permissions/can_grant_can_add_my_signatory.py @@ -0,0 +1,41 @@ +# +# Copyright Soramitsu Co., Ltd. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +# + +import iroha +import commons + +admin = commons.new_user('admin@test') +alice = commons.new_user('alice@test') +bob = commons.new_user('bob@test') + + +@commons.hex +def genesis_tx(): + test_permissions = iroha.RolePermissionSet([iroha.Role_kAddMySignatory]) + tx = iroha.ModelTransactionBuilder() \ + .createdTime(commons.now()) \ + .creatorAccountId(admin['id']) \ + .addPeer('0.0.0.0:50541', admin['key'].publicKey()) \ + .createRole('admin_role', commons.all_permissions()) \ + .createRole('test_role', test_permissions) \ + .createDomain('test', 'test_role') \ + .createAccount('admin', 'test', admin['key'].publicKey()) \ + .createAccount('alice', 'test', alice['key'].publicKey()) \ + .createAccount('bob', 'test', bob['key'].publicKey()) \ + .appendRole(admin['id'], 'admin_role') \ + .build() + return iroha.ModelProtoTransaction(tx) \ + .signAndAddSignature(admin['key']).finish() + + +@commons.hex +def grant_can_add_my_signatory_tx(): + tx = iroha.ModelTransactionBuilder() \ + .createdTime(commons.now()) \ + .creatorAccountId(alice['id']) \ + .grantPermission(bob['id'], iroha.Grantable_kAddMySignatory) \ + .build() + return iroha.ModelProtoTransaction(tx) \ + .signAndAddSignature(alice['key']).finish() diff --git a/example/python/permissions/can_grant_can_remove_my_signatory.py b/example/python/permissions/can_grant_can_remove_my_signatory.py new file mode 100644 index 0000000000..1408fe7b33 --- /dev/null +++ b/example/python/permissions/can_grant_can_remove_my_signatory.py @@ -0,0 +1,41 @@ +# +# Copyright Soramitsu Co., Ltd. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +# + +import iroha +import commons + +admin = commons.new_user('admin@test') +alice = commons.new_user('alice@test') +bob = commons.new_user('bob@test') + + +@commons.hex +def genesis_tx(): + test_permissions = iroha.RolePermissionSet([iroha.Role_kRemoveMySignatory]) + tx = iroha.ModelTransactionBuilder() \ + .createdTime(commons.now()) \ + .creatorAccountId(admin['id']) \ + .addPeer('0.0.0.0:50541', admin['key'].publicKey()) \ + .createRole('admin_role', commons.all_permissions()) \ + .createRole('test_role', test_permissions) \ + .createDomain('test', 'test_role') \ + .createAccount('admin', 'test', admin['key'].publicKey()) \ + .createAccount('alice', 'test', alice['key'].publicKey()) \ + .createAccount('bob', 'test', bob['key'].publicKey()) \ + .appendRole(admin['id'], 'admin_role') \ + .build() + return iroha.ModelProtoTransaction(tx) \ + .signAndAddSignature(admin['key']).finish() + + +@commons.hex +def grant_can_remove_my_signatory_tx(): + tx = iroha.ModelTransactionBuilder() \ + .createdTime(commons.now()) \ + .creatorAccountId(alice['id']) \ + .grantPermission(bob['id'], iroha.Grantable_kRemoveMySignatory) \ + .build() + return iroha.ModelProtoTransaction(tx) \ + .signAndAddSignature(alice['key']).finish() diff --git a/example/python/permissions/can_grant_can_set_my_account_detail.py b/example/python/permissions/can_grant_can_set_my_account_detail.py new file mode 100644 index 0000000000..a3bafc826c --- /dev/null +++ b/example/python/permissions/can_grant_can_set_my_account_detail.py @@ -0,0 +1,41 @@ +# +# Copyright Soramitsu Co., Ltd. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +# + +import iroha +import commons + +admin = commons.new_user('admin@test') +alice = commons.new_user('alice@test') +bob = commons.new_user('bob@test') + + +@commons.hex +def genesis_tx(): + test_permissions = iroha.RolePermissionSet([iroha.Role_kSetMyAccountDetail]) + tx = iroha.ModelTransactionBuilder() \ + .createdTime(commons.now()) \ + .creatorAccountId(admin['id']) \ + .addPeer('0.0.0.0:50541', admin['key'].publicKey()) \ + .createRole('admin_role', commons.all_permissions()) \ + .createRole('test_role', test_permissions) \ + .createDomain('test', 'test_role') \ + .createAccount('admin', 'test', admin['key'].publicKey()) \ + .createAccount('alice', 'test', alice['key'].publicKey()) \ + .createAccount('bob', 'test', bob['key'].publicKey()) \ + .appendRole(admin['id'], 'admin_role') \ + .build() + return iroha.ModelProtoTransaction(tx) \ + .signAndAddSignature(admin['key']).finish() + + +@commons.hex +def grant_can_set_my_account_detail_tx(): + tx = iroha.ModelTransactionBuilder() \ + .createdTime(commons.now()) \ + .creatorAccountId(alice['id']) \ + .grantPermission(bob['id'], iroha.Grantable_kSetMyAccountDetail) \ + .build() + return iroha.ModelProtoTransaction(tx) \ + .signAndAddSignature(alice['key']).finish() diff --git a/example/python/permissions/can_grant_can_set_my_quorum.py b/example/python/permissions/can_grant_can_set_my_quorum.py new file mode 100644 index 0000000000..47301e193e --- /dev/null +++ b/example/python/permissions/can_grant_can_set_my_quorum.py @@ -0,0 +1,41 @@ +# +# Copyright Soramitsu Co., Ltd. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +# + +import iroha +import commons + +admin = commons.new_user('admin@test') +alice = commons.new_user('alice@test') +bob = commons.new_user('bob@test') + + +@commons.hex +def genesis_tx(): + test_permissions = iroha.RolePermissionSet([iroha.Role_kSetMyQuorum]) + tx = iroha.ModelTransactionBuilder() \ + .createdTime(commons.now()) \ + .creatorAccountId(admin['id']) \ + .addPeer('0.0.0.0:50541', admin['key'].publicKey()) \ + .createRole('admin_role', commons.all_permissions()) \ + .createRole('test_role', test_permissions) \ + .createDomain('test', 'test_role') \ + .createAccount('admin', 'test', admin['key'].publicKey()) \ + .createAccount('alice', 'test', alice['key'].publicKey()) \ + .createAccount('bob', 'test', bob['key'].publicKey()) \ + .appendRole(admin['id'], 'admin_role') \ + .build() + return iroha.ModelProtoTransaction(tx) \ + .signAndAddSignature(admin['key']).finish() + + +@commons.hex +def grant_can_set_my_quorum_tx(): + tx = iroha.ModelTransactionBuilder() \ + .createdTime(commons.now()) \ + .creatorAccountId(alice['id']) \ + .grantPermission(bob['id'], iroha.Grantable_kSetMyQuorum) \ + .build() + return iroha.ModelProtoTransaction(tx) \ + .signAndAddSignature(alice['key']).finish() diff --git a/example/python/permissions/can_grant_can_transfer_my_assets.py b/example/python/permissions/can_grant_can_transfer_my_assets.py new file mode 100644 index 0000000000..f9b8ab3494 --- /dev/null +++ b/example/python/permissions/can_grant_can_transfer_my_assets.py @@ -0,0 +1,48 @@ +# +# Copyright Soramitsu Co., Ltd. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +# + +import iroha +import commons + +admin = commons.new_user('admin@test') +alice = commons.new_user('alice@test') +bob = commons.new_user('bob@test') + + +@commons.hex +def genesis_tx(): + test_permissions = iroha.RolePermissionSet([ + iroha.Role_kTransferMyAssets, + iroha.Role_kReceive, + iroha.Role_kTransfer + ]) + tx = iroha.ModelTransactionBuilder() \ + .createdTime(commons.now()) \ + .creatorAccountId(admin['id']) \ + .addPeer('0.0.0.0:50541', admin['key'].publicKey()) \ + .createRole('admin_role', commons.all_permissions()) \ + .createRole('test_role', test_permissions) \ + .createDomain('test', 'test_role') \ + .createAccount('admin', 'test', admin['key'].publicKey()) \ + .createAccount('alice', 'test', alice['key'].publicKey()) \ + .createAccount('bob', 'test', bob['key'].publicKey()) \ + .appendRole(admin['id'], 'admin_role') \ + .createAsset('coin', 'test', 2) \ + .addAssetQuantity('coin#test', '100.00') \ + .transferAsset(admin['id'], alice['id'], 'coin#test', 'init top up', '90.00') \ + .build() + return iroha.ModelProtoTransaction(tx) \ + .signAndAddSignature(admin['key']).finish() + + +@commons.hex +def grant_can_transfer_my_assets_tx(): + tx = iroha.ModelTransactionBuilder() \ + .createdTime(commons.now()) \ + .creatorAccountId(alice['id']) \ + .grantPermission(bob['id'], iroha.Grantable_kTransferMyAssets) \ + .build() + return iroha.ModelProtoTransaction(tx) \ + .signAndAddSignature(alice['key']).finish() diff --git a/example/python/permissions/can_read_assets.py b/example/python/permissions/can_read_assets.py new file mode 100644 index 0000000000..58eeb3d3c8 --- /dev/null +++ b/example/python/permissions/can_read_assets.py @@ -0,0 +1,40 @@ +# +# Copyright Soramitsu Co., Ltd. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +# + +import iroha +import commons + +admin = commons.new_user('admin@test') +alice = commons.new_user('alice@test') + + +@commons.hex +def genesis_tx(): + test_permissions = iroha.RolePermissionSet([iroha.Role_kReadAssets]) + tx = iroha.ModelTransactionBuilder() \ + .createdTime(commons.now()) \ + .creatorAccountId(admin['id']) \ + .addPeer('0.0.0.0:50541', admin['key'].publicKey()) \ + .createRole('admin_role', commons.all_permissions()) \ + .createRole('test_role', test_permissions) \ + .createDomain('test', 'test_role') \ + .createAccount('admin', 'test', admin['key'].publicKey()) \ + .createAccount('alice', 'test', alice['key'].publicKey()) \ + .createAsset('coin', 'test', 2) \ + .build() + return iroha.ModelProtoTransaction(tx) \ + .signAndAddSignature(admin['key']).finish() + + +@commons.hex +def get_asset_query(): + tx = iroha.ModelQueryBuilder() \ + .createdTime(commons.now()) \ + .queryCounter(1) \ + .creatorAccountId(alice['id']) \ + .getAssetInfo('coin#test') \ + .build() + return iroha.ModelProtoQuery(tx) \ + .signAndAddSignature(alice['key']).finish() diff --git a/example/python/permissions/can_receive.py b/example/python/permissions/can_receive.py new file mode 100644 index 0000000000..0f9552e15f --- /dev/null +++ b/example/python/permissions/can_receive.py @@ -0,0 +1,47 @@ +# +# Copyright Soramitsu Co., Ltd. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +# + +import iroha +import commons + +admin = commons.new_user('admin@test') +alice = commons.new_user('alice@test') +bob = commons.new_user('bob@test') + + +@commons.hex +def genesis_tx(): + test_permissions = iroha.RolePermissionSet([ + iroha.Role_kReceive, + iroha.Role_kTransfer + ]) + tx = iroha.ModelTransactionBuilder() \ + .createdTime(commons.now()) \ + .creatorAccountId(admin['id']) \ + .addPeer('0.0.0.0:50541', admin['key'].publicKey()) \ + .createRole('admin_role', commons.all_permissions()) \ + .createRole('test_role', test_permissions) \ + .createDomain('test', 'test_role') \ + .createAccount('admin', 'test', admin['key'].publicKey()) \ + .createAccount('alice', 'test', alice['key'].publicKey()) \ + .createAccount('bob', 'test', bob['key'].publicKey()) \ + .createAsset('coin', 'test', 2) \ + .addAssetQuantity('coin#test', '100.00') \ + .transferAsset(admin['id'], alice['id'], 'coin#test', 'init top up', '90.00') \ + .appendRole(admin['id'], 'admin_role') \ + .build() + return iroha.ModelProtoTransaction(tx) \ + .signAndAddSignature(admin['key']).finish() + + +@commons.hex +def transfer_asset_tx(): + tx = iroha.ModelTransactionBuilder() \ + .createdTime(commons.now()) \ + .creatorAccountId(alice['id']) \ + .transferAsset(alice['id'], bob['id'], 'coin#test', 'transfer to Bob', '60.00') \ + .build() + return iroha.ModelProtoTransaction(tx) \ + .signAndAddSignature(alice['key']).finish() diff --git a/example/python/permissions/can_remove_my_signatory.py b/example/python/permissions/can_remove_my_signatory.py new file mode 100644 index 0000000000..1f17ebd338 --- /dev/null +++ b/example/python/permissions/can_remove_my_signatory.py @@ -0,0 +1,57 @@ +# +# Copyright Soramitsu Co., Ltd. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +# + +import iroha +import commons + +admin = commons.new_user('admin@test') +alice = commons.new_user('alice@test') +bob = commons.new_user('bob@test') + + +@commons.hex +def genesis_tx(): + test_permissions = iroha.RolePermissionSet([ + iroha.Role_kRemoveMySignatory, + iroha.Role_kAddSignatory + ]) + tx = iroha.ModelTransactionBuilder() \ + .createdTime(commons.now()) \ + .creatorAccountId(admin['id']) \ + .addPeer('0.0.0.0:50541', admin['key'].publicKey()) \ + .createRole('admin_role', commons.all_permissions()) \ + .createRole('test_role', test_permissions) \ + .createDomain('test', 'test_role') \ + .createAccount('admin', 'test', admin['key'].publicKey()) \ + .createAccount('alice', 'test', alice['key'].publicKey()) \ + .createAccount('bob', 'test', bob['key'].publicKey()) \ + .appendRole(admin['id'], 'admin_role') \ + .build() + return iroha.ModelProtoTransaction(tx) \ + .signAndAddSignature(admin['key']).finish() + + +@commons.hex +def grant_can_remove_my_signatory_tx(): + extra_key = iroha.ModelCrypto().generateKeypair() + tx = iroha.ModelTransactionBuilder() \ + .createdTime(commons.now()) \ + .creatorAccountId(alice['id']) \ + .grantPermission(bob['id'], iroha.Grantable_kRemoveMySignatory) \ + .addSignatory(alice['id'], extra_key.publicKey()) \ + .build() + return iroha.ModelProtoTransaction(tx) \ + .signAndAddSignature(alice['key']).finish() + + +@commons.hex +def remove_signatory_tx(): + tx = iroha.ModelTransactionBuilder() \ + .createdTime(commons.now()) \ + .creatorAccountId(bob['id']) \ + .removeSignatory(alice['id'], alice['key'].publicKey()) \ + .build() + return iroha.ModelProtoTransaction(tx) \ + .signAndAddSignature(bob['key']).finish() diff --git a/example/python/permissions/can_remove_signatory.py b/example/python/permissions/can_remove_signatory.py new file mode 100644 index 0000000000..03bf922f93 --- /dev/null +++ b/example/python/permissions/can_remove_signatory.py @@ -0,0 +1,41 @@ +# +# Copyright Soramitsu Co., Ltd. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +# + +import iroha +import commons + +admin = commons.new_user('admin@test') +alice = commons.new_user('alice@test') + + +@commons.hex +def genesis_tx(): + test_permissions = iroha.RolePermissionSet([iroha.Role_kRemoveSignatory]) + extra_key = iroha.ModelCrypto().generateKeypair() + tx = iroha.ModelTransactionBuilder() \ + .createdTime(commons.now()) \ + .creatorAccountId(admin['id']) \ + .addPeer('0.0.0.0:50541', admin['key'].publicKey()) \ + .createRole('admin_role', commons.all_permissions()) \ + .createRole('test_role', test_permissions) \ + .createDomain('test', 'test_role') \ + .createAccount('admin', 'test', admin['key'].publicKey()) \ + .createAccount('alice', 'test', alice['key'].publicKey()) \ + .appendRole(admin['id'], 'admin_role') \ + .addSignatory(alice['id'], extra_key.publicKey()) \ + .build() + return iroha.ModelProtoTransaction(tx) \ + .signAndAddSignature(admin['key']).finish() + + +@commons.hex +def remove_signatory_tx(): + tx = iroha.ModelTransactionBuilder() \ + .createdTime(commons.now()) \ + .creatorAccountId(alice['id']) \ + .removeSignatory(alice['id'], alice['key'].publicKey()) \ + .build() + return iroha.ModelProtoTransaction(tx) \ + .signAndAddSignature(alice['key']).finish() diff --git a/example/python/permissions/can_set_detail.py b/example/python/permissions/can_set_detail.py new file mode 100644 index 0000000000..577bee5571 --- /dev/null +++ b/example/python/permissions/can_set_detail.py @@ -0,0 +1,39 @@ +# +# Copyright Soramitsu Co., Ltd. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +# + +import iroha +import commons + +admin = commons.new_user('admin@test') +alice = commons.new_user('alice@test') + + +@commons.hex +def genesis_tx(): + test_permissions = iroha.RolePermissionSet([iroha.Role_kSetDetail]) + tx = iroha.ModelTransactionBuilder() \ + .createdTime(commons.now()) \ + .creatorAccountId(admin['id']) \ + .addPeer('0.0.0.0:50541', admin['key'].publicKey()) \ + .createRole('admin_role', commons.all_permissions()) \ + .createRole('test_role', test_permissions) \ + .createDomain('test', 'test_role') \ + .createAccount('admin', 'test', admin['key'].publicKey()) \ + .createAccount('alice', 'test', alice['key'].publicKey()) \ + .appendRole(admin['id'], 'admin_role') \ + .build() + return iroha.ModelProtoTransaction(tx) \ + .signAndAddSignature(admin['key']).finish() + + +@commons.hex +def set_account_detail_tx(): + tx = iroha.ModelTransactionBuilder() \ + .createdTime(commons.now()) \ + .creatorAccountId(alice['id']) \ + .setAccountDetail(admin['id'], 'fav_color', 'red') \ + .build() + return iroha.ModelProtoTransaction(tx) \ + .signAndAddSignature(alice['key']).finish() diff --git a/example/python/permissions/can_set_my_account_detail.py b/example/python/permissions/can_set_my_account_detail.py new file mode 100644 index 0000000000..43477d0a5a --- /dev/null +++ b/example/python/permissions/can_set_my_account_detail.py @@ -0,0 +1,54 @@ +# +# Copyright Soramitsu Co., Ltd. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +# + +import iroha +import commons + +admin = commons.new_user('admin@test') +alice = commons.new_user('alice@test') +bob = commons.new_user('bob@test') + + +@commons.hex +def genesis_tx(): + test_permissions = iroha.RolePermissionSet([ + iroha.Role_kSetMyAccountDetail, + ]) + tx = iroha.ModelTransactionBuilder() \ + .createdTime(commons.now()) \ + .creatorAccountId(admin['id']) \ + .addPeer('0.0.0.0:50541', admin['key'].publicKey()) \ + .createRole('admin_role', commons.all_permissions()) \ + .createRole('test_role', test_permissions) \ + .createDomain('test', 'test_role') \ + .createAccount('admin', 'test', admin['key'].publicKey()) \ + .createAccount('alice', 'test', alice['key'].publicKey()) \ + .createAccount('bob', 'test', bob['key'].publicKey()) \ + .appendRole(admin['id'], 'admin_role') \ + .build() + return iroha.ModelProtoTransaction(tx) \ + .signAndAddSignature(admin['key']).finish() + + +@commons.hex +def grant_permission_tx(): + tx = iroha.ModelTransactionBuilder() \ + .createdTime(commons.now()) \ + .creatorAccountId(alice['id']) \ + .grantPermission(bob['id'], iroha.Grantable_kSetMyAccountDetail) \ + .build() + return iroha.ModelProtoTransaction(tx) \ + .signAndAddSignature(alice['key']).finish() + + +@commons.hex +def set_detail_tx(): + tx = iroha.ModelTransactionBuilder() \ + .createdTime(commons.now()) \ + .creatorAccountId(bob['id']) \ + .setAccountDetail(alice['id'], 'bobs', 'call') \ + .build() + return iroha.ModelProtoTransaction(tx) \ + .signAndAddSignature(bob['key']).finish() diff --git a/example/python/permissions/can_set_my_quorum.py b/example/python/permissions/can_set_my_quorum.py new file mode 100644 index 0000000000..4310d8f55f --- /dev/null +++ b/example/python/permissions/can_set_my_quorum.py @@ -0,0 +1,57 @@ +# +# Copyright Soramitsu Co., Ltd. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +# + +import iroha +import commons + +admin = commons.new_user('admin@test') +alice = commons.new_user('alice@test') +bob = commons.new_user('bob@test') + + +@commons.hex +def genesis_tx(): + test_permissions = iroha.RolePermissionSet([ + iroha.Role_kSetMyQuorum, + iroha.Role_kAddSignatory + ]) + tx = iroha.ModelTransactionBuilder() \ + .createdTime(commons.now()) \ + .creatorAccountId(admin['id']) \ + .addPeer('0.0.0.0:50541', admin['key'].publicKey()) \ + .createRole('admin_role', commons.all_permissions()) \ + .createRole('test_role', test_permissions) \ + .createDomain('test', 'test_role') \ + .createAccount('admin', 'test', admin['key'].publicKey()) \ + .createAccount('alice', 'test', alice['key'].publicKey()) \ + .createAccount('bob', 'test', bob['key'].publicKey()) \ + .appendRole(admin['id'], 'admin_role') \ + .build() + return iroha.ModelProtoTransaction(tx) \ + .signAndAddSignature(admin['key']).finish() + + +@commons.hex +def grant_can_set_my_quorum_tx(): + extra_key = iroha.ModelCrypto().generateKeypair() + tx = iroha.ModelTransactionBuilder() \ + .createdTime(commons.now()) \ + .creatorAccountId(alice['id']) \ + .grantPermission(bob['id'], iroha.Grantable_kSetMyQuorum) \ + .addSignatory(alice['id'], extra_key.publicKey()) \ + .build() + return iroha.ModelProtoTransaction(tx) \ + .signAndAddSignature(alice['key']).finish() + + +@commons.hex +def set_quorum_tx(): + tx = iroha.ModelTransactionBuilder() \ + .createdTime(commons.now()) \ + .creatorAccountId(bob['id']) \ + .setAccountQuorum(alice['id'], 2) \ + .build() + return iroha.ModelProtoTransaction(tx) \ + .signAndAddSignature(bob['key']).finish() diff --git a/example/python/permissions/can_set_quorum.py b/example/python/permissions/can_set_quorum.py new file mode 100644 index 0000000000..33d0b6235b --- /dev/null +++ b/example/python/permissions/can_set_quorum.py @@ -0,0 +1,42 @@ +# +# Copyright Soramitsu Co., Ltd. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +# + +import iroha +import commons + +admin = commons.new_user('admin@test') +alice = commons.new_user('alice@test') + + +@commons.hex +def genesis_tx(): + test_permissions = iroha.RolePermissionSet([iroha.Role_kSetQuorum]) + extra_key = iroha.ModelCrypto().generateKeypair() + tx = iroha.ModelTransactionBuilder() \ + .createdTime(commons.now()) \ + .creatorAccountId(admin['id']) \ + .addPeer('0.0.0.0:50541', admin['key'].publicKey()) \ + .createRole('admin_role', commons.all_permissions()) \ + .createRole('test_role', test_permissions) \ + .createDomain('test', 'test_role') \ + .createAccount('admin', 'test', admin['key'].publicKey()) \ + .createAccount('alice', 'test', alice['key'].publicKey()) \ + .appendRole(admin['id'], 'admin_role') \ + .addSignatory(alice['id'], extra_key.publicKey()) \ + .build() + return iroha.ModelProtoTransaction(tx) \ + .signAndAddSignature(admin['key']).finish() + + +@commons.hex +def set_quorum_tx(): + # Quourum cannot be greater than amount of keys linked to an account + tx = iroha.ModelTransactionBuilder() \ + .createdTime(commons.now()) \ + .creatorAccountId(alice['id']) \ + .setAccountQuorum(alice['id'], 2) \ + .build() + return iroha.ModelProtoTransaction(tx) \ + .signAndAddSignature(alice['key']).finish() diff --git a/example/python/permissions/can_subtract_asset_qty.py b/example/python/permissions/can_subtract_asset_qty.py new file mode 100644 index 0000000000..e57dc4dd0a --- /dev/null +++ b/example/python/permissions/can_subtract_asset_qty.py @@ -0,0 +1,42 @@ +# +# Copyright Soramitsu Co., Ltd. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +# + +import iroha +import commons + +admin = commons.new_user('admin@test') +alice = commons.new_user('alice@test') + + +@commons.hex +def genesis_tx(): + test_permissions = iroha.RolePermissionSet([iroha.Role_kSubtractAssetQty]) + tx = iroha.ModelTransactionBuilder() \ + .createdTime(commons.now()) \ + .creatorAccountId(admin['id']) \ + .addPeer('0.0.0.0:50541', admin['key'].publicKey()) \ + .createRole('admin_role', commons.all_permissions()) \ + .createRole('test_role', test_permissions) \ + .createDomain('test', 'test_role') \ + .createAccount('admin', 'test', admin['key'].publicKey()) \ + .createAccount('alice', 'test', alice['key'].publicKey()) \ + .appendRole(admin['id'], 'admin_role') \ + .createAsset('coin', 'test', 2) \ + .addAssetQuantity('coin#test', '1000.00') \ + .transferAsset(admin['id'], alice['id'], 'coin#test', 'init top up', '999.99') \ + .build() + return iroha.ModelProtoTransaction(tx) \ + .signAndAddSignature(admin['key']).finish() + + +@commons.hex +def subtract_asset_tx(): + tx = iroha.ModelTransactionBuilder() \ + .createdTime(commons.now()) \ + .creatorAccountId(alice['id']) \ + .subtractAssetQuantity('coin#test', '999.99') \ + .build() + return iroha.ModelProtoTransaction(tx) \ + .signAndAddSignature(alice['key']).finish() diff --git a/example/python/permissions/can_transfer.py b/example/python/permissions/can_transfer.py new file mode 100644 index 0000000000..ddb9d069b9 --- /dev/null +++ b/example/python/permissions/can_transfer.py @@ -0,0 +1,11 @@ +# +# Copyright Soramitsu Co., Ltd. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +# + +import can_receive + + +# Please see example for can_receive permission. +# By design can_receive and can_transfer permissions +# can be tested only together. diff --git a/example/python/permissions/can_transfer_my_assets.py b/example/python/permissions/can_transfer_my_assets.py new file mode 100644 index 0000000000..547bc6c2a7 --- /dev/null +++ b/example/python/permissions/can_transfer_my_assets.py @@ -0,0 +1,59 @@ +# +# Copyright Soramitsu Co., Ltd. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +# + +import iroha +import commons + +admin = commons.new_user('admin@test') +alice = commons.new_user('alice@test') +bob = commons.new_user('bob@test') + + +@commons.hex +def genesis_tx(): + test_permissions = iroha.RolePermissionSet([ + iroha.Role_kTransferMyAssets, + iroha.Role_kReceive, + iroha.Role_kTransfer + ]) + tx = iroha.ModelTransactionBuilder() \ + .createdTime(commons.now()) \ + .creatorAccountId(admin['id']) \ + .addPeer('0.0.0.0:50541', admin['key'].publicKey()) \ + .createRole('admin_role', commons.all_permissions()) \ + .createRole('test_role', test_permissions) \ + .createDomain('test', 'test_role') \ + .createAccount('admin', 'test', admin['key'].publicKey()) \ + .createAccount('alice', 'test', alice['key'].publicKey()) \ + .createAccount('bob', 'test', bob['key'].publicKey()) \ + .appendRole(admin['id'], 'admin_role') \ + .createAsset('coin', 'test', 2) \ + .addAssetQuantity('coin#test', '100.00') \ + .transferAsset(admin['id'], alice['id'], 'coin#test', 'init top up', '90.00') \ + .build() + return iroha.ModelProtoTransaction(tx) \ + .signAndAddSignature(admin['key']).finish() + + +@commons.hex +def grant_permission_tx(): + tx = iroha.ModelTransactionBuilder() \ + .createdTime(commons.now()) \ + .creatorAccountId(alice['id']) \ + .grantPermission(bob['id'], iroha.Grantable_kTransferMyAssets) \ + .build() + return iroha.ModelProtoTransaction(tx) \ + .signAndAddSignature(alice['key']).finish() + + +@commons.hex +def transfer_asset_tx(): + tx = iroha.ModelTransactionBuilder() \ + .createdTime(commons.now()) \ + .creatorAccountId(bob['id']) \ + .transferAsset(alice['id'], admin['id'], 'coin#test', 'transfer from alice to admin done by bob', '60.00') \ + .build() + return iroha.ModelProtoTransaction(tx) \ + .signAndAddSignature(bob['key']).finish() diff --git a/example/python/permissions/commons.py b/example/python/permissions/commons.py new file mode 100644 index 0000000000..dcac6f2517 --- /dev/null +++ b/example/python/permissions/commons.py @@ -0,0 +1,79 @@ +# +# Copyright Soramitsu Co., Ltd. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +# + +import iroha +from time import time + + +def now(): + return int(time() * 1000) + + +def all_permissions(): + return iroha.RolePermissionSet([ + iroha.Role_kAppendRole, + iroha.Role_kCreateRole, + iroha.Role_kDetachRole, + iroha.Role_kAddAssetQty, + iroha.Role_kSubtractAssetQty, + iroha.Role_kAddPeer, + iroha.Role_kAddSignatory, + iroha.Role_kRemoveSignatory, + iroha.Role_kSetQuorum, + iroha.Role_kCreateAccount, + iroha.Role_kSetDetail, + iroha.Role_kCreateAsset, + iroha.Role_kTransfer, + iroha.Role_kReceive, + iroha.Role_kCreateDomain, + iroha.Role_kReadAssets, + iroha.Role_kGetRoles, + iroha.Role_kGetMyAccount, + iroha.Role_kGetAllAccounts, + iroha.Role_kGetDomainAccounts, + iroha.Role_kGetMySignatories, + iroha.Role_kGetAllSignatories, + iroha.Role_kGetDomainSignatories, + iroha.Role_kGetMyAccAst, + iroha.Role_kGetAllAccAst, + iroha.Role_kGetDomainAccAst, + iroha.Role_kGetMyAccDetail, + iroha.Role_kGetAllAccDetail, + iroha.Role_kGetDomainAccDetail, + iroha.Role_kGetMyAccTxs, + iroha.Role_kGetAllAccTxs, + iroha.Role_kGetDomainAccTxs, + iroha.Role_kGetMyAccAstTxs, + iroha.Role_kGetAllAccAstTxs, + iroha.Role_kGetDomainAccAstTxs, + iroha.Role_kGetMyTxs, + iroha.Role_kGetAllTxs, + iroha.Role_kSetMyQuorum, + iroha.Role_kAddMySignatory, + iroha.Role_kRemoveMySignatory, + iroha.Role_kTransferMyAssets, + iroha.Role_kSetMyAccountDetail, + iroha.Role_kGetBlocks + ]) + + +def new_user(user_id): + key = iroha.ModelCrypto().generateKeypair() + if user_id.lower().startswith('admin'): + print('K{}'.format(key.privateKey().hex())) + return { + 'id': user_id, + 'key': key + } + + +def hex(generator): + """ + Decorator for transactions' and queries generators. + + Allows preserving the type of binaries for Binary Testing Framework. + """ + prefix = 'T' if generator.__name__.lower().endswith('tx') else 'Q' + print('{}{}'.format(prefix, generator().hex())) diff --git a/test/integration/CMakeLists.txt b/test/integration/CMakeLists.txt index a63e3c16a3..eb60795e34 100644 --- a/test/integration/CMakeLists.txt +++ b/test/integration/CMakeLists.txt @@ -16,6 +16,7 @@ # add_subdirectory(acceptance) +add_subdirectory(binary) add_subdirectory(consensus) add_subdirectory(pipeline) add_subdirectory(transport) diff --git a/test/integration/binary/CMakeLists.txt b/test/integration/binary/CMakeLists.txt new file mode 100644 index 0000000000..e3daa79633 --- /dev/null +++ b/test/integration/binary/CMakeLists.txt @@ -0,0 +1,40 @@ + +if (SWIG_PYTHON OR SWIG_JAVA) + get_property(SWIG_BUILD_DIR GLOBAL PROPERTY SWIG_BUILD_DIR) +endif () + +if (SWIG_PYTHON) + if (SUPPORT_PYTHON2) + find_package(PythonInterp 2.7 REQUIRED) + else () + find_package(PythonInterp 3.5 REQUIRED) + endif () + + # TODO, igor-egorov, 2018-06-27, IR-1481, move that foreach to a separate target + foreach (item "block" "commands" "primitive" "queries") + compile_proto_to_python("${item}.proto") + list(APPEND PROTO_SWIG_DEPS "${SWIG_BUILD_DIR}/${item}_pb2.py") + endforeach (item) + + add_executable(binary_test + launchers.cpp + binaries_test.cpp + ) + target_link_libraries(binary_test + gtest + gmock + shared_model_proto_backend + integration_framework + bindings + ) + target_include_directories(binary_test PUBLIC ${PROJECT_SOURCE_DIR}/test) + + add_dependencies(binary_test irohapy) + add_test( + NAME "python_binary_test" + COMMAND ${CMAKE_COMMAND} -E + env "PYTHON_INTERPRETER=${PYTHON_EXECUTABLE}" "PYTHONPATH=${SWIG_BUILD_DIR}" + "ROOT_DIR=${PROJECT_SOURCE_DIR}" $ --gtest_filter=*/0* + ) + +endif () diff --git a/test/integration/binary/binaries_test.cpp b/test/integration/binary/binaries_test.cpp new file mode 100644 index 0000000000..b7e7084076 --- /dev/null +++ b/test/integration/binary/binaries_test.cpp @@ -0,0 +1,204 @@ +/** + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "integration/binary/binaries_test_fixture.hpp" + +using namespace shared_model::interface; + +using BinaryTestTypes = ::testing::Types; + +TYPED_TEST_CASE(BinaryTestFixture, BinaryTestTypes); + +// -------------------------- Transactions -------------------------- + +TYPED_TEST(BinaryTestFixture, can_create_account) { + this->doTest(2); +} + +TYPED_TEST(BinaryTestFixture, can_set_detail) { + this->doTest(2); +} + +TYPED_TEST(BinaryTestFixture, can_grant_can_set_my_account_detail) { + this->doTest(2); +} + +TYPED_TEST(BinaryTestFixture, can_set_my_account_detail) { + this->doTest(3); +} + +TYPED_TEST(BinaryTestFixture, can_create_asset) { + this->doTest(2); +} + +TYPED_TEST(BinaryTestFixture, can_receive) { + this->doTest(2); +} + +TYPED_TEST(BinaryTestFixture, can_transfer) { + this->doTest(2); +} + +TYPED_TEST(BinaryTestFixture, can_grant_can_transfer_my_assets) { + this->doTest(2); +} + +TYPED_TEST(BinaryTestFixture, can_transfer_my_assets) { + this->doTest(3); +} + +TYPED_TEST(BinaryTestFixture, can_add_asset_qty) { + this->doTest(2); +} + +TYPED_TEST(BinaryTestFixture, can_subtract_asset_qty) { + this->doTest(2); +} + +TYPED_TEST(BinaryTestFixture, can_create_domain) { + this->doTest(2); +} + +TYPED_TEST(BinaryTestFixture, can_add_peer) { + this->doTest(2); +} + +TYPED_TEST(BinaryTestFixture, can_create_role) { + this->doTest(2); +} + +TYPED_TEST(BinaryTestFixture, can_append_role) { + this->doTest(2); +} + +TYPED_TEST(BinaryTestFixture, can_detach_role) { + this->doTest(2); +} + +TYPED_TEST(BinaryTestFixture, can_add_signatory) { + this->doTest(2); +} + +TYPED_TEST(BinaryTestFixture, can_grant_can_add_my_signatory) { + this->doTest(2); +} + +TYPED_TEST(BinaryTestFixture, can_grant_can_remove_my_signatory) { + this->doTest(2); +} + +TYPED_TEST(BinaryTestFixture, can_grant_can_set_my_quorum) { + this->doTest(2); +} + +TYPED_TEST(BinaryTestFixture, can_add_my_signatory) { + this->doTest(3); +} + +TYPED_TEST(BinaryTestFixture, can_remove_signatory) { + this->doTest(2); +} + +TYPED_TEST(BinaryTestFixture, can_set_my_quorum) { + this->doTest(3); +} + +TYPED_TEST(BinaryTestFixture, can_remove_my_signatory) { + this->doTest(3); +} + +TYPED_TEST(BinaryTestFixture, can_set_quorum) { + this->doTest(2); +} + +// -------------------------- Queries -------------------------- + +TYPED_TEST(BinaryTestFixture, can_get_all_acc_detail) { + this->template doTest(1, 1); +} + +TYPED_TEST(BinaryTestFixture, can_get_all_accounts) { + this->template doTest(1, 1); +} + +TYPED_TEST(BinaryTestFixture, can_get_domain_acc_detail) { + this->template doTest(1, 1); +} + +TYPED_TEST(BinaryTestFixture, can_get_domain_accounts) { + this->template doTest(1, 1); +} + +TYPED_TEST(BinaryTestFixture, can_get_my_acc_detail) { + this->template doTest(1, 1); +} + +TYPED_TEST(BinaryTestFixture, can_get_my_account) { + this->template doTest(1, 1); +} + +TYPED_TEST(BinaryTestFixture, can_get_all_acc_ast) { + this->template doTest(1, 1); +} + +TYPED_TEST(BinaryTestFixture, can_get_domain_acc_ast) { + this->template doTest(1, 1); +} + +TYPED_TEST(BinaryTestFixture, can_get_my_acc_ast) { + this->template doTest(1, 1); +} + +TYPED_TEST(BinaryTestFixture, can_get_all_acc_ast_txs) { + this->template doTest(1, 1); +} + +TYPED_TEST(BinaryTestFixture, can_get_domain_acc_ast_txs) { + this->template doTest(1, 1); +} + +TYPED_TEST(BinaryTestFixture, can_get_my_acc_ast_txs) { + this->template doTest(1, 1); +} + +TYPED_TEST(BinaryTestFixture, can_get_all_acc_txs) { + this->template doTest(1, 1); +} + +TYPED_TEST(BinaryTestFixture, can_get_domain_acc_txs) { + this->template doTest(1, 1); +} + +TYPED_TEST(BinaryTestFixture, can_get_my_acc_txs) { + this->template doTest(1, 1); +} + +TYPED_TEST(BinaryTestFixture, can_read_assets) { + this->template doTest(1, 1); +} + +TYPED_TEST(BinaryTestFixture, can_get_roles) { + this->template doTest(1, 2); +} + +TYPED_TEST(BinaryTestFixture, can_get_all_signatories) { + this->template doTest(1, 1); +} + +TYPED_TEST(BinaryTestFixture, can_get_domain_signatories) { + this->template doTest(1, 1); +} + +TYPED_TEST(BinaryTestFixture, can_get_my_signatories) { + this->template doTest(1, 1); +} + +TYPED_TEST(BinaryTestFixture, can_get_all_txs) { + this->template doTest(3, 1); +} + +TYPED_TEST(BinaryTestFixture, can_get_my_txs) { + this->template doTest(3, 1); +} diff --git a/test/integration/binary/binaries_test_fixture.hpp b/test/integration/binary/binaries_test_fixture.hpp new file mode 100644 index 0000000000..8e8c52a3d2 --- /dev/null +++ b/test/integration/binary/binaries_test_fixture.hpp @@ -0,0 +1,205 @@ +/** + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef IROHA_BINARIES_TEST_FIXTURE_HPP +#define IROHA_BINARIES_TEST_FIXTURE_HPP + +#include + +#include +#include "builders/protobuf/block.hpp" +#include "framework/integration_framework/integration_test_framework.hpp" +#include "framework/specified_visitor.hpp" +#include "integration/binary/launchers.hpp" + + +namespace shared_model { + + namespace proto { + class Query; + } + + namespace interface { + + class Block; + class AccountDetailResponse; + class AccountAssetResponse; + class AccountResponse; + class AssetResponse; + class RolePermissionsResponse; + class RolesResponse; + class SignatoriesResponse; + class TransactionsResponse; + + } // namespace interface + +} // namespace shared_model + +namespace query_validation { + + using QueryIterator = std::vector::iterator; + + namespace internal { + + /** + * Helper type that is used as a marker of the end of types list in variadic + * templates. + * Should not be used outside of query_validation namespace. + */ + class Void {}; + + /** + * Asserts that actual query response type equals to expected type. + * + * @tparam ExpectedResponseType - expected type of QueryResponse object + * @param response - QueryResponse object to check + */ + template + inline void checkQueryResponseType( + const shared_model::proto::QueryResponse &response) { + ASSERT_NO_THROW(boost::apply_visitor( + framework::SpecifiedVisitor(), response.get())); + } + + /** + * Recursively iterates over a list of types and a vector of queries, + * executes queries and asserts that query responses types equal to expected + * types. + * + * @tparam Head - the type of expected query response for the current query + * @tparam Tail - the rest of expected query response types + * @param it - points to query needs to be checked on current step + * @param end - queries' vector iterator that points to .end() + * @param itf - already initialized ITF instance that will be used queries + * execution. + */ + template + inline void _validateQueries(::query_validation::QueryIterator it, + ::query_validation::QueryIterator end, + integration_framework::IntegrationTestFramework &itf) { + if (it != end) { + itf.sendQuery(*it, checkQueryResponseType); + _validateQueries(++it, end, itf); + } + } + + /** + * Handles terminating scenario for recursion made over variadic templates. + */ + template <> + inline void _validateQueries( + ::query_validation::QueryIterator it, + ::query_validation::QueryIterator end, + integration_framework::IntegrationTestFramework &itf){}; + + } // namespace internal + + /** + * Sequentially executes queries via ITF and asserts that types of actual + * query responses equal to expected types. + * + * @tparam ExpectedResponsesTypes - A list of types of expected query + * responses. The order of types should match the order of queries in a + * vector. + * @param it - queries' vector iterator that points to .begin() + * @param end - queries' vector iterator that points to .end() + * @param itf - already initialized ITF instance that will be used queries + * execution. + */ + template + inline void validateQueriesResponseTypes(QueryIterator it, + QueryIterator end, + integration_framework::IntegrationTestFramework &itf) { + internal::_validateQueries( + it, end, itf); + } + +} // namespace query_validation + +/** + * @tparam Launcher - class that can run scripts on target language for + * transactions' and queries' binaries generation. + */ +template +class BinaryTestFixture : public ::testing::Test { + public: + Launcher launcher; + + /** + * Generates genesis block for ITF initialization using the first transaction + * and keypair received from launcher. + * + * @return - genesis block + */ + shared_model::proto::Block genesis() { + return makeGenesis(launcher.transactions[0], *launcher.admin_key); + } + + /** + * Callback that asserts that passed block contains one transaction. + * + * @param result - Block object + */ + static void blockWithTransactionValidation( + const std::shared_ptr &result) { + ASSERT_EQ(result->transactions().size(), 1); + } + + /** + * Initalizes ITF. Sequentially applies transacttions and then queries + * provided by launcher. Checks that every transaction was committed. + * Asserts that query response types match to expected types. + * + * @tparam ExpectedQueryResponsesTypes - list of expected query response + * types. The order should match order of queries that were provided by + * launcher. + * @param transactions_expected - expected amount of transactions + * @param queries_expected - expected amount of queries + */ + template + void doTest(const unsigned &transactions_expected = 0, + const unsigned &queries_expected = 0) { + if (launcher.initialized(transactions_expected, queries_expected)) { + integration_framework::IntegrationTestFramework itf(1); + + itf.setInitialState(launcher.admin_key.value(), genesis()); + + std::for_each( + std::next( // first transaction was used as genesis transaction + launcher.transactions.begin()), + launcher.transactions.end(), + [&itf](const auto &tx) { + itf.sendTx(tx).checkBlock(BinaryTestFixture::blockWithTransactionValidation); + }); + + query_validation::validateQueriesResponseTypes< + ExpectedQueryResponsesTypes...>( + launcher.queries.begin(), launcher.queries.end(), itf); + + itf.done(); + } + } + + protected: + virtual void SetUp() { + launcher(::testing::UnitTest::GetInstance()->current_test_info()->name()); + } + + shared_model::proto::Block makeGenesis( + const shared_model::proto::Transaction &genesis_tx, + const shared_model::crypto::Keypair &keypair) { + return shared_model::proto::BlockBuilder() + .transactions(std::vector{genesis_tx}) + .height(1) + .prevHash(shared_model::crypto::DefaultHashProvider::makeHash( + shared_model::crypto::Blob(""))) + .createdTime(iroha::time::now()) + .build() + .signAndAddSignature(keypair) + .finish(); + } +}; + +#endif // IROHA_BINARIES_TEST_FIXTURE_HPP diff --git a/test/integration/binary/launchers.cpp b/test/integration/binary/launchers.cpp new file mode 100644 index 0000000000..c53cf68a7b --- /dev/null +++ b/test/integration/binary/launchers.cpp @@ -0,0 +1,102 @@ +/** + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "integration/binary/launchers.hpp" + +#include + +#include +#include "bindings/model_crypto.hpp" +#include "common/byteutils.hpp" + +using namespace boost::process; + +namespace binary_test { + + constexpr auto cTimeToKill = std::chrono::minutes(15); + + void Launcher::operator()(const std::string &example) { + ipstream pipe; + const auto &command = launchCommand(example); + if (command.empty()) { + FAIL() << "Launcher provided empty command"; + } + child c(command, std_out > pipe); + auto terminated = c.wait_for(cTimeToKill); + if (not terminated) { + c.terminate(); + FAIL() << "Child process was terminated because execution time limit " + "has been exceeded"; + } + readBinaries(pipe); + } + + void Launcher::readBinaries(ipstream &stream) { + transactions.clear(); + queries.clear(); + std::string packed_line; + std::string raw_payload; + while (stream and std::getline(stream, packed_line) + and packed_line.size() > 1) { + raw_payload = packed_line.substr(1); + if (auto byte_string = iroha::hexstringToBytestring(raw_payload)) { + auto binary_type = packed_line.at(0); + switch (binary_type) { + case 'K': { + if (not admin_key) { + admin_key = shared_model::bindings::ModelCrypto().fromPrivateKey( + raw_payload); + } + break; + } + case 'T': { + iroha::protocol::Transaction proto_tx; + if (proto_tx.ParseFromString(*byte_string)) { + transactions.emplace_back(std::move(proto_tx)); + } + break; + } + case 'Q': { + iroha::protocol::Query proto_query; + if (proto_query.ParseFromString(*byte_string)) { + queries.emplace_back(std::move(proto_query)); + } + break; + } + } // switch (binary_type) + } + } + } + + bool Launcher::initialized(const unsigned &transactions_expected, + const unsigned &queries_expected) { + checkAsserts(transactions_expected, queries_expected); + return admin_key and transactions.size() == transactions_expected + and queries.size() == queries_expected; + } + + void Launcher::checkAsserts(const unsigned &transactions_expected, + const unsigned &queries_expected) { + ASSERT_TRUE(admin_key); + ASSERT_EQ(transactions.size(), transactions_expected); + ASSERT_EQ(queries.size(), queries_expected); + } + + std::string PythonLauncher::launchCommand(const std::string &example) { + std::stringstream s; + // TODO, igor-egorov, 2018-06-30, IR-1488, avoid bash and use + // boost::filesystem + s << "bash -c \"${PYTHON_INTERPRETER} " + "${ROOT_DIR}/example/python/permissions/" + << example << ".py\""; + return s.str(); + } + + std::string JavaLauncher::launchCommand(const std::string &example) { + return ""; + // TODO, igor-egorov, 2018-06-20, IR-1389 + } + +} // namespace binary_test diff --git a/test/integration/binary/launchers.hpp b/test/integration/binary/launchers.hpp new file mode 100644 index 0000000000..7bf220b88d --- /dev/null +++ b/test/integration/binary/launchers.hpp @@ -0,0 +1,83 @@ +/** + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef IROHA_LAUNCHERS_HPP +#define IROHA_LAUNCHERS_HPP + +#include +#include +#include +#include "backend/protobuf/queries/proto_query.hpp" +#include "backend/protobuf/transaction.hpp" +#include "cryptography/keypair.hpp" + +namespace binary_test { + + /** + * Specifies interface for running language-dependent generators of + * transactions' and queries' binaries for test cases. + */ + class Launcher { + public: + /** + * Composes language-dependent command for launching a script for a certain + * test case. + * + * @param test_case - name of test case + * @return - string, a command to be executed + */ + virtual std::string launchCommand(const std::string &test_case) = 0; + + /** + * Runs external binaries generator for a specific test_case and consumes + * its output. Initializes admin_key, transactions vector, and queries + * vector fields. + * + * @param example - name of test_case scenario + */ + void operator()(const std::string &example); + + /** + * Asserts that amount of received transactions and queries equals to + * expected values. + * + * @param transactions_expected - expected amount of transactions + * @param queries_expected - expected amount of queries + * @return - assertion result + */ + bool initialized(const unsigned &transactions_expected = 0, + const unsigned &queries_expected = 0); + + boost::optional admin_key; + std::vector transactions; + std::vector queries; + + protected: + /** + * Parses binaries generator output and initalizes class fields. + * + * @param stream - standard output of external binaries generator. + * It is expected that entities are divided by newline separator. + * The first entity is expected to be a binary of admin's keypair. + * The next lines are transactions' or queries' binaries. + * The first read transaction will be considered as genesis transaction. + */ + void readBinaries(boost::process::ipstream &stream); + void checkAsserts(const unsigned &transactions_expected, + const unsigned &queries_expected); + }; + + class PythonLauncher : public Launcher { + public: + std::string launchCommand(const std::string &example) override; + }; + + class JavaLauncher : public Launcher { + public: + std::string launchCommand(const std::string &example) override; + }; + +} // namespace binary_test + +#endif // IROHA_LAUNCHERS_HPP From 094f1ba42b31d7d77b68f696bb2d055601172095 Mon Sep 17 00:00:00 2001 From: Kitsu Date: Sat, 7 Jul 2018 21:42:07 +0300 Subject: [PATCH 31/97] Fix issue with synchronization after time gap (#1521) - Reorder ContainerValidator ctor params - Reuse FieldValidator among derived classes of SignableModelValidator - Add generic ctor for SM obj validators - Parametrize FieldValidator with time provider - Use block time as validation pivot in BlockLoader Signed-off-by: Kitsu --- irohad/main/impl/block_loader_init.cpp | 5 +-- irohad/network/impl/block_loader_impl.cpp | 40 ++++++++++++------ irohad/network/impl/block_loader_impl.hpp | 10 +---- .../validators/any_block_validator.hpp | 2 +- shared_model/validators/block_validator.hpp | 5 +++ .../validators/blocks_query_validator.hpp | 5 ++- .../validators/container_validator.hpp | 4 +- .../validators/empty_block_validator.hpp | 2 +- shared_model/validators/field_validator.cpp | 9 ++-- shared_model/validators/field_validator.hpp | 16 +++++-- .../validators/proposal_validator.hpp | 5 +++ shared_model/validators/query_validator.hpp | 2 +- .../validators/signable_validator.hpp | 7 +++- .../validators/transaction_validator.hpp | 2 +- .../irohad/network/block_loader_test.cpp | 42 +++++++++---------- 15 files changed, 92 insertions(+), 64 deletions(-) diff --git a/irohad/main/impl/block_loader_init.cpp b/irohad/main/impl/block_loader_init.cpp index 7a29000af9..d4afd069d8 100644 --- a/irohad/main/impl/block_loader_init.cpp +++ b/irohad/main/impl/block_loader_init.cpp @@ -28,10 +28,7 @@ auto BlockLoaderInit::createService(std::shared_ptr storage) { auto BlockLoaderInit::createLoader(std::shared_ptr peer_query, std::shared_ptr storage) { - return std::make_shared( - peer_query, - storage, - std::make_shared()); + return std::make_shared(peer_query, storage); } std::shared_ptr BlockLoaderInit::initBlockLoader( diff --git a/irohad/network/impl/block_loader_impl.cpp b/irohad/network/impl/block_loader_impl.cpp index 28b12e01b3..1f31f2f612 100644 --- a/irohad/network/impl/block_loader_impl.cpp +++ b/irohad/network/impl/block_loader_impl.cpp @@ -23,20 +23,17 @@ #include "builders/protobuf/transport_builder.hpp" #include "interfaces/common_objects/peer.hpp" #include "network/impl/grpc_channel_builder.hpp" +#include "validators/default_validator.hpp" using namespace iroha::ametsuchi; using namespace iroha::network; using namespace shared_model::crypto; using namespace shared_model::interface; +namespace val = shared_model::validation; -BlockLoaderImpl::BlockLoaderImpl( - std::shared_ptr peer_query, - std::shared_ptr block_query, - std::shared_ptr - stateless_validator) - : peer_query_(std::move(peer_query)), - block_query_(std::move(block_query)), - stateless_validator_(stateless_validator) { +BlockLoaderImpl::BlockLoaderImpl(std::shared_ptr peer_query, + std::shared_ptr block_query) + : peer_query_(std::move(peer_query)), block_query_(std::move(block_query)) { log_ = logger::log("BlockLoaderImpl"); } @@ -45,6 +42,21 @@ const char *kTopBlockRetrieveFail = "Failed to retrieve top block"; const char *kPeerRetrieveFail = "Failed to retrieve peers"; const char *kPeerFindFail = "Failed to find requested peer"; +struct TimerWrapper : public val::FieldValidator { + explicit TimerWrapper(iroha::ts64_t t) + : FieldValidator(val::FieldValidator::kDefaultFutureGap, + [=] { return t; }) {} +}; +using BlockValidatorInternal = + val::BlockValidator>; +using Validator = + val::SignableModelValidator; + rxcpp::observable> BlockLoaderImpl::retrieveBlocks( const PublicKey &peer_pubkey) { return rxcpp::observable<>::create>( @@ -55,7 +67,8 @@ rxcpp::observable> BlockLoaderImpl::retrieveBlocks( expected::Value> block) { top_block = block.value; }, [this](expected::Error error) { - log_->error(kTopBlockRetrieveFail + std::string{": "} + error.error); + log_->error(kTopBlockRetrieveFail + std::string{": "} + + error.error); }); if (not top_block) { subscriber.on_completed(); @@ -79,9 +92,9 @@ rxcpp::observable> BlockLoaderImpl::retrieveBlocks( auto reader = this->getPeerStub(**peer).retrieveBlocks(&context, request); while (reader->Read(&block)) { - shared_model::proto::TransportBuilder< - shared_model::proto::Block, - shared_model::validation::DefaultSignableBlockValidator>() + shared_model::proto::TransportBuilder( + Validator(TimerWrapper(block.payload().created_time()))) .build(block) .match( // success case @@ -127,7 +140,8 @@ boost::optional> BlockLoaderImpl::retrieveBlock( // stateless validation of block auto result = std::make_shared(std::move(block)); - auto answer = stateless_validator_->validate(*result); + auto answer = BlockValidatorInternal(TimerWrapper(result->createdTime())) + .validate(*result); if (answer.hasErrors()) { log_->error(answer.reason()); return boost::none; diff --git a/irohad/network/impl/block_loader_impl.hpp b/irohad/network/impl/block_loader_impl.hpp index b78c75995f..ba11ba9f65 100644 --- a/irohad/network/impl/block_loader_impl.hpp +++ b/irohad/network/impl/block_loader_impl.hpp @@ -32,12 +32,8 @@ namespace iroha { namespace network { class BlockLoaderImpl : public BlockLoader { public: - BlockLoaderImpl( - std::shared_ptr peer_query, - std::shared_ptr block_query, - std::shared_ptr = - std::make_shared< - shared_model::validation::DefaultBlockValidator>()); + BlockLoaderImpl(std::shared_ptr peer_query, + std::shared_ptr block_query); rxcpp::observable> retrieveBlocks( @@ -70,8 +66,6 @@ namespace iroha { peer_connections_; std::shared_ptr peer_query_; std::shared_ptr block_query_; - std::shared_ptr - stateless_validator_; logger::Logger log_; }; diff --git a/shared_model/validators/any_block_validator.hpp b/shared_model/validators/any_block_validator.hpp index ebb8a94d51..2aeeb129d2 100644 --- a/shared_model/validators/any_block_validator.hpp +++ b/shared_model/validators/any_block_validator.hpp @@ -36,7 +36,7 @@ namespace shared_model { block_variant, [this](auto block) { return validate(*block); }); } - private: + protected: BlockValidator non_empty_block_validator_; EmptyBlockValidator empty_block_validator_; }; diff --git a/shared_model/validators/block_validator.hpp b/shared_model/validators/block_validator.hpp index 975b762b02..e0d6e6b50d 100644 --- a/shared_model/validators/block_validator.hpp +++ b/shared_model/validators/block_validator.hpp @@ -42,6 +42,11 @@ namespace shared_model { TransactionValidator, TransactionsCollectionValidator> { public: + using ContainerValidator< + interface::Block, + FieldValidator, + TransactionValidator, + TransactionsCollectionValidator>::ContainerValidator; /** * Applies validation on block * @param block diff --git a/shared_model/validators/blocks_query_validator.hpp b/shared_model/validators/blocks_query_validator.hpp index 9d84df9299..fce54e096f 100644 --- a/shared_model/validators/blocks_query_validator.hpp +++ b/shared_model/validators/blocks_query_validator.hpp @@ -18,7 +18,8 @@ namespace shared_model { template class BlocksQueryValidator { public: - BlocksQueryValidator(const FieldValidator &field_validator = FieldValidator()) + BlocksQueryValidator( + const FieldValidator &field_validator = FieldValidator()) : field_validator_(field_validator) {} /** @@ -42,7 +43,7 @@ namespace shared_model { return answer; } - private: + protected: Answer answer_; FieldValidator field_validator_; }; diff --git a/shared_model/validators/container_validator.hpp b/shared_model/validators/container_validator.hpp index 79a7b5ecde..74edbf5a97 100644 --- a/shared_model/validators/container_validator.hpp +++ b/shared_model/validators/container_validator.hpp @@ -59,12 +59,12 @@ namespace shared_model { public: explicit ContainerValidator( + const FieldValidator &field_validator = FieldValidator(), const TransactionsCollectionValidator &transactions_collection_validator = TransactionsCollectionValidator(), const TransactionValidator &transaction_validator = - TransactionValidator(), - const FieldValidator &field_validator = FieldValidator()) + TransactionValidator()) : transactions_collection_validator_( transactions_collection_validator), transaction_validator_(transaction_validator), diff --git a/shared_model/validators/empty_block_validator.hpp b/shared_model/validators/empty_block_validator.hpp index a3e18b3342..64267892e2 100644 --- a/shared_model/validators/empty_block_validator.hpp +++ b/shared_model/validators/empty_block_validator.hpp @@ -34,7 +34,7 @@ namespace shared_model { return answer; } - private: + protected: FieldValidator field_validator_; }; diff --git a/shared_model/validators/field_validator.cpp b/shared_model/validators/field_validator.cpp index d00dfd9818..4004fd9c2b 100644 --- a/shared_model/validators/field_validator.cpp +++ b/shared_model/validators/field_validator.cpp @@ -70,8 +70,9 @@ namespace shared_model { const std::regex FieldValidator::detail_key_regex_(detail_key_pattern_); const std::regex FieldValidator::role_id_regex_(role_id_pattern_); - FieldValidator::FieldValidator(time_t future_gap) - : future_gap_(future_gap) {} + FieldValidator::FieldValidator(time_t future_gap, + TimeFunction time_provider) + : future_gap_(future_gap), time_provider_(time_provider) {} void FieldValidator::validateAccountId( ReasonsGroupType &reason, @@ -275,7 +276,7 @@ namespace shared_model { void FieldValidator::validateCreatedTime( ReasonsGroupType &reason, const interface::types::TimestampType ×tamp) const { - iroha::ts64_t now = iroha::time::now(); + iroha::ts64_t now = time_provider_(); if (now + future_gap_ < timestamp) { auto message = (boost::format("bad timestamp: sent from future, " @@ -285,7 +286,7 @@ namespace shared_model { reason.second.push_back(std::move(message)); } - if (now > max_delay + timestamp) { + if (now > kMaxDelay + timestamp) { auto message = (boost::format("bad timestamp: too old, timestamp: %llu, now: %llu") % timestamp % now) diff --git a/shared_model/validators/field_validator.hpp b/shared_model/validators/field_validator.hpp index 707aa9914e..2b95d1a156 100644 --- a/shared_model/validators/field_validator.hpp +++ b/shared_model/validators/field_validator.hpp @@ -36,8 +36,14 @@ namespace shared_model { * and query */ class FieldValidator { + private: + using TimeFunction = std::function; + public: - FieldValidator(time_t future_gap = default_future_gap); + FieldValidator(time_t future_gap = kDefaultFutureGap, + TimeFunction time_provider = [] { + return iroha::time::now(); + }); void validateAccountId( ReasonsGroupType &reason, @@ -176,11 +182,15 @@ namespace shared_model { // gap for future transactions time_t future_gap_; + // time provider callback + TimeFunction time_provider_; + + public: // max-delay between tx creation and validation - static constexpr auto max_delay = + static constexpr auto kMaxDelay = std::chrono::hours(24) / std::chrono::milliseconds(1); // default value for future_gap field of FieldValidator - static constexpr auto default_future_gap = + static constexpr auto kDefaultFutureGap = std::chrono::minutes(5) / std::chrono::milliseconds(1); // size of key diff --git a/shared_model/validators/proposal_validator.hpp b/shared_model/validators/proposal_validator.hpp index 83b642d2ba..e0c50ceb92 100644 --- a/shared_model/validators/proposal_validator.hpp +++ b/shared_model/validators/proposal_validator.hpp @@ -42,6 +42,11 @@ namespace shared_model { TransactionValidator, TransactionsCollectionValidator> { public: + using ContainerValidator< + interface::Proposal, + FieldValidator, + TransactionValidator, + TransactionsCollectionValidator>::ContainerValidator; /** * Applies validation on proposal * @param proposal diff --git a/shared_model/validators/query_validator.hpp b/shared_model/validators/query_validator.hpp index c306cef604..3e36016063 100644 --- a/shared_model/validators/query_validator.hpp +++ b/shared_model/validators/query_validator.hpp @@ -193,7 +193,7 @@ namespace shared_model { return answer; } - private: + protected: Answer answer_; FieldValidator field_validator_; QueryFieldValidator query_field_validator_; diff --git a/shared_model/validators/signable_validator.hpp b/shared_model/validators/signable_validator.hpp index 034e07d437..313251eb1c 100644 --- a/shared_model/validators/signable_validator.hpp +++ b/shared_model/validators/signable_validator.hpp @@ -26,13 +26,16 @@ namespace shared_model { template class SignableModelValidator : public ModelValidator { - // using ModelValidator::ModelValidator; public: + explicit SignableModelValidator( + FieldValidator &&validator = FieldValidator()) + : ModelValidator(std::move(validator)) {} + Answer validate(const Model &model) const { auto answer = ModelValidator::validate(model); std::string reason_name = "Signature"; ReasonsGroupType reason(reason_name, GroupedReasons()); - FieldValidator().validateSignatures( + ModelValidator::field_validator_.validateSignatures( reason, model.signatures(), model.payload()); if (not reason.second.empty()) { answer.addReason(std::move(reason)); diff --git a/shared_model/validators/transaction_validator.hpp b/shared_model/validators/transaction_validator.hpp index a73e490b87..83881832a4 100644 --- a/shared_model/validators/transaction_validator.hpp +++ b/shared_model/validators/transaction_validator.hpp @@ -294,7 +294,7 @@ namespace shared_model { return answer; } - private: + protected: FieldValidator field_validator_; CommandValidator command_validator_; }; diff --git a/test/module/irohad/network/block_loader_test.cpp b/test/module/irohad/network/block_loader_test.cpp index 6f8e9bfed1..a8f99cfe56 100644 --- a/test/module/irohad/network/block_loader_test.cpp +++ b/test/module/irohad/network/block_loader_test.cpp @@ -20,17 +20,14 @@ #include #include -#include "backend/protobuf/block.hpp" -#include "backend/protobuf/common_objects/peer.hpp" #include "builders/common_objects/peer_builder.hpp" -#include "builders/protobuf/block.hpp" -#include "builders/protobuf/builder_templates/block_template.hpp" #include "builders/protobuf/common_objects/proto_peer_builder.hpp" #include "cryptography/crypto_provider/crypto_defaults.hpp" #include "cryptography/hash.hpp" #include "datetime/time.hpp" #include "framework/test_subscriber.hpp" #include "module/irohad/ametsuchi/ametsuchi_mocks.hpp" +#include "module/shared_model/builders/protobuf/test_block_builder.hpp" #include "network/impl/block_loader_impl.hpp" #include "network/impl/block_loader_service.hpp" #include "validators/default_validator.hpp" @@ -51,10 +48,7 @@ class BlockLoaderTest : public testing::Test { void SetUp() override { peer_query = std::make_shared(); storage = std::make_shared(); - loader = std::make_shared( - peer_query, - storage, - std::make_shared()); + loader = std::make_shared(peer_query, storage); service = std::make_shared(storage); grpc::ServerBuilder builder; @@ -82,18 +76,13 @@ class BlockLoaderTest : public testing::Test { } auto getBaseBlockBuilder() const { - constexpr auto kTotal = (1 << 4) - 1; - return shared_model::proto::TemplateBlockBuilder< - kTotal, - shared_model::validation::DefaultBlockValidator, - shared_model::proto::Block>() - .height(1) - .prevHash(Hash(std::string( - shared_model::crypto::DefaultCryptoAlgorithmType::kHashLength, - '0'))) - .createdTime(iroha::time::now()); + return TestBlockBuilder().height(1).prevHash(kPrevHash).createdTime( + iroha::time::now()); } + const Hash kPrevHash = + Hash(std::string(DefaultCryptoAlgorithmType::kHashLength, '0')); + std::shared_ptr peer; PublicKey peer_key = DefaultCryptoAlgorithmType::generateKeypair().publicKey(); @@ -133,9 +122,15 @@ TEST_F(BlockLoaderTest, ValidWhenSameTopBlock) { */ TEST_F(BlockLoaderTest, ValidWhenOneBlock) { // Current block height 1 => Other block height 2 => one block received - auto block = getBaseBlockBuilder().build(); - auto top_block = getBaseBlockBuilder().height(block.height() + 1).build(); + // time validation should work based on the block field + // so it should pass statless BlockLoader validation + auto block = getBaseBlockBuilder().createdTime(228).build(); + + auto top_block = getBaseBlockBuilder() + .createdTime(block.createdTime() + 1) + .height(block.height() + 1) + .build(); EXPECT_CALL(*peer_query, getLedgerPeers()) .WillOnce(Return(std::vector{peer})); @@ -159,7 +154,10 @@ TEST_F(BlockLoaderTest, ValidWhenOneBlock) { */ TEST_F(BlockLoaderTest, ValidWhenMultipleBlocks) { // Current block height 1 => Other block height n => n-1 blocks received - auto block = getBaseBlockBuilder().build(); + + // time validation should work based on the block field + // so it should pass statless BlockLoader validation + auto block = getBaseBlockBuilder().createdTime(1337).build(); auto num_blocks = 2; auto next_height = block.height() + 1; @@ -219,7 +217,7 @@ TEST_F(BlockLoaderTest, ValidWhenBlockMissing) { EXPECT_CALL(*storage, getBlocksFrom(1)) .WillOnce(Return(rxcpp::observable<>::just(present).map( [](auto &&x) { return wBlock(clone(x)); }))); - auto block = loader->retrieveBlock(peer_key, Hash(std::string(32, '0'))); + auto block = loader->retrieveBlock(peer_key, kPrevHash); ASSERT_FALSE(block); } From a42f274ecab8ac7cdf1ce8c626a153803613d7fa Mon Sep 17 00:00:00 2001 From: Igor Egorov Date: Mon, 9 Jul 2018 10:34:16 +0300 Subject: [PATCH 32/97] Add GetPendingTransactions query to shared model (#1532) This PR adds an interface of GetPendingTransactions query to shared model. Currently, the query does NOT perform actual information retrieving (will be implemented after appearing of transactions batches support in develop branch). Additionally, this pr provides documentation for the new query. It is a starting point for further development of GetPendingTransactions feature. Signed-off-by: Igor Egorov --- docs/source/api/queries.rst | 40 ++++++++++ docs/source/core_concepts/glossary.rst | 78 +++++++++++++++---- irohad/execution/impl/query_execution.cpp | 32 ++++---- irohad/execution/query_execution.hpp | 27 +++---- schema/queries.proto | 5 ++ shared_model/backend/protobuf/CMakeLists.txt | 6 ++ .../impl/proto_get_pending_transactions.cpp | 31 ++++++++ .../proto_get_pending_transactions.hpp | 30 +++++++ .../backend/protobuf/queries/proto_query.hpp | 20 ++--- shared_model/bindings/model_query_builder.cpp | 20 ++--- shared_model/bindings/model_query_builder.hpp | 23 +++--- .../builder_templates/query_template.hpp | 22 ++---- shared_model/interfaces/CMakeLists.txt | 14 +--- .../queries/get_pending_transactions.hpp | 30 +++++++ .../queries/impl/get_pending_transactions.cpp | 22 ++++++ shared_model/interfaces/queries/query.hpp | 20 ++--- shared_model/validators/query_validator.hpp | 24 +++--- 17 files changed, 302 insertions(+), 142 deletions(-) create mode 100644 shared_model/backend/protobuf/queries/impl/proto_get_pending_transactions.cpp create mode 100644 shared_model/backend/protobuf/queries/proto_get_pending_transactions.hpp create mode 100644 shared_model/interfaces/queries/get_pending_transactions.hpp create mode 100644 shared_model/interfaces/queries/impl/get_pending_transactions.cpp diff --git a/docs/source/api/queries.rst b/docs/source/api/queries.rst index e68f145aba..a5b6521160 100644 --- a/docs/source/api/queries.rst +++ b/docs/source/api/queries.rst @@ -159,6 +159,46 @@ Response Structure "Transactions", "an array of transactions", "Committed transactions", "{tx1, tx2…}" +Get Pending Transactions +^^^^^^^^^^^^^^^^^^^^^^^^ + +Purpose +------- + +.. TODO, igor-egorov, 2018-07-03, IR-1356, add a link to MST description here + +GetPendingTransactions is used for retrieving a list of pending (not fully signed) multisignature transactions +or batches of transactions issued by account of query creator. + +Request Schema +-------------- + +.. code-block:: proto + + message GetPendingTransactions { + } + +Response Schema +--------------- + +.. code-block:: proto + + message TransactionsResponse { + repeated Transaction transactions = 1; + } + +Response Structure +------------------ + +The response contains a list of `pending transactions <../core_concepts/glossary.html#pending-transactions>`_. + +.. csv-table:: + :header: "Field", "Description", "Constraint", "Example" + :widths: 15, 30, 20, 15 + + "Transactions", "an array of pending transactions", "Pending transactions", "{tx1, tx2…}" + + Get Account Transactions ^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/docs/source/core_concepts/glossary.rst b/docs/source/core_concepts/glossary.rst index d90e888914..ded2f463b2 100644 --- a/docs/source/core_concepts/glossary.rst +++ b/docs/source/core_concepts/glossary.rst @@ -167,16 +167,6 @@ Verified Proposal A set of transactions that have been passed `stateless <#stateless-validation>`__ and `stateful <#stateful-validation>`__ validation, but were not committed yet. -Role -==== - -A named abstraction that holds a set of `permissions <#permission>`__. - -Simulator -========= - -See `Verified Proposal Creator <#verified-proposal-creator>`__. - Query ===== @@ -192,6 +182,16 @@ of signatures required to consider a transaction signed. The default value is 1. Each account can link additional public keys and increase own quorum number. +Role +==== + +A named abstraction that holds a set of `permissions <#permission>`__. + +Simulator +========= + +See `Verified Proposal Creator <#verified-proposal-creator>`__. + Synchronizer ============ @@ -215,7 +215,7 @@ An ordered set of `commands <#command>`__, which is applied to the ledger atomic Any nonvalid command within a transaction leads to rejection of the whole transaction during the validation process. -Transaction structure +Transaction Structure --------------------- **Payload** stores all transaction fields, except signatures: @@ -223,12 +223,20 @@ Transaction structure - Time of creation (unix time, in milliseconds) - Account ID of transaction creator (username@domain) - Quorum field (indicates required number of signatures) - - Repeated commands which are described in details in commands section <../api/commands.html>`__ + - Repeated commands which are described in details in `commands section <../api/commands.html>`__ + - Batch meta information (optional part). See `Batch of Transactions`_ for details **Signatures** contain one or many signatures (ed25519 public key + signature) -Transaction statuses +Reduced Transaction Hash +^^^^^^^^^^^^^^^^^^^^^^^^ + +Reduced hash is calculated over transaction payload excluding batch meta information. +Used in `Batch of Transactions`_. + + +Transaction Statuses -------------------- Hyperledger Iroha supports both push and pull interaction mode with a client. @@ -239,7 +247,7 @@ In any of these modes, the set of transaction statuses is the same: .. image:: ./../../image_assets/tx_status.png -Transaction status set +Transaction Status Set ^^^^^^^^^^^^^^^^^^^^^^ - NOT_RECEIVED: requested peer does not have this transaction. @@ -250,6 +258,48 @@ Transaction status set - STATEFUL_VALIDATION_SUCCESS: the transaction has successfully passed stateful validation. - COMMITTED: the transaction is the part of a block, which gained enough votes and is in the block store at the moment. +Pending Transactions +^^^^^^^^^^^^^^^^^^^^ + +.. TODO igor-egorov, 2018-07-04, IR-1356, add here a link to MST docs + +Any transaction that has lesser signatures at the moment than `quorum`_ of transaction creator account is considered as pending. +Pending transaction will be submitted for stateful validation as soon as multi signature mechanism (*to be documented*) +will collect required amount of signatures for quorum. + +Transaction that already has quorum of signatures can also be considered as pending in cases +when the transaction is a part of `batch of transactions`_ and there is a not fully signed transaction. + +Batch of Transactions +===================== + +*The feature is to be released.* + +Transactions batch is a feature that allows sending several transactions to Iroha at once preserving their order. + +Each transaction within a batch includes batch meta information. +Batch meta contains batch type identifier (atomic or ordered) and a list of `reduced hashes <#reduced-transaction-hash>`_ +of all transactions within a batch. +The order of hashes prescribes transactions sequence. + +.. TODO igor-egorov, 2018-07-04, IR-1356, add here a link to MST docs + +Batch can contain transactions created by different accounts. +Any transaction within a batch can require single or multiple signatures (depends on quorum set for an account of transaction creator). +At least one transaction inside a batch should have at least one signature to let the batch pass `stateful validation`_. + +Atomic Batch +------------ + +All the transactions within an atomic batch should pass `stateful validation`_ for the batch to be applied to a ledger. + +Ordered Batch +------------- + +Ordered batch preserves only the sequence of transactions applying to a ledger. +All the transactions that able to pass stateful validation within a batch will be applied to a ledger. +Validation failure of one transaction would NOT directly imply the failure of the whole batch. + Validator ========= diff --git a/irohad/execution/impl/query_execution.cpp b/irohad/execution/impl/query_execution.cpp index a807ec7cde..95dc0f95fd 100644 --- a/irohad/execution/impl/query_execution.cpp +++ b/irohad/execution/impl/query_execution.cpp @@ -1,18 +1,6 @@ /** - * Copyright Soramitsu Co., Ltd. 2017 All Rights Reserved. - * http://soramitsu.co.jp - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 */ #include "execution/query_execution.hpp" @@ -332,6 +320,17 @@ QueryProcessingFactory::executeGetSignatories( auto response = QueryResponseBuilder().signatoriesResponse(*signs); return response; } + +QueryProcessingFactory::QueryResponseBuilderDone +QueryProcessingFactory::executeGetPendingTransactions( + const shared_model::interface::GetPendingTransactions &query, + const shared_model::interface::types::AccountIdType &query_creator) { + std::vector txs; + // TODO 2018-07-04, igor-egorov, IR-1486, the core logic is to be implemented + auto response = QueryResponseBuilder().transactionsResponse(txs); + return response; +} + std::shared_ptr QueryProcessingFactory::validateAndExecute( const shared_model::interface::Query &query) { @@ -419,6 +418,11 @@ QueryProcessingFactory::validateAndExecute( builder = executeGetAssetInfo(q); } return clone(builder.queryHash(query_hash).build()); + }, + [&](const shared_model::interface::GetPendingTransactions &q) { + // the query does not require validation + builder = executeGetPendingTransactions(q, query.creatorAccountId()); + return clone(builder.queryHash(query_hash).build()); } ); diff --git a/irohad/execution/query_execution.hpp b/irohad/execution/query_execution.hpp index 60366bb4d4..8da803ef92 100644 --- a/irohad/execution/query_execution.hpp +++ b/irohad/execution/query_execution.hpp @@ -1,27 +1,16 @@ /** - * Copyright Soramitsu Co., Ltd. 2018 All Rights Reserved. - * http://soramitsu.co.jp - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 */ + #ifndef IROHA_QUERY_EXECUTION_HPP #define IROHA_QUERY_EXECUTION_HPP #include "ametsuchi/block_query.hpp" #include "ametsuchi/wsv_query.hpp" -#include "builders/protobuf/builder_templates/query_response_template.hpp" #include "builders/protobuf/builder_templates/blocks_query_template.hpp" -#include "interfaces/query_responses/block_query_response.hpp" +#include "builders/protobuf/builder_templates/query_response_template.hpp" +#include "interfaces/common_objects/types.hpp" namespace shared_model { namespace interface { @@ -60,8 +49,8 @@ namespace iroha { QueryProcessingFactory(std::shared_ptr wsvQuery, std::shared_ptr blockQuery); bool validate(const shared_model::interface::BlocksQuery &query); - private: + private: bool validate(const shared_model::interface::Query &query, const shared_model::interface::GetAssetInfo &get_asset_info); @@ -130,6 +119,10 @@ namespace iroha { const shared_model::interface::GetTransactions &query, const shared_model::interface::types::AccountIdType &accountId); + QueryResponseBuilderDone executeGetPendingTransactions( + const shared_model::interface::GetPendingTransactions &query, + const shared_model::interface::types::AccountIdType &query_creator); + std::shared_ptr _wsvQuery; std::shared_ptr _blockQuery; }; diff --git a/schema/queries.proto b/schema/queries.proto index 3615bdc996..437816d95b 100644 --- a/schema/queries.proto +++ b/schema/queries.proto @@ -44,6 +44,10 @@ message GetRolePermissions{ string role_id = 1; } +message GetPendingTransactions { + +} + message QueryPayloadMeta { uint64 created_time = 1; string creator_account_id = 2; @@ -66,6 +70,7 @@ message Query { GetRoles get_roles = 10; GetRolePermissions get_role_permissions = 11; GetAssetInfo get_asset_info = 12; + GetPendingTransactions get_pending_transactions = 13; } } diff --git a/shared_model/backend/protobuf/CMakeLists.txt b/shared_model/backend/protobuf/CMakeLists.txt index 27c1c40f2d..57fd1ede4e 100644 --- a/shared_model/backend/protobuf/CMakeLists.txt +++ b/shared_model/backend/protobuf/CMakeLists.txt @@ -1,3 +1,8 @@ +# +# Copyright Soramitsu Co., Ltd. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +# + add_library(shared_model_proto_backend impl/permissions.cpp commands/impl/proto_add_asset_quantity.cpp @@ -28,6 +33,7 @@ add_library(shared_model_proto_backend queries/impl/proto_get_roles.cpp queries/impl/proto_get_signatories.cpp queries/impl/proto_get_transactions.cpp + queries/impl/proto_get_pending_transactions.cpp queries/impl/proto_blocks_query.cpp queries/impl/proto_query_payload_meta.cpp ) diff --git a/shared_model/backend/protobuf/queries/impl/proto_get_pending_transactions.cpp b/shared_model/backend/protobuf/queries/impl/proto_get_pending_transactions.cpp new file mode 100644 index 0000000000..9b90b1b033 --- /dev/null +++ b/shared_model/backend/protobuf/queries/impl/proto_get_pending_transactions.cpp @@ -0,0 +1,31 @@ +/** + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "backend/protobuf/queries/proto_get_pending_transactions.hpp" + +namespace shared_model { + namespace proto { + + template + GetPendingTransactions::GetPendingTransactions(QueryType &&query) + : CopyableProto(std::forward(query)) {} + + template GetPendingTransactions::GetPendingTransactions( + GetPendingTransactions::TransportType &); + template GetPendingTransactions::GetPendingTransactions( + const GetPendingTransactions::TransportType &); + template GetPendingTransactions::GetPendingTransactions( + GetPendingTransactions::TransportType &&); + + GetPendingTransactions::GetPendingTransactions( + const GetPendingTransactions &o) + : GetPendingTransactions(o.proto_) {} + + GetPendingTransactions::GetPendingTransactions( + GetPendingTransactions &&o) noexcept + : GetPendingTransactions(std::move(o.proto_)) {} + + } // namespace proto +} // namespace shared_model diff --git a/shared_model/backend/protobuf/queries/proto_get_pending_transactions.hpp b/shared_model/backend/protobuf/queries/proto_get_pending_transactions.hpp new file mode 100644 index 0000000000..97ba387b44 --- /dev/null +++ b/shared_model/backend/protobuf/queries/proto_get_pending_transactions.hpp @@ -0,0 +1,30 @@ +/** + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef IROHA_PROTO_GET_PENDING_TRANSACTIONS_HPP +#define IROHA_PROTO_GET_PENDING_TRANSACTIONS_HPP + +#include "backend/protobuf/common_objects/trivial_proto.hpp" +#include "interfaces/queries/get_pending_transactions.hpp" +#include "queries.pb.h" + +namespace shared_model { + namespace proto { + class GetPendingTransactions final + : public CopyableProto { + public: + template + explicit GetPendingTransactions(QueryType &&query); + + GetPendingTransactions(const GetPendingTransactions &o); + + GetPendingTransactions(GetPendingTransactions &&o) noexcept; + }; + } // namespace proto +} // namespace shared_model + +#endif // IROHA_PROTO_GET_PENDING_TRANSACTIONS_HPP diff --git a/shared_model/backend/protobuf/queries/proto_query.hpp b/shared_model/backend/protobuf/queries/proto_query.hpp index 0bd4768cd9..c984950293 100644 --- a/shared_model/backend/protobuf/queries/proto_query.hpp +++ b/shared_model/backend/protobuf/queries/proto_query.hpp @@ -1,18 +1,6 @@ /** - * Copyright Soramitsu Co., Ltd. 2017 All Rights Reserved. - * http://soramitsu.co.jp - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 */ #ifndef IROHA_SHARED_MODEL_PROTO_QUERY_HPP @@ -32,6 +20,7 @@ #include "backend/protobuf/queries/proto_get_roles.hpp" #include "backend/protobuf/queries/proto_get_signatories.hpp" #include "backend/protobuf/queries/proto_get_transactions.hpp" +#include "backend/protobuf/queries/proto_get_pending_transactions.hpp" #include "queries.pb.h" #include "utils/lazy_initializer.hpp" @@ -51,7 +40,8 @@ namespace shared_model { GetAccountDetail, GetRoles, GetRolePermissions, - GetAssetInfo>; + GetAssetInfo, + GetPendingTransactions>; /// list of types in proto variant using ProtoQueryListType = ProtoQueryVariantType::types; diff --git a/shared_model/bindings/model_query_builder.cpp b/shared_model/bindings/model_query_builder.cpp index 9ee0acfb3e..7cba8db1f3 100644 --- a/shared_model/bindings/model_query_builder.cpp +++ b/shared_model/bindings/model_query_builder.cpp @@ -1,18 +1,6 @@ /** - * Copyright Soramitsu Co., Ltd. 2017 All Rights Reserved. - * http://soramitsu.co.jp - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 */ #include "bindings/model_query_builder.hpp" @@ -89,6 +77,10 @@ namespace shared_model { return ModelQueryBuilder(builder_.getAccountDetail(account_id)); } + ModelQueryBuilder ModelQueryBuilder::getPendingTransactions() { + return ModelQueryBuilder(builder_.getPendingTransactions()); + } + proto::UnsignedWrapper ModelQueryBuilder::build() { return builder_.build(); } diff --git a/shared_model/bindings/model_query_builder.hpp b/shared_model/bindings/model_query_builder.hpp index 1fcd63a160..fb6a2a0112 100644 --- a/shared_model/bindings/model_query_builder.hpp +++ b/shared_model/bindings/model_query_builder.hpp @@ -1,18 +1,6 @@ /** - * Copyright Soramitsu Co., Ltd. 2017 All Rights Reserved. - * http://soramitsu.co.jp - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 */ #ifndef IROHA_SHARED_MODEL_MODEL_QUERY_BUILDER_HPP @@ -141,6 +129,13 @@ namespace shared_model { ModelQueryBuilder getAccountDetail( const interface::types::AccountIdType &account_id); + /** + * Retrieves all pending (not fully signed) multisignature transactions or + * batches of transactions. + * @return builder with getPendingTransactions query inside + */ + ModelQueryBuilder getPendingTransactions(); + /** * Builds result with all appended fields * @return wrapper on unsigned query diff --git a/shared_model/builders/protobuf/builder_templates/query_template.hpp b/shared_model/builders/protobuf/builder_templates/query_template.hpp index 13d0d6e3cb..33146715ec 100644 --- a/shared_model/builders/protobuf/builder_templates/query_template.hpp +++ b/shared_model/builders/protobuf/builder_templates/query_template.hpp @@ -1,18 +1,6 @@ /** - * Copyright Soramitsu Co., Ltd. 2018 All Rights Reserved. - * http://soramitsu.co.jp - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 */ #ifndef IROHA_PROTO_QUERY_BUILDER_TEMPLATE_HPP @@ -202,6 +190,12 @@ namespace shared_model { return getTransactions({hashes...}); } + auto getPendingTransactions() const { + return queryField([&](auto proto_query) { + proto_query->mutable_get_pending_transactions(); + }); + } + auto build() const { static_assert(S == (1 << TOTAL) - 1, "Required fields are not set"); if (not query_.has_payload()) { diff --git a/shared_model/interfaces/CMakeLists.txt b/shared_model/interfaces/CMakeLists.txt index 7fc19a1872..4e435dd301 100644 --- a/shared_model/interfaces/CMakeLists.txt +++ b/shared_model/interfaces/CMakeLists.txt @@ -1,16 +1,7 @@ -# Copyright 2017 Soramitsu Co., Ltd. # -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at +# Copyright Soramitsu Co., Ltd. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 # -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. add_library(shared_model_interfaces impl/permissions.cpp @@ -42,6 +33,7 @@ add_library(shared_model_interfaces queries/impl/get_roles.cpp queries/impl/get_signatories.cpp queries/impl/get_transactions.cpp + queries/impl/get_pending_transactions.cpp queries/impl/blocks_query.cpp queries/impl/query_payload_meta.cpp ) diff --git a/shared_model/interfaces/queries/get_pending_transactions.hpp b/shared_model/interfaces/queries/get_pending_transactions.hpp new file mode 100644 index 0000000000..291684a018 --- /dev/null +++ b/shared_model/interfaces/queries/get_pending_transactions.hpp @@ -0,0 +1,30 @@ +/** + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef IROHA_SHARED_MODEL_GET_PENDING_TRANSACTIONS_HPP +#define IROHA_SHARED_MODEL_GET_PENDING_TRANSACTIONS_HPP + +#include "interfaces/base/model_primitive.hpp" +#include "interfaces/common_objects/types.hpp" + +namespace shared_model { + namespace interface { + + /** + * Get all pending (not fully signed) multisignature transactions or batches + * of transactions. + */ + class GetPendingTransactions + : public ModelPrimitive { + public: + std::string toString() const override; + + bool operator==(const ModelType &rhs) const override; + }; + + } // namespace interface +} // namespace shared_model + +#endif // IROHA_SHARED_MODEL_GET_PENDING_TRANSACTIONS_HPP diff --git a/shared_model/interfaces/queries/impl/get_pending_transactions.cpp b/shared_model/interfaces/queries/impl/get_pending_transactions.cpp new file mode 100644 index 0000000000..7c0007ea8e --- /dev/null +++ b/shared_model/interfaces/queries/impl/get_pending_transactions.cpp @@ -0,0 +1,22 @@ +/** + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "interfaces/queries/get_pending_transactions.hpp" + +namespace shared_model { + namespace interface { + + std::string GetPendingTransactions::toString() const { + return detail::PrettyStringBuilder() + .init("GetPendingTransactions") + .finalize(); + } + + bool GetPendingTransactions::operator==(const ModelType &rhs) const { + return true; + } + + } // namespace interface +} // namespace shared_model diff --git a/shared_model/interfaces/queries/query.hpp b/shared_model/interfaces/queries/query.hpp index ca3cedb638..80d1b0565f 100644 --- a/shared_model/interfaces/queries/query.hpp +++ b/shared_model/interfaces/queries/query.hpp @@ -1,18 +1,6 @@ /** - * Copyright Soramitsu Co., Ltd. 2017 All Rights Reserved. - * http://soramitsu.co.jp - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 */ #ifndef IROHA_SHARED_MODEL_QUERY_HPP @@ -32,6 +20,7 @@ #include "interfaces/queries/get_roles.hpp" #include "interfaces/queries/get_signatories.hpp" #include "interfaces/queries/get_transactions.hpp" +#include "interfaces/queries/get_pending_transactions.hpp" #include "interfaces/queries/query_payload_meta.hpp" namespace shared_model { @@ -59,7 +48,8 @@ namespace shared_model { GetAccountDetail, GetRoles, GetRolePermissions, - GetAssetInfo>; + GetAssetInfo, + GetPendingTransactions>; /// Types of concrete commands, in attached variant using QueryListType = QueryVariantType::types; diff --git a/shared_model/validators/query_validator.hpp b/shared_model/validators/query_validator.hpp index 3e36016063..3a9ed8ae98 100644 --- a/shared_model/validators/query_validator.hpp +++ b/shared_model/validators/query_validator.hpp @@ -1,18 +1,6 @@ /** - * Copyright Soramitsu Co., Ltd. 2017 All Rights Reserved. - * http://soramitsu.co.jp - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 */ #ifndef IROHA_SHARED_MODEL_QUERY_VALIDATOR_HPP @@ -137,6 +125,14 @@ namespace shared_model { return reason; } + ReasonsGroupType operator()( + const interface::GetPendingTransactions &qry) const { + ReasonsGroupType reason; + reason.first = "GetPendingTransactions"; + + return reason; + } + private: FieldValidator validator_; }; From 4e0a5af046ed2fce0219a941a7805e7b3887633c Mon Sep 17 00:00:00 2001 From: Kitsu Date: Tue, 10 Jul 2018 22:01:52 +0300 Subject: [PATCH 33/97] Check the signatures for their size (#1536) Signed-off-by: Kitsu --- shared_model/validators/field_validator.cpp | 6 ++- .../acceptance/tx_acceptance_test.cpp | 17 +++++++ .../irohad/network/block_loader_test.cpp | 47 ++++++++++++++----- 3 files changed, 55 insertions(+), 15 deletions(-) diff --git a/shared_model/validators/field_validator.cpp b/shared_model/validators/field_validator.cpp index 4004fd9c2b..7a09ee72e7 100644 --- a/shared_model/validators/field_validator.cpp +++ b/shared_model/validators/field_validator.cpp @@ -310,6 +310,9 @@ namespace shared_model { ReasonsGroupType &reason, const interface::types::SignatureRangeType &signatures, const crypto::Blob &source) const { + if (boost::empty(signatures)) { + reason.second.push_back("Signatures cannot be empty"); + } for (const auto &signature : signatures) { const auto &sign = signature.signedData(); const auto &pkey = signature.publicKey(); @@ -354,8 +357,7 @@ namespace shared_model { } void FieldValidator::validateBatchMeta( shared_model::validation::ReasonsGroupType &reason, - const interface::BatchMeta &batch_meta) - const {} + const interface::BatchMeta &batch_meta) const {} void FieldValidator::validateHeight( shared_model::validation::ReasonsGroupType &reason, diff --git a/test/integration/acceptance/tx_acceptance_test.cpp b/test/integration/acceptance/tx_acceptance_test.cpp index e2d59758bf..32f7b179c7 100644 --- a/test/integration/acceptance/tx_acceptance_test.cpp +++ b/test/integration/acceptance/tx_acceptance_test.cpp @@ -233,3 +233,20 @@ TEST_F(AcceptanceTest, TransactionValidSignedBlob) { .checkBlock(checkStatefulValid) .done(); } + +/** + * @given some user + * @when sending transaction without any signature + * @then the response is STATELESS_VALIDATION_FAILED + */ +TEST_F(AcceptanceTest, EmptySignatures) { + std::string kAccountId = "some@account"; + auto proto_tx = baseTx().build().getTransport(); + proto_tx.clear_signatures(); + auto tx = shared_model::proto::Transaction(proto_tx); + + integration_framework::IntegrationTestFramework(1) + .setInitialState(kAdminKeypair) + .sendTx(tx, checkStatelessInvalid) + .done(); +} diff --git a/test/module/irohad/network/block_loader_test.cpp b/test/module/irohad/network/block_loader_test.cpp index a8f99cfe56..36f8bacefd 100644 --- a/test/module/irohad/network/block_loader_test.cpp +++ b/test/module/irohad/network/block_loader_test.cpp @@ -76,8 +76,14 @@ class BlockLoaderTest : public testing::Test { } auto getBaseBlockBuilder() const { - return TestBlockBuilder().height(1).prevHash(kPrevHash).createdTime( - iroha::time::now()); + return shared_model::proto::TemplateBlockBuilder< + (1 << shared_model::proto::TemplateBlockBuilder<>::total) - 1, + shared_model::validation::AlwaysValidValidator, + shared_model::proto::UnsignedWrapper< + shared_model::proto::Block>>() + .height(1) + .prevHash(kPrevHash) + .createdTime(iroha::time::now()); } const Hash kPrevHash = @@ -86,6 +92,7 @@ class BlockLoaderTest : public testing::Test { std::shared_ptr peer; PublicKey peer_key = DefaultCryptoAlgorithmType::generateKeypair().publicKey(); + Keypair key = DefaultCryptoAlgorithmType::generateKeypair(); std::shared_ptr peer_query; std::shared_ptr storage; std::shared_ptr loader; @@ -100,7 +107,7 @@ class BlockLoaderTest : public testing::Test { */ TEST_F(BlockLoaderTest, ValidWhenSameTopBlock) { // Current block height 1 => Other block height 1 => no blocks received - auto block = getBaseBlockBuilder().build(); + auto block = getBaseBlockBuilder().build().signAndAddSignature(key).finish(); EXPECT_CALL(*peer_query, getLedgerPeers()) .WillOnce(Return(std::vector{peer})); @@ -122,15 +129,20 @@ TEST_F(BlockLoaderTest, ValidWhenSameTopBlock) { */ TEST_F(BlockLoaderTest, ValidWhenOneBlock) { // Current block height 1 => Other block height 2 => one block received - // time validation should work based on the block field - // so it should pass statless BlockLoader validation - auto block = getBaseBlockBuilder().createdTime(228).build(); + // so it should pass stateless BlockLoader validation + auto block = getBaseBlockBuilder() + .createdTime(228) + .build() + .signAndAddSignature(key) + .finish(); auto top_block = getBaseBlockBuilder() .createdTime(block.createdTime() + 1) .height(block.height() + 1) - .build(); + .build() + .signAndAddSignature(key) + .finish(); EXPECT_CALL(*peer_query, getLedgerPeers()) .WillOnce(Return(std::vector{peer})); @@ -154,17 +166,24 @@ TEST_F(BlockLoaderTest, ValidWhenOneBlock) { */ TEST_F(BlockLoaderTest, ValidWhenMultipleBlocks) { // Current block height 1 => Other block height n => n-1 blocks received - // time validation should work based on the block field - // so it should pass statless BlockLoader validation - auto block = getBaseBlockBuilder().createdTime(1337).build(); + // so it should pass stateless BlockLoader validation + auto block = getBaseBlockBuilder() + .createdTime(1337) + .build() + .signAndAddSignature(key) + .finish(); auto num_blocks = 2; auto next_height = block.height() + 1; std::vector blocks; for (auto i = next_height; i < next_height + num_blocks; ++i) { - auto blk = getBaseBlockBuilder().height(i).build(); + auto blk = getBaseBlockBuilder() + .height(i) + .build() + .signAndAddSignature(key) + .finish(); blocks.emplace_back(clone(blk)); } @@ -190,7 +209,8 @@ TEST_F(BlockLoaderTest, ValidWhenMultipleBlocks) { */ TEST_F(BlockLoaderTest, ValidWhenBlockPresent) { // Request existing block => success - auto requested = getBaseBlockBuilder().build(); + auto requested = + getBaseBlockBuilder().build().signAndAddSignature(key).finish(); EXPECT_CALL(*peer_query, getLedgerPeers()) .WillOnce(Return(std::vector{peer})); @@ -210,7 +230,8 @@ TEST_F(BlockLoaderTest, ValidWhenBlockPresent) { */ TEST_F(BlockLoaderTest, ValidWhenBlockMissing) { // Request nonexisting block => failure - auto present = getBaseBlockBuilder().build(); + auto present = + getBaseBlockBuilder().build().signAndAddSignature(key).finish(); EXPECT_CALL(*peer_query, getLedgerPeers()) .WillOnce(Return(std::vector{peer})); From dc578acc192a224984d5050a143aa6be9a7e9295 Mon Sep 17 00:00:00 2001 From: Sergei Date: Wed, 11 Jul 2018 15:48:37 +0700 Subject: [PATCH 34/97] Transaction Sequence Validator (#1549) Signed-off-by: Sergei --- .../protobuf/transaction_sequence_builder.hpp | 67 ++++++++ .../builders/protobuf/unsigned_proto.hpp | 8 + .../interfaces/iroha_internal/batch_meta.hpp | 6 +- .../iroha_internal/transaction_sequence.cpp | 22 --- .../iroha_internal/transaction_sequence.hpp | 15 -- shared_model/validators/CMakeLists.txt | 1 + .../any_order_validator.hpp | 23 +++ .../batch_order_validator.cpp | 111 +++++++++++++ .../batch_order_validator.hpp | 34 ++++ .../order_validator.hpp | 23 +++ ...gned_transactions_collection_validator.cpp | 23 ++- ...gned_transactions_collection_validator.hpp | 10 +- .../transactions_collection_validator.hpp | 10 +- ...gned_transactions_collection_validator.cpp | 15 +- ...gned_transactions_collection_validator.hpp | 10 +- .../protobuf/transport_builder_test.cpp | 146 ++++++++++++++++-- 16 files changed, 448 insertions(+), 76 deletions(-) create mode 100644 shared_model/builders/protobuf/transaction_sequence_builder.hpp create mode 100644 shared_model/validators/transactions_collection/any_order_validator.hpp create mode 100644 shared_model/validators/transactions_collection/batch_order_validator.cpp create mode 100644 shared_model/validators/transactions_collection/batch_order_validator.hpp create mode 100644 shared_model/validators/transactions_collection/order_validator.hpp diff --git a/shared_model/builders/protobuf/transaction_sequence_builder.hpp b/shared_model/builders/protobuf/transaction_sequence_builder.hpp new file mode 100644 index 0000000000..8fa3ab1e65 --- /dev/null +++ b/shared_model/builders/protobuf/transaction_sequence_builder.hpp @@ -0,0 +1,67 @@ +/** + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef IROHA_TRANSACTION_SEQUENCE_BUILDER_HPP +#define IROHA_TRANSACTION_SEQUENCE_BUILDER_HPP + +#include "interfaces/common_objects/types.hpp" +#include "interfaces/iroha_internal/transaction_sequence.hpp" + +namespace shared_model { + namespace proto { + + /** + * Class for building Transaction Sequence + * @tparam SV Stateless validator type + */ + template + class TransportBuilder { + private: + /** + * Creator of transaction sequence + * @param transactions collection of transactions + * @param validator validator of the collections + * @return Result containing transaction sequence if validation successful + * and string message containing error otherwise + */ + template + iroha::expected::Result + createTransactionSequence( + const interface::types::TransactionsForwardCollectionType + &transactions, + const validation::TransactionsCollectionValidator< + TransactionValidator, + OrderValidator> &validator) { + auto answer = validator.validate(transactions); + if (answer.hasErrors()) { + return iroha::expected::makeError(answer.reason()); + } + return iroha::expected::makeValue( + interface::TransactionSequence(transactions)); + } + + public: + TransportBuilder( + SV stateless_validator = SV()) + : stateless_validator_(stateless_validator) {} + + /** + * Builds TransactionSequence from transport object + * @param transport protobuf object from which TransactionSequence is + * built + * @return Result containing either TransactionSequence or message string + */ + template + iroha::expected::Result + build(T &transport) { + return createTransactionSequence(transport, stateless_validator_); + } + + private: + SV stateless_validator_; + }; + } // namespace proto +} // namespace shared_model +#endif // IROHA_TRANSACTION_SEQUENCE_BUILDER_HPP diff --git a/shared_model/builders/protobuf/unsigned_proto.hpp b/shared_model/builders/protobuf/unsigned_proto.hpp index 07d8bf95d6..258fbba630 100644 --- a/shared_model/builders/protobuf/unsigned_proto.hpp +++ b/shared_model/builders/protobuf/unsigned_proto.hpp @@ -70,6 +70,14 @@ namespace shared_model { return object_.hash(); } + template + typename std::enable_if< + std::is_base_of::value, + interface::types::HashType>::type + reduced_hash() { + return object_.reduced_hash(); + } + private: T object_; }; diff --git a/shared_model/interfaces/iroha_internal/batch_meta.hpp b/shared_model/interfaces/iroha_internal/batch_meta.hpp index a11d244c47..f65b1ab9d1 100644 --- a/shared_model/interfaces/iroha_internal/batch_meta.hpp +++ b/shared_model/interfaces/iroha_internal/batch_meta.hpp @@ -22,7 +22,8 @@ namespace shared_model { std::string toString() const override { return detail::PrettyStringBuilder() .init("BatchMeta") - .append("Type", type() == types::BatchType::ATOMIC ? "ATOMIC" : "ORDERED") + .append("Type", + type() == types::BatchType::ATOMIC ? "ATOMIC" : "ORDERED") .appendAll(transactionHashes(), [](auto &hash) { return hash.toString(); }) .finalize(); @@ -40,7 +41,8 @@ namespace shared_model { * @return true, if wrapped objects are same */ bool operator==(const ModelType &rhs) const override { - return type() == rhs.type(); + return type() == rhs.type() + and transactionHashes() == rhs.transactionHashes(); } }; } // namespace interface diff --git a/shared_model/interfaces/iroha_internal/transaction_sequence.cpp b/shared_model/interfaces/iroha_internal/transaction_sequence.cpp index 04aaf8f682..8ca3ebc195 100644 --- a/shared_model/interfaces/iroha_internal/transaction_sequence.cpp +++ b/shared_model/interfaces/iroha_internal/transaction_sequence.cpp @@ -10,28 +10,6 @@ namespace shared_model { namespace interface { - template - iroha::expected::Result - TransactionSequence::createTransactionSequence( - const types::TransactionsForwardCollectionType &transactions, - const validation::TransactionsCollectionValidator - &validator) { - auto answer = validator.validate(transactions); - if (answer.hasErrors()) { - return iroha::expected::makeError(answer.reason()); - } - return iroha::expected::makeValue(TransactionSequence(transactions)); - } - - template iroha::expected::Result - TransactionSequence::createTransactionSequence( - const types::TransactionsForwardCollectionType &transactions, - const validation::TransactionsCollectionValidator< - validation::TransactionValidator< - validation::FieldValidator, - validation::CommandValidatorVisitor< - validation::FieldValidator>>> &validator); - types::TransactionsForwardCollectionType TransactionSequence::transactions() { return transactions_; diff --git a/shared_model/interfaces/iroha_internal/transaction_sequence.hpp b/shared_model/interfaces/iroha_internal/transaction_sequence.hpp index 43e3aa7f56..8e693dfbe5 100644 --- a/shared_model/interfaces/iroha_internal/transaction_sequence.hpp +++ b/shared_model/interfaces/iroha_internal/transaction_sequence.hpp @@ -21,27 +21,12 @@ namespace shared_model { */ class TransactionSequence { public: - /** - * Creator of transaction sequence - * @param transactions collection of transactions - * @param validator validator of the collections - * @return Result containing transaction sequence if validation successful - * and string message containing error otherwise - */ - template - static iroha::expected::Result - createTransactionSequence( - const types::TransactionsForwardCollectionType &transactions, - const validation::TransactionsCollectionValidator< - TransactionValidator> &validator); - /** * Get transactions collection * @return transactions collection */ types::TransactionsForwardCollectionType transactions(); - private: explicit TransactionSequence( const types::TransactionsForwardCollectionType &transactions); diff --git a/shared_model/validators/CMakeLists.txt b/shared_model/validators/CMakeLists.txt index 47bb9ff10d..8889323b8a 100644 --- a/shared_model/validators/CMakeLists.txt +++ b/shared_model/validators/CMakeLists.txt @@ -17,6 +17,7 @@ add_library(shared_model_stateless_validation field_validator.cpp transactions_collection/signed_transactions_collection_validator.cpp transactions_collection/unsigned_transactions_collection_validator.cpp + transactions_collection/batch_order_validator.cpp ) target_link_libraries(shared_model_stateless_validation diff --git a/shared_model/validators/transactions_collection/any_order_validator.hpp b/shared_model/validators/transactions_collection/any_order_validator.hpp new file mode 100644 index 0000000000..36cf36a333 --- /dev/null +++ b/shared_model/validators/transactions_collection/any_order_validator.hpp @@ -0,0 +1,23 @@ +/** + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef IROHA_ANY_ORDER_VALIDATOR_HPP +#define IROHA_ANY_ORDER_VALIDATOR_HPP + +#include "order_validator.hpp" + +namespace shared_model { + namespace validation { + class AnyOrderValidator : public OrderValidator { + public: + Answer validate(const interface::types::TransactionsForwardCollectionType + &transactions) const override { + return Answer(); + }; + }; + } // namespace validation +} // namespace shared_model + +#endif // IROHA_ANY_ORDER_VALIDATOR_HPP diff --git a/shared_model/validators/transactions_collection/batch_order_validator.cpp b/shared_model/validators/transactions_collection/batch_order_validator.cpp new file mode 100644 index 0000000000..d103e02dd7 --- /dev/null +++ b/shared_model/validators/transactions_collection/batch_order_validator.cpp @@ -0,0 +1,111 @@ +/** + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#include "batch_order_validator.hpp" +#include "interfaces/iroha_internal/batch_meta.hpp" + +namespace shared_model { + namespace validation { + std::string BatchOrderValidator::canFollow( + boost::optional tr1, + boost::optional tr2) const { + boost::optional> batch1 = + tr1 ? tr1->batch_meta() : boost::none; + boost::optional> batch2 = + tr2 ? tr2->batch_meta() : boost::none; + // both transactions are not a part of any batch + if (not batch1 and not batch2) { + return ""; + } + // beginning of a batch + if (not batch1) { + if (batch2.get()->transactionHashes().size() == 0) { + return (boost::format("Tx %s has a batch of 0 transactions") + % tr2->hash().hex()) + .str(); + } + if (batch2.get()->transactionHashes().front() != tr2->reduced_hash()) { + return (boost::format("Tx %s is a first transaction of a batch, but " + "it's reduced hash %s doesn't match the first " + "reduced hash in batch %s") + % tr2->hash().hex() + % batch2.get()->transactionHashes().front().hex() + % tr2->reduced_hash().hex()) + .str(); + } + return ""; + } + // end of a batch + if (not batch2) { + if (batch1.get()->transactionHashes().back() != tr1->reduced_hash()) { + return (boost::format("Tx %s is a last transaction of a batch, but " + "it's reduced hash %s doesn't match the last " + "reduced hash in batch %s") + % tr1->hash().hex() + % batch1.get()->transactionHashes().back().hex() + % tr1->reduced_hash().hex()) + .str(); + } + return ""; + } + // inside of a batch + auto it1 = + boost::find(batch1.get()->transactionHashes(), tr1->reduced_hash()); + auto it2 = + boost::find(batch2.get()->transactionHashes(), tr2->reduced_hash()); + if (it1 == end(batch1.get()->transactionHashes()) + or it2 == end(batch2.get()->transactionHashes()) + or next(it1) == end(batch1.get()->transactionHashes()) + or *next(it1) != *it2) { + // end of the bach and beginning of the next + if (canFollow(tr1, boost::none) == "" + and canFollow(boost::none, tr2) == "") { + return ""; + } + return (boost::format("Tx %s is followed by %s, but their reduced" + "hashed doesn't follow each other in their batch" + "meta") + % tr1->hash().hex() % tr2->hash().hex()) + .str(); + } + if (**batch1 != **batch2) { + return (boost::format("Tx %s and %s are part of the same batch, but " + "their batch metas doesn't match") + % tr1->hash().hex() % tr2->hash().hex()) + .str(); + } + return ""; + } + + Answer BatchOrderValidator::validate( + const interface::types::TransactionsForwardCollectionType &transactions) + const { + Answer res; + ReasonsGroupType reason; + reason.first = "Transaction order"; + boost::optional prev_transaction = + boost::none; + + for (auto &transaction : transactions) { + auto message = canFollow(prev_transaction, transaction); + if (message != "") { + reason.second.push_back(message); + } + prev_transaction = transaction; + } + auto message = canFollow(prev_transaction, boost::none); + if (message != "") { + reason.second.push_back(message); + } + if (not reason.second.empty()) { + res.addReason(std::move(reason)); + } + return res; + } + } // namespace validation +} // namespace shared_model diff --git a/shared_model/validators/transactions_collection/batch_order_validator.hpp b/shared_model/validators/transactions_collection/batch_order_validator.hpp new file mode 100644 index 0000000000..6e4ab74cc0 --- /dev/null +++ b/shared_model/validators/transactions_collection/batch_order_validator.hpp @@ -0,0 +1,34 @@ +/** + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef IROHA_BATCH_ORDER_VALIDATOR_HPP +#define IROHA_BATCH_ORDER_VALIDATOR_HPP + +#include "order_validator.hpp" + +namespace shared_model { + namespace validation { + class BatchOrderValidator : public OrderValidator { + private: + /** + * check order of transaction, recieves 2 consequtive transactions in + * sequence and check weater they can be a part of correct sequence + * @param t1, t2 transactions to check the order of boost::none to specify + * beginning or end of sequence + * @return empty string if order is correct or error message + */ + std::string canFollow( + boost::optional tr1, + boost::optional tr2) const; + + public: + virtual Answer validate( + const interface::types::TransactionsForwardCollectionType + &transactions) const override; + }; + } // namespace validation +} // namespace shared_model + +#endif // IROHA_ORDER_VALIDATOR_HPP diff --git a/shared_model/validators/transactions_collection/order_validator.hpp b/shared_model/validators/transactions_collection/order_validator.hpp new file mode 100644 index 0000000000..a828d738d7 --- /dev/null +++ b/shared_model/validators/transactions_collection/order_validator.hpp @@ -0,0 +1,23 @@ +/** + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef IROHA_ORDER_VALIDATOR_HPP +#define IROHA_ORDER_VALIDATOR_HPP + +#include "validators/answer.hpp" +#include "interfaces/common_objects/transaction_sequence_common.hpp" + +namespace shared_model { + namespace validation { + class OrderValidator { + public: + virtual Answer validate( + const interface::types::TransactionsForwardCollectionType + &transactions) const = 0; + }; + } // namespace validation +} // namespace shared_model + +#endif // IROHA_ORDER_VALIDATOR_HPP diff --git a/shared_model/validators/transactions_collection/signed_transactions_collection_validator.cpp b/shared_model/validators/transactions_collection/signed_transactions_collection_validator.cpp index 528858d7a3..58ec6366f3 100644 --- a/shared_model/validators/transactions_collection/signed_transactions_collection_validator.cpp +++ b/shared_model/validators/transactions_collection/signed_transactions_collection_validator.cpp @@ -8,15 +8,19 @@ #include #include "validators/field_validator.hpp" #include "validators/transaction_validator.hpp" +#include "validators/transactions_collection/batch_order_validator.hpp" namespace shared_model { namespace validation { - template - Answer - SignedTransactionsCollectionValidator::validate( - const interface::types::TransactionsForwardCollectionType &transactions) - const { + template + Answer SignedTransactionsCollectionValidator:: + validate(const interface::types::TransactionsForwardCollectionType + &transactions) const { + Answer res = + SignedTransactionsCollectionValidator::order_validator_.validate( + transactions); ReasonsGroupType reason; reason.first = "Transaction list"; for (const auto &tx : transactions) { @@ -30,8 +34,6 @@ namespace shared_model { reason.second.push_back(message); } } - - Answer res; if (not reason.second.empty()) { res.addReason(std::move(reason)); } @@ -44,5 +46,12 @@ namespace shared_model { validate(const interface::types::TransactionsForwardCollectionType &transactions) const; + template Answer SignedTransactionsCollectionValidator< + TransactionValidator>, + BatchOrderValidator>:: + validate(const interface::types::TransactionsForwardCollectionType + &transactions) const; + } // namespace validation } // namespace shared_model diff --git a/shared_model/validators/transactions_collection/signed_transactions_collection_validator.hpp b/shared_model/validators/transactions_collection/signed_transactions_collection_validator.hpp index d5853b3c50..f8a65c9046 100644 --- a/shared_model/validators/transactions_collection/signed_transactions_collection_validator.hpp +++ b/shared_model/validators/transactions_collection/signed_transactions_collection_validator.hpp @@ -6,6 +6,7 @@ #ifndef IROHA_SIGNED_TRANSACTIONS_COLLECTION_VALIDATOR_HPP #define IROHA_SIGNED_TRANSACTIONS_COLLECTION_VALIDATOR_HPP +#include "validators/transactions_collection/any_order_validator.hpp" #include "validators/transactions_collection/transactions_collection_validator.hpp" namespace shared_model { @@ -16,12 +17,15 @@ namespace shared_model { * transaction from the collection to be unsigned. Batch logic should be * checked */ - template + template class SignedTransactionsCollectionValidator - : public TransactionsCollectionValidator { + : public TransactionsCollectionValidator { public: using TransactionsCollectionValidator< - TransactionValidator>::TransactionsCollectionValidator; + TransactionValidator, + OrderValidator>::TransactionsCollectionValidator; Answer validate(const interface::types::TransactionsForwardCollectionType &transactions) const override; }; diff --git a/shared_model/validators/transactions_collection/transactions_collection_validator.hpp b/shared_model/validators/transactions_collection/transactions_collection_validator.hpp index 2a530d7257..904439b819 100644 --- a/shared_model/validators/transactions_collection/transactions_collection_validator.hpp +++ b/shared_model/validators/transactions_collection/transactions_collection_validator.hpp @@ -8,6 +8,7 @@ #include "interfaces/common_objects/transaction_sequence_common.hpp" #include "validators/answer.hpp" +#include "validators/transactions_collection/any_order_validator.hpp" namespace shared_model { namespace validation { @@ -16,16 +17,19 @@ namespace shared_model { * Validator of transaction's collection, this is not fair implementation * now, it always returns empty answer */ - template + template class TransactionsCollectionValidator { protected: TransactionValidator transaction_validator_; + OrderValidator order_validator_; public: TransactionsCollectionValidator( const TransactionValidator &transactions_validator = - TransactionValidator()) - : transaction_validator_(transactions_validator) {} + TransactionValidator(), + const OrderValidator &order_validator = OrderValidator()) + : order_validator_(order_validator) {} /** * Validates collection of transactions diff --git a/shared_model/validators/transactions_collection/unsigned_transactions_collection_validator.cpp b/shared_model/validators/transactions_collection/unsigned_transactions_collection_validator.cpp index c68d194e3f..cdefe57d89 100644 --- a/shared_model/validators/transactions_collection/unsigned_transactions_collection_validator.cpp +++ b/shared_model/validators/transactions_collection/unsigned_transactions_collection_validator.cpp @@ -8,15 +8,19 @@ #include #include "validators/field_validator.hpp" #include "validators/transaction_validator.hpp" +#include "validators/transactions_collection/batch_order_validator.hpp" namespace shared_model { namespace validation { - template - Answer - UnsignedTransactionsCollectionValidator::validate( - const interface::types::TransactionsForwardCollectionType &transactions) - const { + template + Answer UnsignedTransactionsCollectionValidator:: + validate(const interface::types::TransactionsForwardCollectionType + &transactions) const { + Answer res = + UnsignedTransactionsCollectionValidator::order_validator_.validate( + transactions); ReasonsGroupType reason; reason.first = "Transaction list"; for (const auto &tx : transactions) { @@ -31,7 +35,6 @@ namespace shared_model { } } - Answer res; if (not reason.second.empty()) { res.addReason(std::move(reason)); } diff --git a/shared_model/validators/transactions_collection/unsigned_transactions_collection_validator.hpp b/shared_model/validators/transactions_collection/unsigned_transactions_collection_validator.hpp index 6513dfd76f..515bd11127 100644 --- a/shared_model/validators/transactions_collection/unsigned_transactions_collection_validator.hpp +++ b/shared_model/validators/transactions_collection/unsigned_transactions_collection_validator.hpp @@ -7,6 +7,7 @@ #define IROHA_UNSIGNED_TRANSACTIONS_SEQUENCE_VALIDATOR_HPP #include "validators/transactions_collection/transactions_collection_validator.hpp" +#include "validators/transactions_collection/any_order_validator.hpp" namespace shared_model { namespace validation { @@ -15,12 +16,15 @@ namespace shared_model { * Unsigned transactions collection validator allows to some transaction * from the collection to be unsigned. Batch logic should be checked */ - template + template class UnsignedTransactionsCollectionValidator - : public TransactionsCollectionValidator { + : public TransactionsCollectionValidator { public: using TransactionsCollectionValidator< - TransactionValidator>::TransactionsCollectionValidator; + TransactionValidator, + OrderValidator>::TransactionsCollectionValidator; Answer validate(const interface::types::TransactionsForwardCollectionType &transactions) const override; }; diff --git a/test/module/shared_model/builders/protobuf/transport_builder_test.cpp b/test/module/shared_model/builders/protobuf/transport_builder_test.cpp index 745ff7329a..ef5fd281d0 100644 --- a/test/module/shared_model/builders/protobuf/transport_builder_test.cpp +++ b/test/module/shared_model/builders/protobuf/transport_builder_test.cpp @@ -1,18 +1,6 @@ /** - * Copyright Soramitsu Co., Ltd. 2018 All Rights Reserved. - * http://soramitsu.co.jp - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 */ #include @@ -24,26 +12,39 @@ #include "builders/protobuf/proposal.hpp" #include "builders/protobuf/queries.hpp" #include "builders/protobuf/transaction.hpp" +#include "builders/protobuf/transaction_sequence_builder.hpp" #include "builders/protobuf/transport_builder.hpp" #include "common/types.hpp" #include "framework/result_fixture.hpp" +#include "interfaces/common_objects/types.hpp" +#include "interfaces/iroha_internal/transaction_sequence.hpp" #include "module/shared_model/builders/protobuf/test_block_builder.hpp" #include "module/shared_model/builders/protobuf/test_empty_block_builder.hpp" #include "module/shared_model/builders/protobuf/test_proposal_builder.hpp" #include "module/shared_model/builders/protobuf/test_query_builder.hpp" #include "module/shared_model/builders/protobuf/test_transaction_builder.hpp" +#include "validators/transactions_collection/batch_order_validator.hpp" using namespace shared_model; using namespace shared_model::proto; using namespace iroha::expected; using iroha::operator|; +using TransactionSequenceBuilder = TransportBuilder< + interface::TransactionSequence, + validation::SignedTransactionsCollectionValidator< + validation::TransactionValidator< + validation::FieldValidator, + validation::CommandValidatorVisitor>, + validation::BatchOrderValidator>>; + class TransportBuilderTest : public ::testing::Test { protected: void SetUp() override { created_time = iroha::time::now(); invalid_created_time = 123; account_id = "account@domain"; + account_id2 = "acccount@domain"; quorum = 2; counter = 1048576; hash = std::string(32, '0'); @@ -59,7 +60,10 @@ class TransportBuilderTest : public ::testing::Test { .quorum(quorum) .setAccountQuorum(account_id, quorum); } - + auto createUnbuildTransaction() { + return getBaseTransactionBuilder() + .creatorAccountId(account_id); + } auto createTransaction() { return getBaseTransactionBuilder() .creatorAccountId(account_id) @@ -165,6 +169,32 @@ class TransportBuilderTest : public ::testing::Test { .build(); } + //--------------------------------Batch--------------------------------------- + auto getValidBatch( + int seed, + int size, + interface::types::BatchType type = interface::types::BatchType::ATOMIC) { + std::vector transactions; + std::vector builders; + std::vector batch_hashes; + for (int i = 0; i < size; i++) { + auto reduced_tr = TestUnsignedTransactionBuilder() + .createdTime(created_time + seed + i) + .quorum(quorum) + .setAccountQuorum(account_id, quorum) + .creatorAccountId(account_id); + builders.push_back(reduced_tr); + batch_hashes.push_back(reduced_tr.build().reduced_hash()); + } + for (auto &builder : builders) { + auto tr = builder.batchMeta(type, batch_hashes) + .build() + .signAndAddSignature(keypair) + .finish(); + transactions.push_back(tr); + } + return transactions; + } /** * Receives model object, gets transport from it, converts transport into * model object and checks if original and obtained model objects are the same @@ -189,6 +219,7 @@ class TransportBuilderTest : public ::testing::Test { decltype(iroha::time::now()) created_time; decltype(created_time) invalid_created_time; std::string account_id; + std::string account_id2; uint8_t quorum; uint64_t counter; std::string hash; @@ -434,3 +465,88 @@ TEST_F(TransportBuilderTest, BlockVariantWithInvalidBlock) { .build(block.getTransport())); ASSERT_TRUE(error); } + +//---------------------------Transaction Sequence------------------------------- + +/** + * @given empty range of transactions + * @when TransportBuilder tries to build TransactionSequence object + * @then built object contains TransactionSequence shared model object + * AND it containcs 0 transactions + */ +TEST_F(TransportBuilderTest, TransactionSequenceEmpty) { + std::vector tr; + auto val = framework::expected::val( + TransportBuilder>, + validation::BatchOrderValidator>>() + .build(tr)); + ASSERT_TRUE(val); + val | [](auto &seq) { ASSERT_EQ(boost::size(seq.value.transactions()), 0); }; +} + +/** + * @given sequence of transaction with a right order + * @when TransportBuilder tries to build TransactionSequence object + * @then built object contains TransactionSequence shared model object + */ +TEST_F(TransportBuilderTest, TransactionSequenceCorrect) { + std::vector transactions; + auto batch1 = getValidBatch(0, 10); + auto batch2 = getValidBatch(20, 5); + auto batch3 = getValidBatch(30, 5); + std::move( + std::begin(batch1), std::end(batch1), std::back_inserter(transactions)); + std::move( + std::begin(batch2), std::end(batch2), std::back_inserter(transactions)); + transactions.push_back(createTransaction()); + transactions.push_back(createTransaction()); + transactions.push_back(createTransaction()); + std::move( + std::begin(batch3), std::end(batch3), std::back_inserter(transactions)); + transactions.push_back(createTransaction()); + auto val = framework::expected::val( + TransactionSequenceBuilder().build(transactions)); + ASSERT_TRUE(val); + val | [](auto &seq) { ASSERT_EQ(boost::size(seq.value.transactions()), 24); }; +} +/** + * @given batch of transaction with transaction in the middle + * @when TransportBuilder tries to build TransactionSequence object + * @then built an error + */ +TEST_F(TransportBuilderTest, TransactionInteraptedBatch) { + std::vector transactions; + auto batch = getValidBatch(0, 10); + std::move(std::begin(batch), + std::begin(batch) + 3, + std::back_inserter(transactions)); + transactions.push_back(createTransaction()); + std::move( + std::begin(batch) + 3, std::end(batch), std::back_inserter(transactions)); + auto error = framework::expected::err( + TransactionSequenceBuilder().build(transactions)); + ASSERT_TRUE(error); +} + +/** + * @given batch of transaction with wrong order + * @when TransportBuilder tries to build TransactionSequence object + * @then built an error + */ +TEST_F(TransportBuilderTest, BatchWrongOrder) { + std::vector transactions; + auto batch = getValidBatch(0, 10); + std::move( + std::begin(batch) + 3, std::end(batch), std::back_inserter(transactions)); + std::move(std::begin(batch), + std::begin(batch) + 3, + std::back_inserter(transactions)); + auto error = framework::expected::err( + TransactionSequenceBuilder().build(transactions)); + ASSERT_TRUE(error); +} From 5cafea52d3c4fa759a3d78b1619fd3ee1c028bb8 Mon Sep 17 00:00:00 2001 From: Akvinikym Date: Wed, 11 Jul 2018 13:46:03 +0300 Subject: [PATCH 35/97] GetAccountDetail interface: Postgres queries Signed-off-by: Akvinikym --- irohad/ametsuchi/impl/postgres_wsv_query.cpp | 60 ++++++++++-- irohad/ametsuchi/impl/postgres_wsv_query.hpp | 15 ++- irohad/ametsuchi/wsv_query.hpp | 8 +- test/module/iroha-cli/client_test.cpp | 2 +- .../irohad/ametsuchi/ametsuchi_mocks.hpp | 12 ++- .../ametsuchi/wsv_query_command_test.cpp | 93 ++++++++++++++++++- 6 files changed, 172 insertions(+), 18 deletions(-) diff --git a/irohad/ametsuchi/impl/postgres_wsv_query.cpp b/irohad/ametsuchi/impl/postgres_wsv_query.cpp index 47b1eda0cf..0f559664a3 100644 --- a/irohad/ametsuchi/impl/postgres_wsv_query.cpp +++ b/irohad/ametsuchi/impl/postgres_wsv_query.cpp @@ -21,6 +21,7 @@ namespace iroha { namespace ametsuchi { + using shared_model::interface::types::AccountDetailKeyType; using shared_model::interface::types::AccountIdType; using shared_model::interface::types::AssetIdType; using shared_model::interface::types::DomainIdType; @@ -119,20 +120,65 @@ namespace iroha { } boost::optional PostgresWsvQuery::getAccountDetail( - const std::string &account_id) { - return execute_("SELECT data#>>" + transaction_.quote("{}") - + " FROM account WHERE account_id = " - + transaction_.quote(account_id) + ";") - | [&](const auto &result) -> boost::optional { + const std::string &account_id, + const AccountDetailKeyType &key, + const AccountIdType &writer) { + return [this, &account_id, &key, &writer]() { + if (key.empty() and writer.empty()) { + // retrieve all values for a specified account + return execute_("SELECT data#>>" + transaction_.quote("{}") + + " FROM account WHERE account_id = " + + transaction_.quote(account_id) + ";"); + } else if (not key.empty() and not writer.empty()) { + // retrieve values for the account, under the key and added by the + // writer + return execute_( + "SELECT json_build_object(" + + transaction_.quote(writer) + ", json_build_object(" + + transaction_.quote(key) + ", (" + "SELECT data #>> '{\"" + writer + "\"" + + ", \"" + key + "\"}' " + "FROM account " + "WHERE account_id = " + transaction_.quote(account_id) + + ")));"); + } else if (not writer.empty()) { + // retrieve values added by the writer under all keys + return execute_( + "SELECT json_build_object(" + + transaction_.quote(writer) + ", (" + "SELECT data -> " + transaction_.quote(writer) + " " + "FROM account " + "WHERE account_id = " + transaction_.quote(account_id) + + "));"); + } else { + // retrieve values from all writers under the key + return execute_( + "SELECT json_object_agg(key, value) AS json " + "FROM (" + "SELECT json_build_object(" + "kv.key, " + "json_build_object(" + + transaction_.quote(key) + ", " + "kv.value -> " + transaction_.quote(key) + ")) " + "FROM jsonb_each((" + "SELECT data " + "FROM account " + "WHERE account_id = " + + transaction_.quote(account_id) + ")) kv " + "WHERE kv.value ? " + transaction_.quote(key) + ") " + "AS jsons, json_each(json_build_object);"); + } + }() | [&](const auto &result) -> boost::optional { if (result.empty()) { + // result will be empty iff account id is not found log_->info(kAccountNotFound, account_id); return boost::none; } - auto row = result.at(0); std::string res; + auto row = result.at(0); row.at(0) >> res; - // if res is empty, then that key does not exist for this account + // if res is empty, then there is no data for this account if (res.empty()) { return boost::none; } diff --git a/irohad/ametsuchi/impl/postgres_wsv_query.hpp b/irohad/ametsuchi/impl/postgres_wsv_query.hpp index 9a32b38d4c..3a15c67506 100644 --- a/irohad/ametsuchi/impl/postgres_wsv_query.hpp +++ b/irohad/ametsuchi/impl/postgres_wsv_query.hpp @@ -42,30 +42,41 @@ namespace iroha { boost::optional> getAccount(const shared_model::interface::types::AccountIdType &account_id) override; + boost::optional getAccountDetail( - const shared_model::interface::types::AccountIdType &account_id) - override; + const shared_model::interface::types::AccountIdType &account_id, + const shared_model::interface::types::AccountDetailKeyType &key = "", + const shared_model::interface::types::AccountIdType &writer = + "") override; + boost::optional> getSignatories(const shared_model::interface::types::AccountIdType &account_id) override; + boost::optional> getAsset( const shared_model::interface::types::AssetIdType &asset_id) override; + boost::optional< std::vector>> getAccountAssets(const shared_model::interface::types::AccountIdType &account_id) override; + boost::optional> getAccountAsset( const shared_model::interface::types::AccountIdType &account_id, const shared_model::interface::types::AssetIdType &asset_id) override; + boost::optional< std::vector>> getPeers() override; + boost::optional> getRoles() override; + boost::optional> getDomain(const shared_model::interface::types::DomainIdType &domain_id) override; + bool hasAccountGrantablePermission( const shared_model::interface::types::AccountIdType &permitee_account_id, diff --git a/irohad/ametsuchi/wsv_query.hpp b/irohad/ametsuchi/wsv_query.hpp index dcd602ccca..6251789465 100644 --- a/irohad/ametsuchi/wsv_query.hpp +++ b/irohad/ametsuchi/wsv_query.hpp @@ -102,10 +102,16 @@ namespace iroha { /** * Get accounts information from its key-value storage * @param account_id - account to get details about + * @param key - only values under this key from Json are returned; default + * empty + * @param writer - only values, added by the writer's account, are + * returned; default empty * @return optional of account details */ virtual boost::optional getAccountDetail( - const std::string &account_id) = 0; + const std::string &account_id, + const std::string &key = "", + const std::string &writer = "") = 0; /** * Get signatories of account by user account_id diff --git a/test/module/iroha-cli/client_test.cpp b/test/module/iroha-cli/client_test.cpp index cabda937bc..4839d65dde 100644 --- a/test/module/iroha-cli/client_test.cpp +++ b/test/module/iroha-cli/client_test.cpp @@ -301,7 +301,7 @@ TEST_F(ClientServerTest, SendQueryWhenValid) { EXPECT_CALL(*wsv_query, getSignatories("admin@test")) .WillRepeatedly(Return(signatories)); - EXPECT_CALL(*wsv_query, getAccountDetail("test@test")) + EXPECT_CALL(*wsv_query, getAccountDetail("test@test", "", "")) .WillOnce(Return(boost::make_optional(std::string("value")))); const std::vector kRole{"role"}; diff --git a/test/module/irohad/ametsuchi/ametsuchi_mocks.hpp b/test/module/irohad/ametsuchi/ametsuchi_mocks.hpp index 6a1d975817..d7512a474c 100644 --- a/test/module/irohad/ametsuchi/ametsuchi_mocks.hpp +++ b/test/module/irohad/ametsuchi/ametsuchi_mocks.hpp @@ -21,6 +21,7 @@ #include #include #include "ametsuchi/block_query.hpp" +#include "ametsuchi/key_value_storage.hpp" #include "ametsuchi/mutable_factory.hpp" #include "ametsuchi/mutable_storage.hpp" #include "ametsuchi/peer_query.hpp" @@ -28,7 +29,6 @@ #include "ametsuchi/temporary_factory.hpp" #include "ametsuchi/temporary_wsv.hpp" #include "ametsuchi/wsv_query.hpp" -#include "ametsuchi/key_value_storage.hpp" #include "common/result.hpp" #include "interfaces/common_objects/peer.hpp" @@ -39,8 +39,11 @@ namespace iroha { MOCK_METHOD1(getAccountRoles, boost::optional>( const std::string &account_id)); - MOCK_METHOD1(getAccountDetail, - boost::optional(const std::string &account_id)); + MOCK_METHOD3( + getAccountDetail, + boost::optional(const std::string &account_id, + const std::string &key, + const std::string &writer)); MOCK_METHOD1(getRolePermissions, boost::optional( const std::string &role_name)); @@ -261,8 +264,7 @@ namespace iroha { class MockKeyValueStorage : public KeyValueStorage { public: MOCK_METHOD2(add, bool(Identifier, const Bytes &)); - MOCK_CONST_METHOD1(get, - boost::optional(Identifier)); + MOCK_CONST_METHOD1(get, boost::optional(Identifier)); MOCK_CONST_METHOD0(directory, std::string(void)); MOCK_CONST_METHOD0(last_id, Identifier(void)); MOCK_METHOD0(dropAll, void(void)); diff --git a/test/module/irohad/ametsuchi/wsv_query_command_test.cpp b/test/module/irohad/ametsuchi/wsv_query_command_test.cpp index 654060cdb0..6ba63d8936 100644 --- a/test/module/irohad/ametsuchi/wsv_query_command_test.cpp +++ b/test/module/irohad/ametsuchi/wsv_query_command_test.cpp @@ -206,7 +206,7 @@ namespace iroha { /** * @given inserted role, domain, account - * @when update json data in account + * @when update json data in account * @then get account and check json data is the same */ TEST_F(AccountTest, UpdateAccountJSONData) { @@ -233,7 +233,96 @@ namespace iroha { * @then getAccountDetail will return nullopt */ TEST_F(AccountTest, GetAccountDetailInvalidWhenNotFound) { - EXPECT_FALSE(query->getAccountDetail("invalid account id")); + EXPECT_FALSE(query->getAccountDetail("invalid account id", "", "")); + } + + /** + * @given details, inserted for one account + * @when performing query to retrieve all account's details + * @then getAccountDetail will return all details of this account + */ + TEST_F(AccountTest, GetAccountDetailWithAccount) { + ASSERT_TRUE(val(command->insertAccount(*account))); + ASSERT_TRUE(val(command->setAccountKV( + account->accountId(), account->accountId(), "some_key", "some_val"))); + + auto acc_details = query->getAccountDetail(account->accountId(), "", ""); + ASSERT_TRUE(acc_details); + ASSERT_EQ(R"({"id@domain": {"key": "value", "some_key": "some_val"}})", + *acc_details); + } + + /** + * @given details, inserted into one account by two writers, with one of the + * keys repeated + * @when performing query to retrieve details under this key + * @then getAccountDetail will return details from both writers under the + * specified key + */ + TEST_F(AccountTest, GetAccountDetailWithKey) { + ASSERT_TRUE(val(command->insertAccount(*account))); + ASSERT_TRUE(val(command->setAccountKV( + account->accountId(), account->accountId(), "some_key", "some_val"))); + ASSERT_TRUE(val(command->setAccountKV(account->accountId(), + account->accountId(), + "another_key", + "another_val"))); + ASSERT_TRUE(val(command->setAccountKV( + account->accountId(), "admin", "some_key", "even_third_val"))); + + auto acc_details = + query->getAccountDetail(account->accountId(), "some_key", ""); + ASSERT_TRUE(acc_details); + ASSERT_EQ( + "{ \"admin\" : {\"some_key\" : \"even_third_val\"}, " + "\"id@domain\" : {\"some_key\" : \"some_val\"} }", + *acc_details); + } + + /** + * @given details, inserted into one account by two writers + * @when performing query to retrieve details, added by one of the writers + * @then getAccountDetail will return only details, added by the specified + * writer + */ + TEST_F(AccountTest, GetAccountDetailWithWriter) { + ASSERT_TRUE(val(command->insertAccount(*account))); + ASSERT_TRUE(val(command->setAccountKV( + account->accountId(), account->accountId(), "some_key", "some_val"))); + ASSERT_TRUE(val(command->setAccountKV( + account->accountId(), "admin", "another_key", "another_val"))); + + auto acc_details = + query->getAccountDetail(account->accountId(), "", "admin"); + ASSERT_TRUE(acc_details); + ASSERT_EQ(R"({"admin" : {"another_key": "another_val"}})", + *acc_details); + } + + /** + * @given details, inserted into one account by two writers, with one of the + * keys repeated + * @when performing query to retrieve details under this key and added by + * one of the writers + * @then getAccountDetail will return only details, which are under the + * specified key and added by the specified writer + */ + TEST_F(AccountTest, GetAccountDetailWithKeyAndWriter) { + ASSERT_TRUE(val(command->insertAccount(*account))); + ASSERT_TRUE(val(command->setAccountKV( + account->accountId(), account->accountId(), "some_key", "some_val"))); + ASSERT_TRUE(val(command->setAccountKV(account->accountId(), + account->accountId(), + "another_key", + "another_val"))); + ASSERT_TRUE(val(command->setAccountKV( + account->accountId(), "admin", "some_key", "even_third_val"))); + + auto acc_details = query->getAccountDetail( + account->accountId(), "some_key", account->accountId()); + ASSERT_TRUE(acc_details); + ASSERT_EQ(R"({"id@domain" : {"some_key" : "some_val"}})", + *acc_details); } class AccountRoleTest : public WsvQueryCommandTest { From d181e0782fdb777def4f6bbfa850391a6929394a Mon Sep 17 00:00:00 2001 From: Kitsu Date: Thu, 12 Jul 2018 00:44:15 +0300 Subject: [PATCH 36/97] Endpoint fuzzing (#1531) - Add torii fuzzing - Add status fuzzing - Add find fuzzing - Use libprotobuf-mutator - Use gtest entrypoint explicitly (thanks @lebdron) Signed-off-by: Kitsu --- CMakeLists.txt | 6 +- cmake/Modules/Findgtest.cmake | 34 ++++-- cmake/Modules/Findprotobuf-mutator.cmake | 36 ++++++ cmake/functions.cmake | 2 +- patch/libprotobuf-mutator.patch | 107 ++++++++++++++++++ test/CMakeLists.txt | 6 + test/fuzzing/CMakeLists.txt | 39 +++++++ test/fuzzing/find_fuzz.cpp | 48 ++++++++ test/fuzzing/status_fuzz.cpp | 74 ++++++++++++ test/fuzzing/torii_fuzz.cpp | 57 ++++++++++ test/integration/acceptance/CMakeLists.txt | 2 +- .../irohad/ametsuchi/ametsuchi_mocks.hpp | 2 +- 12 files changed, 396 insertions(+), 17 deletions(-) create mode 100644 cmake/Modules/Findprotobuf-mutator.cmake create mode 100644 patch/libprotobuf-mutator.patch create mode 100644 test/fuzzing/CMakeLists.txt create mode 100644 test/fuzzing/find_fuzz.cpp create mode 100644 test/fuzzing/status_fuzz.cpp create mode 100644 test/fuzzing/torii_fuzz.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index e3ad2bbdc5..c3b0587bfb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -131,9 +131,7 @@ if(TESTING) endif() if (FUZZING) - if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") - add_subdirectory(fuzz) - else() - message(Fuzzing with compilers other than clang does not supported yet) + if (NOT "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") + message(Fuzzing with compilers other than clang is not supported yet) endif() endif() diff --git a/cmake/Modules/Findgtest.cmake b/cmake/Modules/Findgtest.cmake index 22e6cd6b63..daa6037cc1 100644 --- a/cmake/Modules/Findgtest.cmake +++ b/cmake/Modules/Findgtest.cmake @@ -1,5 +1,7 @@ -add_library(gtest UNKNOWN IMPORTED) -add_library(gmock UNKNOWN IMPORTED) +add_library(gtest::gtest UNKNOWN IMPORTED) +add_library(gtest::main UNKNOWN IMPORTED) +add_library(gmock::gmock UNKNOWN IMPORTED) +add_library(gmock::main UNKNOWN IMPORTED) find_path(gtest_INCLUDE_DIR gtest/gtest.h) mark_as_advanced(gtest_INCLUDE_DIR) @@ -30,8 +32,8 @@ find_package_handle_standard_args(gtest DEFAULT_MSG set(URL https://github.com/google/googletest) set(VERSION ec44c6c1675c25b9827aacd08c02433cccde7780) -set_target_description(gtest "Unit testing library" ${URL} ${VERSION}) -set_target_description(gmock "Mocking library" ${URL} ${VERSION}) +set_target_description(gtest::gtest "Unit testing library" ${URL} ${VERSION}) +set_target_description(gmock::gmock "Mocking library" ${URL} ${VERSION}) if (NOT gtest_FOUND) ExternalProject_Add(google_test @@ -62,18 +64,30 @@ if (NOT gtest_FOUND) file(MAKE_DIRECTORY ${gtest_INCLUDE_DIR}) file(MAKE_DIRECTORY ${gmock_INCLUDE_DIR}) - add_dependencies(gtest google_test) - add_dependencies(gmock google_test) + add_dependencies(gtest::gtest google_test) + add_dependencies(gtest::main google_test) + add_dependencies(gmock::gmock google_test) + add_dependencies(gmock::main google_test) endif () -set_target_properties(gtest PROPERTIES +set_target_properties(gtest::gtest PROPERTIES INTERFACE_INCLUDE_DIRECTORIES ${gtest_INCLUDE_DIR} - INTERFACE_LINK_LIBRARIES "pthread;${gtest_MAIN_LIBRARY}" + INTERFACE_LINK_LIBRARIES Threads::Threads IMPORTED_LOCATION ${gtest_LIBRARY} ) +set_target_properties(gtest::main PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES ${gtest_INCLUDE_DIR} + INTERFACE_LINK_LIBRARIES gtest::gtest + IMPORTED_LOCATION ${gtest_MAIN_LIBRARY} + ) -set_target_properties(gmock PROPERTIES +set_target_properties(gmock::gmock PROPERTIES INTERFACE_INCLUDE_DIRECTORIES ${gmock_INCLUDE_DIR} - INTERFACE_LINK_LIBRARIES "pthread;${gmock_MAIN_LIBRARY}" + INTERFACE_LINK_LIBRARIES Threads::Threads IMPORTED_LOCATION ${gmock_LIBRARY} ) +set_target_properties(gmock::main PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES ${gmock_INCLUDE_DIR} + INTERFACE_LINK_LIBRARIES gmock::gmock + IMPORTED_LOCATION ${gmock_MAIN_LIBRARY} + ) diff --git a/cmake/Modules/Findprotobuf-mutator.cmake b/cmake/Modules/Findprotobuf-mutator.cmake new file mode 100644 index 0000000000..6650d821ef --- /dev/null +++ b/cmake/Modules/Findprotobuf-mutator.cmake @@ -0,0 +1,36 @@ +add_library(protobuf-mutator UNKNOWN IMPORTED) + +set(URL https://github.com/google/libprotobuf-mutator.git) +set(VERSION c9a1e56750a4eef6ffca95f41f79f06979056e01) +set(protomutator_LIB ${CMAKE_STATIC_LIBRARY_PREFIX}protobuf-mutator${CMAKE_STATIC_LIBRARY_SUFFIX}) +set(libfuzzer_LIB ${CMAKE_STATIC_LIBRARY_PREFIX}protobuf-mutator-libfuzzer${CMAKE_STATIC_LIBRARY_SUFFIX}) + +externalproject_add(google_protobuf-mutator + GIT_REPOSITORY ${URL} + GIT_TAG ${VERSION} + PATCH_COMMAND patch -p1 < ${PROJECT_SOURCE_DIR}/patch/libprotobuf-mutator.patch || true + CMAKE_ARGS -G${CMAKE_GENERATOR} -DTESTING=OFF + BUILD_BYPRODUCTS ${EP_PREFIX}/src/google_protobuf-mutator-build/src/${protomutator_LIB} + ${EP_PREFIX}/src/google_protobuf-mutator-build/src/libfuzzer/${libfuzzer_LIB} + INSTALL_COMMAND "" + TEST_COMMAND "" # remove test step + UPDATE_COMMAND "" # remove update step + ) +externalproject_get_property(google_protobuf-mutator source_dir binary_dir) +set(protobuf_mutator_INCLUDE_DIR ${source_dir}/src) +set(protobuf_mutator_LIBRARY ${binary_dir}/src/${protomutator_LIB}) +file(MAKE_DIRECTORY ${protobuf_mutator_INCLUDE_DIR}) +include_directories(${source_dir}) +link_directories(${binary_dir}) + +add_dependencies(protobuf-mutator google_protobuf-mutator) + +set_target_properties(protobuf-mutator PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES ${protobuf_mutator_INCLUDE_DIR} + INTERFACE_LINK_LIBRARIES ${protobuf_mutator_LIBRARY} + IMPORTED_LOCATION ${binary_dir}/src/libfuzzer/${libfuzzer_LIB} + ) + +if(ENABLE_LIBS_PACKAGING) + add_install_step_for_lib(${protobuf_mutator_LIBRARY}) +endif() diff --git a/cmake/functions.cmake b/cmake/functions.cmake index cc2e442bce..cbb9411649 100644 --- a/cmake/functions.cmake +++ b/cmake/functions.cmake @@ -25,7 +25,7 @@ function(addtest test_name SOURCES) set(test_xml_output --gtest_output=xml:${REPORT_DIR}/xunit-${test_name}.xml) endif () add_executable(${test_name} ${SOURCES}) - target_link_libraries(${test_name} gtest gmock) + target_link_libraries(${test_name} gtest::main gmock::main) target_include_directories(${test_name} PUBLIC ${PROJECT_SOURCE_DIR}/test) # fetch directory after test in source dir call diff --git a/patch/libprotobuf-mutator.patch b/patch/libprotobuf-mutator.patch new file mode 100644 index 0000000000..d816af0007 --- /dev/null +++ b/patch/libprotobuf-mutator.patch @@ -0,0 +1,107 @@ +diff --git a/CMakeLists.txt b/CMakeLists.txt +index c018d45..9497144 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -18,6 +18,7 @@ project(LibProtobufMutator CXX) + enable_language(C) + enable_language(CXX) + ++option(TESTING "Enable test building" ON) + option(LIB_PROTO_MUTATOR_DOWNLOAD_PROTOBUF + "Automatically download working protobuf" OFF) + option(LIB_PROTO_MUTATOR_WITH_ASAN "Enable address sanitizer" OFF) +@@ -110,20 +111,19 @@ else() + include_directories(${CMAKE_CURRENT_BINARY_DIR}) + endif() + +-enable_testing() +- +-include(googletest) ++if (TESTING) ++ enable_testing() ++ find_package(GTest) ++ if (NOT GTEST_FOUND) ++ include(googletest) ++ endif() + +-if (NOT LIB_PROTO_MUTATOR_CTEST_JOBS) +- ProcessorCount(LIB_PROTO_MUTATOR_CTEST_JOBS) ++ if (NOT LIB_PROTO_MUTATOR_CTEST_JOBS) ++ ProcessorCount(LIB_PROTO_MUTATOR_CTEST_JOBS) ++ endif() ++ add_custom_target(check ++ COMMAND ${CMAKE_CTEST_COMMAND} -j${LIB_PROTO_MUTATOR_CTEST_JOBS} --output-on-failure) + endif() +-add_custom_target(check +- COMMAND ${CMAKE_CTEST_COMMAND} -j${LIB_PROTO_MUTATOR_CTEST_JOBS} --output-on-failure) + + add_subdirectory(src) + +-if (NOT "${LIB_PROTO_MUTATOR_FUZZER_LIBRARIES}" STREQUAL "" OR +- NOT "${FUZZING_FLAGS}" STREQUAL "") +- add_subdirectory(examples EXCLUDE_FROM_ALL) +-endif() +- +diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt +index 5c13d2d..624f8b6 100644 +--- a/src/CMakeLists.txt ++++ b/src/CMakeLists.txt +@@ -24,31 +24,33 @@ target_link_libraries(protobuf-mutator + set_property(TARGET protobuf-mutator + PROPERTY COMPILE_FLAGS "${NO_FUZZING_FLAGS}") + +-protobuf_generate_cpp(PROTO_SRCS PROTO_HDRS +- mutator_test_proto2.proto +- mutator_test_proto3.proto) ++if (TESTING) ++ protobuf_generate_cpp(PROTO_SRCS PROTO_HDRS ++ mutator_test_proto2.proto ++ mutator_test_proto3.proto) + +-add_executable(mutator_test +- mutator_test.cc +- utf8_fix_test.cc +- weighted_reservoir_sampler_test.cc +- ${PROTO_SRCS}) +-target_link_libraries(mutator_test +- protobuf-mutator +- ${ZLIB_LIBRARIES} +- ${GTEST_BOTH_LIBRARIES} +- ${CMAKE_THREAD_LIBS_INIT}) ++ add_executable(mutator_test ++ mutator_test.cc ++ utf8_fix_test.cc ++ weighted_reservoir_sampler_test.cc ++ ${PROTO_SRCS}) ++ target_link_libraries(mutator_test ++ protobuf-mutator ++ ${ZLIB_LIBRARIES} ++ ${GTEST_BOTH_LIBRARIES} ++ ${CMAKE_THREAD_LIBS_INIT}) + +-ProcessorCount(CPU_COUNT) +-math(EXPR TEST_SHARDS_COUNT 2*${CPU_COUNT}) +-math(EXPR TEST_SHARDS_MAX ${TEST_SHARDS_COUNT}-1) +-foreach(SHARD RANGE ${TEST_SHARDS_MAX}) +- add_test(test.protobuf_mutator_test_${SHARD} mutator_test --gtest_color=yes AUTO) +- set_property( +- TEST test.protobuf_mutator_test_${SHARD} +- APPEND PROPERTY ENVIRONMENT +- GTEST_SHARD_INDEX=${SHARD} +- GTEST_TOTAL_SHARDS=${TEST_SHARDS_COUNT}) +-endforeach(SHARD) ++ ProcessorCount(CPU_COUNT) ++ math(EXPR TEST_SHARDS_COUNT 2*${CPU_COUNT}) ++ math(EXPR TEST_SHARDS_MAX ${TEST_SHARDS_COUNT}-1) ++ foreach(SHARD RANGE ${TEST_SHARDS_MAX}) ++ add_test(test.protobuf_mutator_test_${SHARD} mutator_test --gtest_color=yes AUTO) ++ set_property( ++ TEST test.protobuf_mutator_test_${SHARD} ++ APPEND PROPERTY ENVIRONMENT ++ GTEST_SHARD_INDEX=${SHARD} ++ GTEST_TOTAL_SHARDS=${TEST_SHARDS_COUNT}) ++ endforeach(SHARD) + +-add_dependencies(check mutator_test) ++ add_dependencies(check mutator_test) ++endif() diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index cd9ea35c24..15fb956b03 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -17,6 +17,8 @@ set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/test_bin) +include_directories(${CMAKE_CURRENT_SOURCE_DIR}) + add_subdirectory(module) add_subdirectory(framework) add_subdirectory(integration) @@ -26,3 +28,7 @@ add_subdirectory(system) if(BENCHMARKING) add_subdirectory(benchmark) endif() + +if (FUZZING) + add_subdirectory(fuzzing) +endif() diff --git a/test/fuzzing/CMakeLists.txt b/test/fuzzing/CMakeLists.txt new file mode 100644 index 0000000000..6eb746455b --- /dev/null +++ b/test/fuzzing/CMakeLists.txt @@ -0,0 +1,39 @@ +# Copyright Soramitsu Co., Ltd. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 + +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -fsanitize=fuzzer,address") + +find_package(protobuf-mutator REQUIRED) + +add_executable(torii_fuzzing torii_fuzz.cpp) +target_link_libraries(torii_fuzzing + rxcpp + gtest::gtest + gmock::gmock + ametsuchi + libs_common + torii_service + protobuf-mutator + ) + +add_executable(status_fuzzing status_fuzz.cpp) +target_link_libraries(status_fuzzing + rxcpp + gtest::gtest + gmock::gmock + ametsuchi + libs_common + torii_service + protobuf-mutator + ) + +add_executable(find_fuzzing find_fuzz.cpp) +target_link_libraries(find_fuzzing + rxcpp + gtest::gtest + gmock::gmock + ametsuchi + libs_common + torii_service + protobuf-mutator + ) diff --git a/test/fuzzing/find_fuzz.cpp b/test/fuzzing/find_fuzz.cpp new file mode 100644 index 0000000000..b4933733c3 --- /dev/null +++ b/test/fuzzing/find_fuzz.cpp @@ -0,0 +1,48 @@ +/** + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include "libfuzzer/libfuzzer_macro.h" +#include "module/irohad/ametsuchi/ametsuchi_mocks.hpp" +#include "module/irohad/network/network_mocks.hpp" +#include "torii/processor/query_processor_impl.hpp" +#include "torii/query_service.hpp" + +using namespace std::chrono_literals; +using testing::_; +using testing::Return; + +struct QueryFixture { + std::shared_ptr service_; + std::shared_ptr qry_processor_; + std::shared_ptr storage_; + std::shared_ptr bq_; + std::shared_ptr wq_; + + QueryFixture() { + storage_ = std::make_shared(); + bq_ = std::make_shared(); + wq_ = std::make_shared(); + EXPECT_CALL(*storage_, getBlockQuery()).WillRepeatedly(Return(bq_)); + EXPECT_CALL(*storage_, getWsvQuery()).WillRepeatedly(Return(wq_)); + qry_processor_ = + std::make_shared(storage_); + service_ = std::make_shared(qry_processor_); + } +}; + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, std::size_t size) { + static QueryFixture handler; + if (size < 1) { + return 0; + } + iroha::protocol::Query qry; + if (protobuf_mutator::libfuzzer::LoadProtoInput(true, data, size, &qry)) { + iroha::protocol::QueryResponse resp; + handler.service_->Find(qry, resp); + } + return 0; +} diff --git a/test/fuzzing/status_fuzz.cpp b/test/fuzzing/status_fuzz.cpp new file mode 100644 index 0000000000..5251e90304 --- /dev/null +++ b/test/fuzzing/status_fuzz.cpp @@ -0,0 +1,74 @@ +/** + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include "libfuzzer/libfuzzer_macro.h" +#include "module/irohad/ametsuchi/ametsuchi_mocks.hpp" +#include "module/irohad/multi_sig_transactions/mst_mocks.hpp" +#include "module/irohad/network/network_mocks.hpp" +#include "torii/command_service.hpp" +#include "torii/processor/transaction_processor_impl.hpp" + +using namespace std::chrono_literals; +using testing::_; +using testing::Return; + +struct CommandFixture { + std::shared_ptr service_; + std::shared_ptr tx_processor_; + std::shared_ptr storage_; + std::shared_ptr pcs_; + std::shared_ptr mst_processor_; + std::shared_ptr bq_; + + rxcpp::subjects::subject> + prop_notifier_; + rxcpp::subjects::subject< + std::shared_ptr> + vprop_notifier_; + rxcpp::subjects::subject commit_notifier_; + rxcpp::subjects::subject mst_notifier_; + + CommandFixture() { + pcs_ = std::make_shared(); + EXPECT_CALL(*pcs_, on_proposal()) + .WillRepeatedly(Return(prop_notifier_.get_observable())); + EXPECT_CALL(*pcs_, on_commit()) + .WillRepeatedly(Return(commit_notifier_.get_observable())); + EXPECT_CALL(*pcs_, on_verified_proposal()) + .WillRepeatedly(Return(vprop_notifier_.get_observable())); + + mst_processor_ = std::make_shared(); + EXPECT_CALL(*mst_processor_, onPreparedTransactionsImpl()) + .WillRepeatedly(Return(mst_notifier_.get_observable())); + EXPECT_CALL(*mst_processor_, onExpiredTransactionsImpl()) + .WillRepeatedly(Return(mst_notifier_.get_observable())); + + storage_ = std::make_shared(); + bq_ = std::make_shared(); + EXPECT_CALL(*storage_, getBlockQuery()).WillRepeatedly(Return(bq_)); + tx_processor_ = std::make_shared( + pcs_, mst_processor_); + service_ = + std::make_shared(tx_processor_, storage_, 15s); + } +}; + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, std::size_t size) { + static CommandFixture handler; + if (size < 1) { + return 0; + } + EXPECT_CALL(*handler.bq_, hasTxWithHash(_)) + .WillRepeatedly(Return(static_cast(data[0]))); + iroha::protocol::TxStatusRequest tx; + if (protobuf_mutator::libfuzzer::LoadProtoInput( + true, data + 1, size - 1, &tx)) { + iroha::protocol::ToriiResponse resp; + handler.service_->Status(tx, resp); + } + return 0; +} diff --git a/test/fuzzing/torii_fuzz.cpp b/test/fuzzing/torii_fuzz.cpp new file mode 100644 index 0000000000..2cfb67b537 --- /dev/null +++ b/test/fuzzing/torii_fuzz.cpp @@ -0,0 +1,57 @@ +/** + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include "libfuzzer/libfuzzer_macro.h" +#include "module/irohad/ametsuchi/ametsuchi_mocks.hpp" +#include "module/irohad/multi_sig_transactions/mst_mocks.hpp" +#include "module/irohad/network/network_mocks.hpp" +#include "torii/command_service.hpp" +#include "torii/processor/transaction_processor_impl.hpp" + +using namespace std::chrono_literals; +using testing::Return; + +struct CommandFixture { + std::shared_ptr service_; + std::shared_ptr tx_processor_; + std::shared_ptr pcs_; + std::shared_ptr mst_processor_; + + rxcpp::subjects::subject> + prop_notifier_; + rxcpp::subjects::subject< + std::shared_ptr> + vprop_notifier_; + rxcpp::subjects::subject commit_notifier_; + rxcpp::subjects::subject mst_notifier_; + + CommandFixture() { + pcs_ = std::make_shared(); + EXPECT_CALL(*pcs_, on_proposal()) + .WillRepeatedly(Return(prop_notifier_.get_observable())); + EXPECT_CALL(*pcs_, on_commit()) + .WillRepeatedly(Return(commit_notifier_.get_observable())); + EXPECT_CALL(*pcs_, on_verified_proposal()) + .WillRepeatedly(Return(vprop_notifier_.get_observable())); + + mst_processor_ = std::make_shared(); + EXPECT_CALL(*mst_processor_, onPreparedTransactionsImpl()) + .WillRepeatedly(Return(mst_notifier_.get_observable())); + EXPECT_CALL(*mst_processor_, onExpiredTransactionsImpl()) + .WillRepeatedly(Return(mst_notifier_.get_observable())); + + tx_processor_ = std::make_shared( + pcs_, mst_processor_); + service_ = + std::make_shared(tx_processor_, nullptr, 15s); + } +}; + +DEFINE_BINARY_PROTO_FUZZER(const iroha::protocol::Transaction &tx) { + static CommandFixture handler; + handler.service_->Torii(tx); +} diff --git a/test/integration/acceptance/CMakeLists.txt b/test/integration/acceptance/CMakeLists.txt index 91fc764b7e..73876617d6 100644 --- a/test/integration/acceptance/CMakeLists.txt +++ b/test/integration/acceptance/CMakeLists.txt @@ -16,7 +16,7 @@ # add_library(acceptance_fixture acceptance_fixture.cpp) target_link_libraries(acceptance_fixture - gtest + gtest::gtest integration_framework shared_model_proto_builders ) diff --git a/test/module/irohad/ametsuchi/ametsuchi_mocks.hpp b/test/module/irohad/ametsuchi/ametsuchi_mocks.hpp index d7512a474c..3f4cb18fab 100644 --- a/test/module/irohad/ametsuchi/ametsuchi_mocks.hpp +++ b/test/module/irohad/ametsuchi/ametsuchi_mocks.hpp @@ -251,7 +251,7 @@ namespace iroha { MOCK_METHOD0(dropStorage, void(void)); rxcpp::observable> - on_commit() { + on_commit() override { return notifier.get_observable(); } void commit(std::unique_ptr storage) override { From b69796358db5aa381b30aa086a6e5cbf4e9fe09a Mon Sep 17 00:00:00 2001 From: Kitsu Date: Thu, 12 Jul 2018 01:26:30 +0300 Subject: [PATCH 37/97] Split scheme to server and client ones (#1550) Signed-off-by: Kitsu --- CMakeLists.txt | 5 +- clean.sh | 1 - cmake/functions.cmake | 29 ++-- example/java/TransactionExample.java | 12 +- example/java/build.gradle | 4 +- example/python/prepare.sh | 4 +- example/python/tx-example.py | 4 +- iroha-cli/query_response_handler.hpp | 2 +- irohad/model/converters/impl/pb_common.cpp | 2 +- irohad/model/converters/pb_common.hpp | 2 +- .../converters/pb_query_response_factory.hpp | 2 +- .../converters/pb_transaction_factory.hpp | 2 +- .../impl/ordering_service_transport_grpc.hpp | 2 +- irohad/torii/command_client.cpp | 4 +- irohad/torii/query_service.hpp | 2 +- schema/.gitignore | 2 - schema/CMakeLists.txt | 138 ++++++------------ schema/mst.proto | 2 +- schema/ordering.proto | 2 +- shared_model/CMakeLists.txt | 23 +-- shared_model/backend/protobuf/batch_meta.hpp | 2 +- .../protobuf/commands/proto_add_peer.hpp | 1 + .../protobuf/common_objects/account.hpp | 2 +- .../protobuf/common_objects/account_asset.hpp | 2 +- .../backend/protobuf/common_objects/asset.hpp | 2 +- .../protobuf/common_objects/domain.hpp | 2 +- .../backend/protobuf/common_objects/peer.hpp | 2 +- .../proto_account_asset_response.hpp | 6 +- .../proto_account_detail_response.hpp | 2 +- .../proto_account_response.hpp | 2 +- .../query_responses/proto_asset_response.hpp | 2 +- .../proto_block_error_response.hpp | 4 +- .../proto_block_query_response.hpp | 2 +- .../query_responses/proto_block_response.hpp | 2 +- .../proto_concrete_error_query_response.hpp | 2 +- .../proto_error_query_response.hpp | 2 +- .../query_responses/proto_query_response.hpp | 2 +- .../proto_role_permissions_response.hpp | 2 +- .../query_responses/proto_roles_response.hpp | 2 +- .../proto_signatories_response.hpp | 2 +- .../proto_transaction_response.hpp | 2 +- shared_model/backend/protobuf/transaction.hpp | 3 +- .../query_response_template.hpp | 2 +- .../transaction_template.hpp | 2 +- .../proto_account_asset_builder.hpp | 2 +- .../common_objects/proto_account_builder.hpp | 2 +- .../common_objects/proto_asset_builder.hpp | 2 +- .../common_objects/proto_domain_builder.hpp | 2 +- .../builders/protobuf/transaction.hpp | 13 -- shared_model/clean.sh | 1 - shared_model/interfaces/CMakeLists.txt | 1 + shared_model/packages/javascript/binding.gyp | 2 +- shared_model/schema/CMakeLists.txt | 29 ++++ shared_model/schema/block.proto | 23 +++ .../schema}/commands.proto | 5 + .../schema}/endpoint.proto | 11 +- .../schema}/primitive.proto | 5 + .../schema}/proposal.proto | 7 +- .../schema/qry_responses.proto | 6 + {schema => shared_model/schema}/queries.proto | 5 + .../schema/transaction.proto | 23 +-- .../acceptance/invalid_fields_test.cpp | 2 +- .../shared_model/backend_proto/CMakeLists.txt | 18 ++- .../backend_proto/shared_proto_util_test.cpp | 8 +- .../shared_model/bindings/BuilderTest.java | 4 +- .../shared_model/bindings/CMakeLists.txt | 6 +- .../shared_model/bindings/builder-test.py | 4 +- .../builders/common_objects/CMakeLists.txt | 15 +- .../builders/protobuf/CMakeLists.txt | 16 +- .../protobuf/transport_builder_test.cpp | 1 - .../validators/field_validator_test.cpp | 7 +- .../validators/validators_fixture.hpp | 5 +- 72 files changed, 267 insertions(+), 256 deletions(-) delete mode 100644 schema/.gitignore create mode 100644 shared_model/schema/CMakeLists.txt create mode 100644 shared_model/schema/block.proto rename {schema => shared_model/schema}/commands.proto (95%) rename {schema => shared_model/schema}/endpoint.proto (83%) rename {schema => shared_model/schema}/primitive.proto (95%) rename {schema => shared_model/schema}/proposal.proto (57%) rename schema/responses.proto => shared_model/schema/qry_responses.proto (94%) rename {schema => shared_model/schema}/queries.proto (93%) rename schema/block.proto => shared_model/schema/transaction.proto (55%) diff --git a/CMakeLists.txt b/CMakeLists.txt index c3b0587bfb..d67b73096c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,3 +1,6 @@ +# Copyright Soramitsu Co., Ltd. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 + cmake_minimum_required(VERSION 3.5.1) find_program(CCACHE_PROGRAM ccache) @@ -101,11 +104,11 @@ message(STATUS "-DSWIG_CSHARP=${SWIG_CSHARP}") message(STATUS "-DSWIG_NODE=${SWIG_NODE}") SET(IROHA_SCHEMA_DIR "${PROJECT_SOURCE_DIR}/schema") +set(SCHEMA_OUT_DIR ${CMAKE_BINARY_DIR}/schema) include_directories( ${PROJECT_SOURCE_DIR}/irohad ${PROJECT_SOURCE_DIR}/shared_model ${PROJECT_SOURCE_DIR}/libs - ${IROHA_SCHEMA_DIR} ) SET(IROHA_ROOT_PROJECT ON) diff --git a/clean.sh b/clean.sh index 59e214debe..59cff7cc96 100755 --- a/clean.sh +++ b/clean.sh @@ -1,5 +1,4 @@ #!/bin/bash -rm schema/*.{cc,h} rm -rf external rm -rf build rm -rf cmake-build-debug diff --git a/cmake/functions.cmake b/cmake/functions.cmake index cbb9411649..f077186254 100644 --- a/cmake/functions.cmake +++ b/cmake/functions.cmake @@ -71,31 +71,34 @@ function(compile_proto_to_cpp PROTO) set(GEN_ARGS ${protobuf_INCLUDE_DIR}) endif() add_custom_command( - OUTPUT ${IROHA_SCHEMA_DIR}/${GEN_PB_HEADER} ${IROHA_SCHEMA_DIR}/${GEN_PB} + OUTPUT ${SCHEMA_OUT_DIR}/${GEN_PB_HEADER} ${SCHEMA_OUT_DIR}/${GEN_PB} COMMAND ${GEN_COMMAND} - ARGS -I${GEN_ARGS} -I. --cpp_out=${IROHA_SCHEMA_DIR} ${PROTO} - DEPENDS protoc ${IROHA_SCHEMA_DIR}/${PROTO} - WORKING_DIRECTORY ${IROHA_SCHEMA_DIR} + ARGS -I${GEN_ARGS} -I${CMAKE_CURRENT_SOURCE_DIR} ${ARGN} --cpp_out=${SCHEMA_OUT_DIR} ${PROTO} + DEPENDS protoc ${SCHEMA_PATH}/${PROTO} + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} ) endfunction() - -function(compile_proto_to_grpc_cpp PROTO) - compile_proto_to_cpp(${PROTO}) +function(compile_proto_only_grpc_to_cpp PROTO) string(REGEX REPLACE "\\.proto$" ".grpc.pb.h" GEN_GRPC_PB_HEADER ${PROTO}) string(REGEX REPLACE "\\.proto$" ".grpc.pb.cc" GEN_GRPC_PB ${PROTO}) add_custom_command( - OUTPUT ${IROHA_SCHEMA_DIR}/${GEN_GRPC_PB_HEADER} ${IROHA_SCHEMA_DIR}/${GEN_GRPC_PB} + OUTPUT ${SCHEMA_OUT_DIR}/${GEN_GRPC_PB_HEADER} ${SCHEMA_OUT_DIR}/${GEN_GRPC_PB} COMMAND ${CMAKE_COMMAND} -E env LD_LIBRARY_PATH=${protobuf_LIBRARY_DIR}:$ENV{LD_LIBRARY_PATH} "${protoc_EXECUTABLE}" - ARGS -I${protobuf_INCLUDE_DIR} -I. --grpc_out=${IROHA_SCHEMA_DIR} --plugin=protoc-gen-grpc="${grpc_CPP_PLUGIN}" ${PROTO} - DEPENDS grpc_cpp_plugin ${IROHA_SCHEMA_DIR}/${PROTO} - WORKING_DIRECTORY ${IROHA_SCHEMA_DIR} + ARGS -I${protobuf_INCLUDE_DIR} -I${CMAKE_CURRENT_SOURCE_DIR} ${ARGN} --grpc_out=${SCHEMA_OUT_DIR} --plugin=protoc-gen-grpc="${grpc_CPP_PLUGIN}" ${PROTO} + DEPENDS grpc_cpp_plugin ${SCHEMA_PATH}/${PROTO} + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} ) endfunction() +function(compile_proto_to_grpc_cpp PROTO) + compile_proto_to_cpp(${PROTO} "${ARGN}") + compile_proto_only_grpc_to_cpp(${PROTO} "${ARGN}") +endfunction() function(compile_proto_to_python PROTO) string(REGEX REPLACE "\\.proto$" "_pb2.py" PY_PB ${PROTO}) + set(SM_SCHEMA_DIR "${PROJECT_SOURCE_DIR}/shared_model/schema") if (MSVC) set(GEN_COMMAND "${Protobuf_PROTOC_EXECUTABLE}") set(GEN_ARGS ${Protobuf_INCLUDE_DIR}) @@ -106,8 +109,8 @@ function(compile_proto_to_python PROTO) add_custom_command( OUTPUT ${SWIG_BUILD_DIR}/${PY_PB} COMMAND ${GEN_COMMAND} - ARGS -I${GEN_ARGS} -I. --python_out=${SWIG_BUILD_DIR} ${PROTO} - DEPENDS protoc ${IROHA_SCHEMA_DIR}/${PROTO} + ARGS -I${GEN_ARGS} -I${SM_SCHEMA_DIR} --python_out=${SWIG_BUILD_DIR} ${PROTO} + DEPENDS protoc ${SM_SCHEMA_DIR}/${PROTO} WORKING_DIRECTORY ${IROHA_SCHEMA_DIR} ) endfunction() diff --git a/example/java/TransactionExample.java b/example/java/TransactionExample.java index cab06f8d88..e66107e7d9 100644 --- a/example/java/TransactionExample.java +++ b/example/java/TransactionExample.java @@ -1,5 +1,5 @@ import iroha.protocol.Commands; -import iroha.protocol.BlockOuterClass; +import iroha.protocol.TransactionOuterClass; import iroha.protocol.Endpoint; import iroha.protocol.Queries; import iroha.protocol.Queries.Query; @@ -12,9 +12,9 @@ import iroha.protocol.Endpoint.TxStatus; import iroha.protocol.Endpoint.TxStatusRequest; import iroha.protocol.Endpoint.ToriiResponse; -import iroha.protocol.Responses.QueryResponse; -import iroha.protocol.Responses.AssetResponse; -import iroha.protocol.Responses.Asset; +import iroha.protocol.QryResponses.QueryResponse; +import iroha.protocol.QryResponses.AssetResponse; +import iroha.protocol.QryResponses.Asset; import com.google.protobuf.InvalidProtocolBufferException; import io.grpc.ManagedChannel; @@ -84,9 +84,9 @@ public static void main(String[] args) { byte bs[] = toByteArray(txblob); // create proto object - BlockOuterClass.Transaction protoTx = null; + TransactionOuterClass.Transaction protoTx = null; try { - protoTx = BlockOuterClass.Transaction.parseFrom(bs); + protoTx = TransactionOuterClass.Transaction.parseFrom(bs); } catch (InvalidProtocolBufferException e) { System.err.println("Exception while converting byte array to protobuf:" + e.getMessage()); System.exit(1); diff --git a/example/java/build.gradle b/example/java/build.gradle index a80b9ae414..f7d8aad6bc 100644 --- a/example/java/build.gradle +++ b/example/java/build.gradle @@ -3,7 +3,7 @@ apply plugin: 'com.google.protobuf' apply plugin: 'application' def BUILD_DIR = "./build/shared_model/bindings" -def SCHEMA_DIR = "../../schema" +def SCHEMA_DIR = ["../../schema", "../../shared_model/schema"] buildscript { repositories { @@ -38,7 +38,7 @@ sourceSets { srcDirs = ['.'] } proto { - srcDirs = [SCHEMA_DIR] + srcDirs = SCHEMA_DIR } } } diff --git a/example/python/prepare.sh b/example/python/prepare.sh index 12e5da318d..35244e8dde 100755 --- a/example/python/prepare.sh +++ b/example/python/prepare.sh @@ -9,5 +9,5 @@ cmake -H$IROHA_HOME -Bbuild -DSWIG_PYTHON=ON -DSUPPORT_PYTHON2=ON; cmake --build build/ --target irohapy -- -j"$(getconf _NPROCESSORS_ONLN)" # generate proto files in current dir -protoc --proto_path=../../schema --python_out=. block.proto primitive.proto commands.proto queries.proto responses.proto endpoint.proto -python -m grpc_tools.protoc --proto_path=../../schema --python_out=. --grpc_python_out=. endpoint.proto yac.proto ordering.proto loader.proto +protoc --proto_path=../../shared_model/schema --python_out=. block.proto primitive.proto commands.proto queries.proto qry_responses.proto endpoint.proto +python -m grpc_tools.protoc --proto_path=../../schema --proto_path=../../shared_model/schema --python_out=. --grpc_python_out=. endpoint.proto yac.proto ordering.proto loader.proto diff --git a/example/python/tx-example.py b/example/python/tx-example.py index 99106a0520..98ec845833 100644 --- a/example/python/tx-example.py +++ b/example/python/tx-example.py @@ -2,7 +2,7 @@ sys.path.insert(0, 'build/shared_model/bindings') import iroha -import block_pb2 +import transaction_pb2 import endpoint_pb2 import endpoint_pb2_grpc import queries_pb2 @@ -82,7 +82,7 @@ def print_status_streaming(tx): def send_tx(tx, key_pair): tx_blob = iroha.ModelProtoTransaction(tx).signAndAddSignature(key_pair).finish().blob() - proto_tx = block_pb2.Transaction() + proto_tx = transaction_pb2.Transaction() if sys.version_info[0] == 2: tmp = ''.join(map(chr, tx_blob)) diff --git a/iroha-cli/query_response_handler.hpp b/iroha-cli/query_response_handler.hpp index 18eb0b1a17..85f4b11510 100644 --- a/iroha-cli/query_response_handler.hpp +++ b/iroha-cli/query_response_handler.hpp @@ -23,7 +23,7 @@ #include #include -#include "responses.pb.h" +#include "qry_responses.pb.h" namespace spdlog { class logger; diff --git a/irohad/model/converters/impl/pb_common.cpp b/irohad/model/converters/impl/pb_common.cpp index de8d3aa88e..80e7bef4ad 100644 --- a/irohad/model/converters/impl/pb_common.cpp +++ b/irohad/model/converters/impl/pb_common.cpp @@ -19,7 +19,7 @@ #include "model/command.hpp" #include "model/commands/all.hpp" #include "model/domain.hpp" -#include "responses.pb.h" +#include "qry_responses.pb.h" namespace iroha { namespace model { diff --git a/irohad/model/converters/pb_common.hpp b/irohad/model/converters/pb_common.hpp index 47baf70f30..2c6ac31f3a 100644 --- a/irohad/model/converters/pb_common.hpp +++ b/irohad/model/converters/pb_common.hpp @@ -28,7 +28,7 @@ #include "model/domain.hpp" #include "model/peer.hpp" #include "model/signature.hpp" -#include "responses.pb.h" +#include "qry_responses.pb.h" namespace iroha { namespace model { diff --git a/irohad/model/converters/pb_query_response_factory.hpp b/irohad/model/converters/pb_query_response_factory.hpp index ccff362b33..8e92a06823 100644 --- a/irohad/model/converters/pb_query_response_factory.hpp +++ b/irohad/model/converters/pb_query_response_factory.hpp @@ -28,7 +28,7 @@ #include "model/queries/responses/roles_response.hpp" #include "model/queries/responses/signatories_response.hpp" #include "model/queries/responses/transactions_response.hpp" -#include "responses.pb.h" +#include "qry_responses.pb.h" namespace iroha { namespace model { diff --git a/irohad/model/converters/pb_transaction_factory.hpp b/irohad/model/converters/pb_transaction_factory.hpp index a4dc32f205..4d1823f523 100644 --- a/irohad/model/converters/pb_transaction_factory.hpp +++ b/irohad/model/converters/pb_transaction_factory.hpp @@ -19,8 +19,8 @@ #define IROHA_PB_TRANSACTION_FACTORY_HPP #include -#include "block.pb.h" #include "model/transaction.hpp" +#include "transaction.pb.h" namespace iroha { namespace model { diff --git a/irohad/ordering/impl/ordering_service_transport_grpc.hpp b/irohad/ordering/impl/ordering_service_transport_grpc.hpp index 5946fb9a1a..b77ca538ec 100644 --- a/irohad/ordering/impl/ordering_service_transport_grpc.hpp +++ b/irohad/ordering/impl/ordering_service_transport_grpc.hpp @@ -19,11 +19,11 @@ #include -#include "block.pb.h" #include "logger/logger.hpp" #include "network/impl/async_grpc_client.hpp" #include "network/ordering_service_transport.hpp" #include "ordering.grpc.pb.h" +#include "transaction.pb.h" namespace iroha { namespace ordering { diff --git a/irohad/torii/command_client.cpp b/irohad/torii/command_client.cpp index 81816c0613..220d85cd8c 100644 --- a/irohad/torii/command_client.cpp +++ b/irohad/torii/command_client.cpp @@ -15,10 +15,10 @@ limitations under the License. #include -#include "block.pb.h" -#include "network/impl/grpc_channel_builder.hpp" #include "common/byteutils.hpp" +#include "network/impl/grpc_channel_builder.hpp" #include "torii/command_client.hpp" +#include "transaction.pb.h" namespace torii { diff --git a/irohad/torii/query_service.hpp b/irohad/torii/query_service.hpp index 4a6cb16c1b..4dab055ea1 100644 --- a/irohad/torii/query_service.hpp +++ b/irohad/torii/query_service.hpp @@ -20,7 +20,7 @@ limitations under the License. #include #include "endpoint.grpc.pb.h" #include "endpoint.pb.h" -#include "responses.pb.h" +#include "qry_responses.pb.h" #include "backend/protobuf/queries/proto_blocks_query.hpp" #include "backend/protobuf/queries/proto_query.hpp" diff --git a/schema/.gitignore b/schema/.gitignore deleted file mode 100644 index ef4ee898b9..0000000000 --- a/schema/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -*.h -*.cc diff --git a/schema/CMakeLists.txt b/schema/CMakeLists.txt index 070f748287..b35bb294cd 100644 --- a/schema/CMakeLists.txt +++ b/schema/CMakeLists.txt @@ -1,97 +1,55 @@ -# -# Copyright Soramitsu Co., Ltd. 2017 All Rights Reserved. -# http://soramitsu.co.jp -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -compile_proto_to_cpp(block.proto) -compile_proto_to_cpp(proposal.proto) -compile_proto_to_cpp(primitive.proto) -compile_proto_to_cpp(commands.proto) -compile_proto_to_cpp(queries.proto) -compile_proto_to_cpp(responses.proto) -compile_proto_to_cpp(endpoint.proto) - -if (IROHA_ROOT_PROJECT) - compile_proto_to_grpc_cpp(endpoint.proto) - compile_proto_to_grpc_cpp(yac.proto) - compile_proto_to_grpc_cpp(ordering.proto) - compile_proto_to_grpc_cpp(loader.proto) - compile_proto_to_grpc_cpp(mst.proto) -endif () - -add_library(schema - block.pb.cc - commands.pb.cc - primitive.pb.cc - queries.pb.cc - responses.pb.cc - endpoint.pb.cc - proposal.pb.cc +# Copyright Soramitsu Co., Ltd. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 + +set(SM_SCHEMA_PATH ${CMAKE_CURRENT_SOURCE_DIR}/../shared_model/schema/) +set(SCHEMA_PATH ${SM_SCHEMA_PATH}) +compile_proto_only_grpc_to_cpp(endpoint.proto "-I${SM_SCHEMA_PATH}") +set(SCHEMA_PATH ${CMAKE_CURRENT_SOURCE_DIR}) +compile_proto_to_grpc_cpp(yac.proto) +compile_proto_to_grpc_cpp(ordering.proto "-I${SM_SCHEMA_PATH}") +compile_proto_to_grpc_cpp(loader.proto "-I${SM_SCHEMA_PATH}") +compile_proto_to_grpc_cpp(mst.proto "-I${SM_SCHEMA_PATH}") + +add_library(endpoint + endpoint.grpc.pb.cc + ) +target_link_libraries(endpoint + grpc++ + schema ) -target_link_libraries(schema - protobuf +add_library(yac_grpc + yac.pb.cc + yac.grpc.pb.cc ) -target_include_directories(schema PUBLIC - ${CMAKE_CURRENT_SOURCE_DIR} +target_link_libraries(yac_grpc + protobuf + grpc++ ) -if (IROHA_ROOT_PROJECT) - add_library(endpoint - endpoint.pb.cc - endpoint.grpc.pb.cc - ) - target_link_libraries(endpoint - protobuf - grpc++ - schema - ) - - add_library(yac_grpc - yac.pb.cc - yac.grpc.pb.cc - ) - target_link_libraries(yac_grpc - protobuf - grpc++ - ) - - add_library(ordering_grpc - ordering.pb.cc - ordering.grpc.pb.cc - ) - target_link_libraries(ordering_grpc - schema - grpc++ - ) +add_library(ordering_grpc + ordering.pb.cc + ordering.grpc.pb.cc + ) +target_link_libraries(ordering_grpc + schema + grpc++ + ) - add_library(loader_grpc - loader.pb.cc - loader.grpc.pb.cc - ) - target_link_libraries(loader_grpc - schema - grpc++ - ) +add_library(loader_grpc + loader.pb.cc + loader.grpc.pb.cc + ) +target_link_libraries(loader_grpc + schema + grpc++ + ) - add_library(mst_grpc - mst.pb.cc - mst.grpc.pb.cc - ) - target_link_libraries(mst_grpc - schema - grpc++ - ) -endif () +add_library(mst_grpc + mst.pb.cc + mst.grpc.pb.cc + ) +target_link_libraries(mst_grpc + schema + grpc++ + ) diff --git a/schema/mst.proto b/schema/mst.proto index 4d721dadc6..5e9f37549d 100644 --- a/schema/mst.proto +++ b/schema/mst.proto @@ -1,7 +1,7 @@ syntax = "proto3"; package iroha.network.transport; -import "block.proto"; +import "transaction.proto"; import "primitive.proto"; import "google/protobuf/empty.proto"; diff --git a/schema/ordering.proto b/schema/ordering.proto index 2c776fcd59..40b97571d4 100644 --- a/schema/ordering.proto +++ b/schema/ordering.proto @@ -1,7 +1,7 @@ syntax = "proto3"; package iroha.ordering.proto; -import "block.proto"; +import "transaction.proto"; import "proposal.proto"; import "google/protobuf/empty.proto"; diff --git a/shared_model/CMakeLists.txt b/shared_model/CMakeLists.txt index a3eb447f6a..8e42c8ff17 100644 --- a/shared_model/CMakeLists.txt +++ b/shared_model/CMakeLists.txt @@ -1,32 +1,20 @@ -# Copyright 2018 Soramitsu Co., Ltd. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. +# Copyright Soramitsu Co., Ltd. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 cmake_minimum_required(VERSION 3.5.1) project(shared_model C CXX) -set(IROHA_SCHEMA_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../schema") +set(IROHA_SCHEMA_DIR "${CMAKE_CURRENT_SOURCE_DIR}/schema") include_directories( ${CMAKE_CURRENT_SOURCE_DIR}/../libs - ${IROHA_SCHEMA_DIR} ${CMAKE_CURRENT_SOURCE_DIR} ) if (NOT IROHA_ROOT_PROJECT) set(CMAKE_POSITION_INDEPENDENT_CODE TRUE) if (NOT MSVC) - set(CMAKE_CXX_FLAGS "-std=c++14 -Wall") + set(CMAKE_CXX_FLAGS "-std=c++14 -Wall -fdiagnostics-color=always") set(CMAKE_CXX_FLAGS_RELEASE "-O3") set(CMAKE_CXX_FLAGS_DEBUG "-g -Wextra -Wno-unused-parameter -Wno-deprecated-declarations -O0") endif () @@ -44,8 +32,8 @@ if (NOT IROHA_ROOT_PROJECT) include(FeatureSummary) include(${CMAKE_CURRENT_SOURCE_DIR}/../cmake/functions.cmake) include(cmake/dependencies.cmake) + set(SCHEMA_OUT_DIR ${CMAKE_BINARY_DIR}/schema) - add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../schema schema) add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../libs/generator generator) add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../libs/amount amount) endif () @@ -58,6 +46,7 @@ add_subdirectory(cryptography) add_subdirectory(interfaces) add_subdirectory(utils) add_subdirectory(validators) +add_subdirectory(schema) if (NOT IROHA_ROOT_PROJECT) if (TESTING) diff --git a/shared_model/backend/protobuf/batch_meta.hpp b/shared_model/backend/protobuf/batch_meta.hpp index 707f7789ff..a1c1df5e59 100644 --- a/shared_model/backend/protobuf/batch_meta.hpp +++ b/shared_model/backend/protobuf/batch_meta.hpp @@ -11,9 +11,9 @@ #include "backend/protobuf/common_objects/trivial_proto.hpp" #include "backend/protobuf/util.hpp" -#include "block.pb.h" #include "interfaces/common_objects/amount.hpp" #include "interfaces/common_objects/types.hpp" +#include "transaction.pb.h" #include "utils/lazy_initializer.hpp" namespace shared_model { diff --git a/shared_model/backend/protobuf/commands/proto_add_peer.hpp b/shared_model/backend/protobuf/commands/proto_add_peer.hpp index 8e03adf5e7..054f82171a 100644 --- a/shared_model/backend/protobuf/commands/proto_add_peer.hpp +++ b/shared_model/backend/protobuf/commands/proto_add_peer.hpp @@ -19,6 +19,7 @@ #define IROHA_PROTO_ADD_PEER_HPP #include "backend/protobuf/common_objects/peer.hpp" +#include "commands.pb.h" #include "interfaces/commands/add_peer.hpp" #include "interfaces/common_objects/peer.hpp" diff --git a/shared_model/backend/protobuf/common_objects/account.hpp b/shared_model/backend/protobuf/common_objects/account.hpp index 5a2a27b2e7..296b0acdee 100644 --- a/shared_model/backend/protobuf/common_objects/account.hpp +++ b/shared_model/backend/protobuf/common_objects/account.hpp @@ -21,7 +21,7 @@ #include "backend/protobuf/common_objects/trivial_proto.hpp" #include "backend/protobuf/util.hpp" #include "interfaces/common_objects/account.hpp" -#include "responses.pb.h" +#include "qry_responses.pb.h" #include "utils/lazy_initializer.hpp" namespace shared_model { diff --git a/shared_model/backend/protobuf/common_objects/account_asset.hpp b/shared_model/backend/protobuf/common_objects/account_asset.hpp index 43b0e8e4a3..ac23d93c80 100644 --- a/shared_model/backend/protobuf/common_objects/account_asset.hpp +++ b/shared_model/backend/protobuf/common_objects/account_asset.hpp @@ -22,7 +22,7 @@ #include "backend/protobuf/common_objects/trivial_proto.hpp" #include "backend/protobuf/util.hpp" #include "interfaces/common_objects/account_asset.hpp" -#include "responses.pb.h" +#include "qry_responses.pb.h" #include "utils/lazy_initializer.hpp" namespace shared_model { diff --git a/shared_model/backend/protobuf/common_objects/asset.hpp b/shared_model/backend/protobuf/common_objects/asset.hpp index 16ffd72bbe..2ee32f941d 100644 --- a/shared_model/backend/protobuf/common_objects/asset.hpp +++ b/shared_model/backend/protobuf/common_objects/asset.hpp @@ -21,7 +21,7 @@ #include "backend/protobuf/common_objects/trivial_proto.hpp" #include "backend/protobuf/util.hpp" #include "interfaces/common_objects/asset.hpp" -#include "responses.pb.h" +#include "qry_responses.pb.h" #include "utils/lazy_initializer.hpp" namespace shared_model { diff --git a/shared_model/backend/protobuf/common_objects/domain.hpp b/shared_model/backend/protobuf/common_objects/domain.hpp index 7e0ecfb2ce..24bdb71b3b 100644 --- a/shared_model/backend/protobuf/common_objects/domain.hpp +++ b/shared_model/backend/protobuf/common_objects/domain.hpp @@ -20,7 +20,7 @@ #include "backend/protobuf/common_objects/trivial_proto.hpp" #include "interfaces/common_objects/domain.hpp" -#include "responses.pb.h" +#include "qry_responses.pb.h" #include "utils/lazy_initializer.hpp" namespace shared_model { diff --git a/shared_model/backend/protobuf/common_objects/peer.hpp b/shared_model/backend/protobuf/common_objects/peer.hpp index 29bc72b2e1..c4cb3a0b1e 100644 --- a/shared_model/backend/protobuf/common_objects/peer.hpp +++ b/shared_model/backend/protobuf/common_objects/peer.hpp @@ -21,7 +21,7 @@ #include "backend/protobuf/common_objects/trivial_proto.hpp" #include "backend/protobuf/util.hpp" #include "interfaces/common_objects/peer.hpp" -#include "responses.pb.h" +#include "primitive.pb.h" #include "utils/lazy_initializer.hpp" namespace shared_model { diff --git a/shared_model/backend/protobuf/query_responses/proto_account_asset_response.hpp b/shared_model/backend/protobuf/query_responses/proto_account_asset_response.hpp index 217a5d7050..5b072afcaf 100644 --- a/shared_model/backend/protobuf/query_responses/proto_account_asset_response.hpp +++ b/shared_model/backend/protobuf/query_responses/proto_account_asset_response.hpp @@ -24,10 +24,9 @@ #include "backend/protobuf/common_objects/trivial_proto.hpp" #include "interfaces/common_objects/types.hpp" #include "interfaces/query_responses/account_asset_response.hpp" -#include "responses.pb.h" +#include "qry_responses.pb.h" #include "utils/lazy_initializer.hpp" - namespace shared_model { namespace proto { class AccountAssetResponse final @@ -42,7 +41,8 @@ namespace shared_model { AccountAssetResponse(AccountAssetResponse &&o); - const interface::types::AccountAssetCollectionType accountAssets() const override; + const interface::types::AccountAssetCollectionType accountAssets() + const override; private: template diff --git a/shared_model/backend/protobuf/query_responses/proto_account_detail_response.hpp b/shared_model/backend/protobuf/query_responses/proto_account_detail_response.hpp index f6df0be1d5..fdb2d5850f 100644 --- a/shared_model/backend/protobuf/query_responses/proto_account_detail_response.hpp +++ b/shared_model/backend/protobuf/query_responses/proto_account_detail_response.hpp @@ -21,7 +21,7 @@ #include "backend/protobuf/common_objects/account_asset.hpp" #include "backend/protobuf/common_objects/trivial_proto.hpp" #include "interfaces/query_responses/account_detail_response.hpp" -#include "responses.pb.h" +#include "qry_responses.pb.h" #include "utils/lazy_initializer.hpp" namespace shared_model { diff --git a/shared_model/backend/protobuf/query_responses/proto_account_response.hpp b/shared_model/backend/protobuf/query_responses/proto_account_response.hpp index a6be1d2f21..00c83cb42e 100644 --- a/shared_model/backend/protobuf/query_responses/proto_account_response.hpp +++ b/shared_model/backend/protobuf/query_responses/proto_account_response.hpp @@ -23,7 +23,7 @@ #include "backend/protobuf/common_objects/account.hpp" #include "backend/protobuf/common_objects/trivial_proto.hpp" #include "interfaces/query_responses/account_response.hpp" -#include "responses.pb.h" +#include "qry_responses.pb.h" #include "utils/lazy_initializer.hpp" namespace shared_model { diff --git a/shared_model/backend/protobuf/query_responses/proto_asset_response.hpp b/shared_model/backend/protobuf/query_responses/proto_asset_response.hpp index 695d61a82c..0c50efab44 100644 --- a/shared_model/backend/protobuf/query_responses/proto_asset_response.hpp +++ b/shared_model/backend/protobuf/query_responses/proto_asset_response.hpp @@ -21,7 +21,7 @@ #include "backend/protobuf/common_objects/asset.hpp" #include "backend/protobuf/common_objects/trivial_proto.hpp" #include "interfaces/query_responses/asset_response.hpp" -#include "responses.pb.h" +#include "qry_responses.pb.h" #include "utils/lazy_initializer.hpp" namespace shared_model { diff --git a/shared_model/backend/protobuf/query_responses/proto_block_error_response.hpp b/shared_model/backend/protobuf/query_responses/proto_block_error_response.hpp index 79b7096864..0d9322d9e9 100644 --- a/shared_model/backend/protobuf/query_responses/proto_block_error_response.hpp +++ b/shared_model/backend/protobuf/query_responses/proto_block_error_response.hpp @@ -8,7 +8,7 @@ #include "backend/protobuf/common_objects/trivial_proto.hpp" #include "interfaces/query_responses/block_error_response.hpp" -#include "responses.pb.h" +#include "qry_responses.pb.h" #include "utils/lazy_initializer.hpp" namespace shared_model { @@ -25,7 +25,7 @@ namespace shared_model { BlockErrorResponse(BlockErrorResponse &&o); - const interface::types::DescriptionType &message() const override ; + const interface::types::DescriptionType &message() const override; private: template diff --git a/shared_model/backend/protobuf/query_responses/proto_block_query_response.hpp b/shared_model/backend/protobuf/query_responses/proto_block_query_response.hpp index 95b6d0327d..079c0073c6 100644 --- a/shared_model/backend/protobuf/query_responses/proto_block_query_response.hpp +++ b/shared_model/backend/protobuf/query_responses/proto_block_query_response.hpp @@ -12,7 +12,7 @@ #include "backend/protobuf/common_objects/trivial_proto.hpp" #include "interfaces/queries/query.hpp" #include "interfaces/query_responses/block_query_response.hpp" -#include "responses.pb.h" +#include "qry_responses.pb.h" #include "utils/lazy_initializer.hpp" #include "utils/variant_deserializer.hpp" diff --git a/shared_model/backend/protobuf/query_responses/proto_block_response.hpp b/shared_model/backend/protobuf/query_responses/proto_block_response.hpp index 7b25631793..42e2eef472 100644 --- a/shared_model/backend/protobuf/query_responses/proto_block_response.hpp +++ b/shared_model/backend/protobuf/query_responses/proto_block_response.hpp @@ -9,7 +9,7 @@ #include "backend/protobuf/block.hpp" #include "backend/protobuf/common_objects/trivial_proto.hpp" #include "interfaces/query_responses/block_response.hpp" -#include "responses.pb.h" +#include "qry_responses.pb.h" #include "utils/lazy_initializer.hpp" namespace shared_model { diff --git a/shared_model/backend/protobuf/query_responses/proto_concrete_error_query_response.hpp b/shared_model/backend/protobuf/query_responses/proto_concrete_error_query_response.hpp index e6b31f7029..171e2ac269 100644 --- a/shared_model/backend/protobuf/query_responses/proto_concrete_error_query_response.hpp +++ b/shared_model/backend/protobuf/query_responses/proto_concrete_error_query_response.hpp @@ -28,7 +28,7 @@ #include "interfaces/query_responses/error_responses/not_supported_error_response.hpp" #include "interfaces/query_responses/error_responses/stateful_failed_error_response.hpp" #include "interfaces/query_responses/error_responses/stateless_failed_error_response.hpp" -#include "responses.pb.h" +#include "qry_responses.pb.h" namespace shared_model { namespace proto { diff --git a/shared_model/backend/protobuf/query_responses/proto_error_query_response.hpp b/shared_model/backend/protobuf/query_responses/proto_error_query_response.hpp index 1526d98048..b43d8389ec 100644 --- a/shared_model/backend/protobuf/query_responses/proto_error_query_response.hpp +++ b/shared_model/backend/protobuf/query_responses/proto_error_query_response.hpp @@ -21,7 +21,7 @@ #include "backend/protobuf/common_objects/trivial_proto.hpp" #include "backend/protobuf/query_responses/proto_concrete_error_query_response.hpp" #include "interfaces/query_responses/error_query_response.hpp" -#include "responses.pb.h" +#include "qry_responses.pb.h" #include "utils/lazy_initializer.hpp" namespace shared_model { diff --git a/shared_model/backend/protobuf/query_responses/proto_query_response.hpp b/shared_model/backend/protobuf/query_responses/proto_query_response.hpp index eb79b93c0e..185931ca9e 100644 --- a/shared_model/backend/protobuf/query_responses/proto_query_response.hpp +++ b/shared_model/backend/protobuf/query_responses/proto_query_response.hpp @@ -30,7 +30,7 @@ #include "backend/protobuf/common_objects/trivial_proto.hpp" #include "interfaces/query_responses/query_response.hpp" -#include "responses.pb.h" +#include "qry_responses.pb.h" #include "utils/lazy_initializer.hpp" namespace shared_model { diff --git a/shared_model/backend/protobuf/query_responses/proto_role_permissions_response.hpp b/shared_model/backend/protobuf/query_responses/proto_role_permissions_response.hpp index e6d075d6e1..d5fc704a66 100644 --- a/shared_model/backend/protobuf/query_responses/proto_role_permissions_response.hpp +++ b/shared_model/backend/protobuf/query_responses/proto_role_permissions_response.hpp @@ -20,7 +20,7 @@ #include "backend/protobuf/common_objects/trivial_proto.hpp" #include "interfaces/query_responses/role_permissions.hpp" -#include "responses.pb.h" +#include "qry_responses.pb.h" #include "utils/lazy_initializer.hpp" namespace shared_model { diff --git a/shared_model/backend/protobuf/query_responses/proto_roles_response.hpp b/shared_model/backend/protobuf/query_responses/proto_roles_response.hpp index f141e1fa3f..d78d1c1511 100644 --- a/shared_model/backend/protobuf/query_responses/proto_roles_response.hpp +++ b/shared_model/backend/protobuf/query_responses/proto_roles_response.hpp @@ -20,7 +20,7 @@ #include "backend/protobuf/common_objects/trivial_proto.hpp" #include "interfaces/query_responses/roles_response.hpp" -#include "responses.pb.h" +#include "qry_responses.pb.h" #include "utils/lazy_initializer.hpp" namespace shared_model { diff --git a/shared_model/backend/protobuf/query_responses/proto_signatories_response.hpp b/shared_model/backend/protobuf/query_responses/proto_signatories_response.hpp index 31eb7cb320..cf5beb68f9 100644 --- a/shared_model/backend/protobuf/query_responses/proto_signatories_response.hpp +++ b/shared_model/backend/protobuf/query_responses/proto_signatories_response.hpp @@ -21,7 +21,7 @@ #include "interfaces/query_responses/signatories_response.hpp" #include "backend/protobuf/common_objects/trivial_proto.hpp" -#include "responses.pb.h" +#include "qry_responses.pb.h" #include "utils/lazy_initializer.hpp" namespace shared_model { diff --git a/shared_model/backend/protobuf/query_responses/proto_transaction_response.hpp b/shared_model/backend/protobuf/query_responses/proto_transaction_response.hpp index d2535f2923..25f26b4740 100644 --- a/shared_model/backend/protobuf/query_responses/proto_transaction_response.hpp +++ b/shared_model/backend/protobuf/query_responses/proto_transaction_response.hpp @@ -22,7 +22,7 @@ #include "backend/protobuf/transaction.hpp" #include "interfaces/common_objects/types.hpp" #include "interfaces/query_responses/transactions_response.hpp" -#include "responses.pb.h" +#include "qry_responses.pb.h" #include "utils/lazy_initializer.hpp" namespace shared_model { diff --git a/shared_model/backend/protobuf/transaction.hpp b/shared_model/backend/protobuf/transaction.hpp index 623d4c00b9..242dd8d2e3 100644 --- a/shared_model/backend/protobuf/transaction.hpp +++ b/shared_model/backend/protobuf/transaction.hpp @@ -25,7 +25,6 @@ #include "backend/protobuf/commands/proto_command.hpp" #include "backend/protobuf/common_objects/signature.hpp" #include "batch_meta.hpp" -#include "block.pb.h" #include "utils/lazy_initializer.hpp" namespace shared_model { @@ -125,7 +124,7 @@ namespace shared_model { [this] { return makeBlob(reduced_payload_); }}; const Lazy>> meta_{ - [this] () -> boost::optional> { + [this]() -> boost::optional> { if (payload_.has_batch()) { std::shared_ptr b = std::make_shared(payload_.batch()); diff --git a/shared_model/builders/protobuf/builder_templates/query_response_template.hpp b/shared_model/builders/protobuf/builder_templates/query_response_template.hpp index ebb100653a..3b7bee9815 100644 --- a/shared_model/builders/protobuf/builder_templates/query_response_template.hpp +++ b/shared_model/builders/protobuf/builder_templates/query_response_template.hpp @@ -12,7 +12,7 @@ #include "common/visitor.hpp" #include "interfaces/common_objects/types.hpp" #include "interfaces/permissions.hpp" -#include "responses.pb.h" +#include "qry_responses.pb.h" namespace shared_model { namespace proto { diff --git a/shared_model/builders/protobuf/builder_templates/transaction_template.hpp b/shared_model/builders/protobuf/builder_templates/transaction_template.hpp index c36e6e6d11..c916543f99 100644 --- a/shared_model/builders/protobuf/builder_templates/transaction_template.hpp +++ b/shared_model/builders/protobuf/builder_templates/transaction_template.hpp @@ -22,9 +22,9 @@ #include -#include "block.pb.h" #include "commands.pb.h" #include "primitive.pb.h" +#include "transaction.pb.h" #include "amount/amount.hpp" #include "backend/protobuf/permissions.hpp" diff --git a/shared_model/builders/protobuf/common_objects/proto_account_asset_builder.hpp b/shared_model/builders/protobuf/common_objects/proto_account_asset_builder.hpp index d3d45871c1..7c88bed648 100644 --- a/shared_model/builders/protobuf/common_objects/proto_account_asset_builder.hpp +++ b/shared_model/builders/protobuf/common_objects/proto_account_asset_builder.hpp @@ -19,7 +19,7 @@ #define IROHA_PROTO_ACCOUNT_ASSET_BUILDER_HPP #include "backend/protobuf/common_objects/account_asset.hpp" -#include "responses.pb.h" +#include "qry_responses.pb.h" namespace shared_model { namespace proto { diff --git a/shared_model/builders/protobuf/common_objects/proto_account_builder.hpp b/shared_model/builders/protobuf/common_objects/proto_account_builder.hpp index ea1e9122eb..0e64308342 100644 --- a/shared_model/builders/protobuf/common_objects/proto_account_builder.hpp +++ b/shared_model/builders/protobuf/common_objects/proto_account_builder.hpp @@ -19,7 +19,7 @@ #define IROHA_PROTO_ACCOUNT_BUILDER_HPP #include "backend/protobuf/common_objects/account.hpp" -#include "responses.pb.h" +#include "qry_responses.pb.h" namespace shared_model { namespace proto { diff --git a/shared_model/builders/protobuf/common_objects/proto_asset_builder.hpp b/shared_model/builders/protobuf/common_objects/proto_asset_builder.hpp index f0c7bd71e6..c0123aeb8b 100644 --- a/shared_model/builders/protobuf/common_objects/proto_asset_builder.hpp +++ b/shared_model/builders/protobuf/common_objects/proto_asset_builder.hpp @@ -19,7 +19,7 @@ #define IROHA_PROTO_ASSET_BUILDER_HPP #include "backend/protobuf/common_objects/asset.hpp" -#include "responses.pb.h" +#include "qry_responses.pb.h" namespace shared_model { namespace proto { diff --git a/shared_model/builders/protobuf/common_objects/proto_domain_builder.hpp b/shared_model/builders/protobuf/common_objects/proto_domain_builder.hpp index 55b1ec6a97..3eab6c5878 100644 --- a/shared_model/builders/protobuf/common_objects/proto_domain_builder.hpp +++ b/shared_model/builders/protobuf/common_objects/proto_domain_builder.hpp @@ -19,7 +19,7 @@ #define IROHA_PROTO_DOMAIN_BUILDER_HPP #include "backend/protobuf/common_objects/domain.hpp" -#include "responses.pb.h" +#include "qry_responses.pb.h" namespace shared_model { namespace proto { diff --git a/shared_model/builders/protobuf/transaction.hpp b/shared_model/builders/protobuf/transaction.hpp index 5330db7576..77d2ae7134 100644 --- a/shared_model/builders/protobuf/transaction.hpp +++ b/shared_model/builders/protobuf/transaction.hpp @@ -18,19 +18,6 @@ #ifndef IROHA_PROTO_TRANSACTION_BUILDER_HPP #define IROHA_PROTO_TRANSACTION_BUILDER_HPP -#include "backend/protobuf/transaction.hpp" - -#include - -#include "block.pb.h" -#include "commands.pb.h" -#include "primitive.pb.h" - -#include "amount/amount.hpp" -#include "builders/protobuf/helpers.hpp" -#include "builders/protobuf/unsigned_proto.hpp" -#include "interfaces/common_objects/types.hpp" -#include "validators/default_validator.hpp" #include "builders/protobuf/builder_templates/transaction_template.hpp" namespace shared_model { diff --git a/shared_model/clean.sh b/shared_model/clean.sh index e0e2b76a27..a3615ae37a 100755 --- a/shared_model/clean.sh +++ b/shared_model/clean.sh @@ -1,4 +1,3 @@ #!/bin/bash -rm ../schema/*.{cc,h} rm -rf external rm -rf build diff --git a/shared_model/interfaces/CMakeLists.txt b/shared_model/interfaces/CMakeLists.txt index 4e435dd301..0b57c56ef4 100644 --- a/shared_model/interfaces/CMakeLists.txt +++ b/shared_model/interfaces/CMakeLists.txt @@ -60,5 +60,6 @@ endif () target_link_libraries(shared_model_interfaces shared_model_cryptography + schema ${Boost_LIBRARIES} ) diff --git a/shared_model/packages/javascript/binding.gyp b/shared_model/packages/javascript/binding.gyp index a157f52f4a..e8b0af2989 100644 --- a/shared_model/packages/javascript/binding.gyp +++ b/shared_model/packages/javascript/binding.gyp @@ -77,9 +77,9 @@ 'dependencies': [ 'shared_model' ], 'include_dirs': [ '<(iroha_lib_dir)', + '<(iroha_lib_dir)/schema', # TODO: Remove these include directories when Shared Model # will be completely separated from Iroha - '<(iroha_lib_dir)/../schema', '<(iroha_lib_dir)/../libs' ], 'sources': [ diff --git a/shared_model/schema/CMakeLists.txt b/shared_model/schema/CMakeLists.txt new file mode 100644 index 0000000000..bdfde1c457 --- /dev/null +++ b/shared_model/schema/CMakeLists.txt @@ -0,0 +1,29 @@ +# Copyright Soramitsu Co., Ltd. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 + +set(SCHEMA_PATH ${CMAKE_CURRENT_SOURCE_DIR}) +compile_proto_to_cpp(block.proto) +compile_proto_to_cpp(transaction.proto) +compile_proto_to_cpp(commands.proto) +compile_proto_to_cpp(primitive.proto) +compile_proto_to_cpp(proposal.proto) +compile_proto_to_cpp(qry_responses.proto) +compile_proto_to_cpp(queries.proto) +compile_proto_to_cpp(endpoint.proto) + +add_library(schema + ${SCHEMA_OUT_DIR}/block.pb.cc + ${SCHEMA_OUT_DIR}/transaction.pb.cc + ${SCHEMA_OUT_DIR}/commands.pb.cc + ${SCHEMA_OUT_DIR}/primitive.pb.cc + ${SCHEMA_OUT_DIR}/proposal.pb.cc + ${SCHEMA_OUT_DIR}/qry_responses.pb.cc + ${SCHEMA_OUT_DIR}/queries.pb.cc + ${SCHEMA_OUT_DIR}/endpoint.pb.cc) + +target_link_libraries(schema + protobuf + ) +target_include_directories(schema PUBLIC + ${SCHEMA_OUT_DIR} + ) diff --git a/shared_model/schema/block.proto b/shared_model/schema/block.proto new file mode 100644 index 0000000000..8f10455e69 --- /dev/null +++ b/shared_model/schema/block.proto @@ -0,0 +1,23 @@ +/** + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +syntax = "proto3"; +package iroha.protocol; +import "primitive.proto"; +import "transaction.proto"; + +message Block { + // everything that should be signed: + message Payload { + repeated Transaction transactions = 1; + uint32 tx_number = 2; // the number of transactions inside. Maximum 16384 or 2^14 + uint64 height = 3; // the current block number in a ledger + bytes prev_block_hash = 5; // Previous block hash + uint64 created_time = 6; + } + + Payload payload = 1; + repeated Signature signatures = 2; +} diff --git a/schema/commands.proto b/shared_model/schema/commands.proto similarity index 95% rename from schema/commands.proto rename to shared_model/schema/commands.proto index ec854a548c..f8ebcd93cd 100644 --- a/schema/commands.proto +++ b/shared_model/schema/commands.proto @@ -1,3 +1,8 @@ +/** + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + syntax = "proto3"; package iroha.protocol; import "primitive.proto"; diff --git a/schema/endpoint.proto b/shared_model/schema/endpoint.proto similarity index 83% rename from schema/endpoint.proto rename to shared_model/schema/endpoint.proto index da522c9550..38a1abfa56 100644 --- a/schema/endpoint.proto +++ b/shared_model/schema/endpoint.proto @@ -1,12 +1,16 @@ +/** + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + syntax = "proto3"; package iroha.protocol; -import "block.proto"; +import "transaction.proto"; import "queries.proto"; +import "qry_responses.proto"; import "google/protobuf/empty.proto"; -import "responses.proto"; - enum TxStatus { STATELESS_VALIDATION_FAILED = 0; @@ -34,7 +38,6 @@ service CommandService { rpc StatusStream(TxStatusRequest) returns (stream ToriiResponse); } - service QueryService { rpc Find (Query) returns (QueryResponse); rpc FetchCommits (BlocksQuery) returns (stream BlockQueryResponse); diff --git a/schema/primitive.proto b/shared_model/schema/primitive.proto similarity index 95% rename from schema/primitive.proto rename to shared_model/schema/primitive.proto index e930eb7516..3dd2ffe6c1 100644 --- a/schema/primitive.proto +++ b/shared_model/schema/primitive.proto @@ -1,3 +1,8 @@ +/** + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + /** * Messages related to primitive types, used in Commands and Queries * diff --git a/schema/proposal.proto b/shared_model/schema/proposal.proto similarity index 57% rename from schema/proposal.proto rename to shared_model/schema/proposal.proto index e31c9d951e..b0f4d6e33e 100644 --- a/schema/proposal.proto +++ b/shared_model/schema/proposal.proto @@ -1,7 +1,12 @@ +/** + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + syntax = "proto3"; package iroha.protocol; -import "block.proto"; +import "transaction.proto"; message Proposal { uint64 height = 1; diff --git a/schema/responses.proto b/shared_model/schema/qry_responses.proto similarity index 94% rename from schema/responses.proto rename to shared_model/schema/qry_responses.proto index d50d5f8393..af70fc8205 100644 --- a/schema/responses.proto +++ b/shared_model/schema/qry_responses.proto @@ -1,6 +1,12 @@ +/** + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + syntax = "proto3"; package iroha.protocol; import "block.proto"; +import "transaction.proto"; import "primitive.proto"; // *** WSV data structure *** // diff --git a/schema/queries.proto b/shared_model/schema/queries.proto similarity index 93% rename from schema/queries.proto rename to shared_model/schema/queries.proto index 437816d95b..2db0ea4143 100644 --- a/schema/queries.proto +++ b/shared_model/schema/queries.proto @@ -1,3 +1,8 @@ +/** + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + syntax = "proto3"; package iroha.protocol; diff --git a/schema/block.proto b/shared_model/schema/transaction.proto similarity index 55% rename from schema/block.proto rename to shared_model/schema/transaction.proto index c66a73feef..53cd095af0 100644 --- a/schema/block.proto +++ b/shared_model/schema/transaction.proto @@ -1,12 +1,13 @@ +/** + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + syntax = "proto3"; package iroha.protocol; import "commands.proto"; import "primitive.proto"; -message Header { - uint64 created_time = 1; - repeated Signature signatures = 2; -} message Transaction { message Payload { message BatchMeta{ @@ -32,17 +33,3 @@ message Transaction { Payload payload = 1; repeated Signature signatures = 2; } - -message Block { - // everything that should be signed: - message Payload { - repeated Transaction transactions = 1; - uint32 tx_number = 2; // the number of transactions inside. Maximum 16384 or 2^14 - uint64 height = 3; // the current block number in a ledger - bytes prev_block_hash = 5; // Previous block hash - uint64 created_time = 6; - } - - Payload payload = 1; - repeated Signature signatures = 2; -} diff --git a/test/integration/acceptance/invalid_fields_test.cpp b/test/integration/acceptance/invalid_fields_test.cpp index 69f5ceb5b2..7c490e1bc6 100644 --- a/test/integration/acceptance/invalid_fields_test.cpp +++ b/test/integration/acceptance/invalid_fields_test.cpp @@ -4,9 +4,9 @@ */ #include -#include "block.pb.h" #include "framework/integration_framework/integration_test_framework.hpp" #include "integration/acceptance/acceptance_fixture.hpp" +#include "transaction.pb.h" using namespace integration_framework; using namespace shared_model; diff --git a/test/module/shared_model/backend_proto/CMakeLists.txt b/test/module/shared_model/backend_proto/CMakeLists.txt index bc1675d33e..2f2b00525e 100644 --- a/test/module/shared_model/backend_proto/CMakeLists.txt +++ b/test/module/shared_model/backend_proto/CMakeLists.txt @@ -47,14 +47,16 @@ target_link_libraries(shared_proto_queries_test shared_model_stateless_validation ) -addtest(shared_proto_query_responses_test - shared_proto_query_responses_test.cpp - ) -target_link_libraries(shared_proto_query_responses_test - shared_model_proto_backend - shared_model_stateless_validation - shared_model_cryptography - ) +if (IROHA_ROOT_PROJECT) + addtest(shared_proto_query_responses_test + shared_proto_query_responses_test.cpp + ) + target_link_libraries(shared_proto_query_responses_test + shared_model_proto_backend + shared_model_stateless_validation + shared_model_cryptography + ) +endif () addtest(shared_proto_util_test shared_proto_util_test.cpp diff --git a/test/module/shared_model/backend_proto/shared_proto_util_test.cpp b/test/module/shared_model/backend_proto/shared_proto_util_test.cpp index 206530f6c1..00f8d60cd8 100644 --- a/test/module/shared_model/backend_proto/shared_proto_util_test.cpp +++ b/test/module/shared_model/backend_proto/shared_proto_util_test.cpp @@ -16,7 +16,7 @@ */ #include "backend/protobuf/util.hpp" -#include "block.pb.h" +#include "commands.pb.h" #include @@ -30,10 +30,10 @@ using shared_model::crypto::toBinaryString; * @then make sure that the deserialized from string is the same */ TEST(UtilTest, StringFromMakeBlob) { - protocol::Header base, deserialized; - base.set_created_time(100); + protocol::SetAccountQuorum base, deserialized; + base.set_quorum(100); auto blob = makeBlob(base); ASSERT_TRUE(deserialized.ParseFromString(toBinaryString(blob))); - ASSERT_EQ(deserialized.created_time(), base.created_time()); + ASSERT_EQ(deserialized.quorum(), base.quorum()); } diff --git a/test/module/shared_model/bindings/BuilderTest.java b/test/module/shared_model/bindings/BuilderTest.java index 4f6fcb8f08..8d40d9dec4 100644 --- a/test/module/shared_model/bindings/BuilderTest.java +++ b/test/module/shared_model/bindings/BuilderTest.java @@ -10,7 +10,7 @@ import java.math.BigInteger; import iroha.protocol.Commands; -import iroha.protocol.BlockOuterClass; +import iroha.protocol.TransactionOuterClass; import com.google.protobuf.InvalidProtocolBufferException; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -150,7 +150,7 @@ private boolean checkProtoTx(Blob serialized) { } try { - BlockOuterClass.Transaction.parseFrom(bs); + TransactionOuterClass.Transaction.parseFrom(bs); } catch (InvalidProtocolBufferException e) { System.out.print("Exception: "); System.out.println(e.getMessage()); diff --git a/test/module/shared_model/bindings/CMakeLists.txt b/test/module/shared_model/bindings/CMakeLists.txt index c57d737f58..e533e6b756 100644 --- a/test/module/shared_model/bindings/CMakeLists.txt +++ b/test/module/shared_model/bindings/CMakeLists.txt @@ -66,12 +66,12 @@ if (SWIG_PYTHON) ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/blocks-query-test.py WORKING_DIRECTORY ${SWIG_BUILD_DIR}) - foreach(item "block" "commands" "primitive" "queries") + foreach(item "block" "transaction" "commands" "primitive" "queries") compile_proto_to_python("${item}.proto") list(APPEND PROTO_SWIG_DEPS "${SWIG_BUILD_DIR}/${item}_pb2.py") endforeach(item) - add_custom_target(python_tests + add_custom_target(python_tests ALL DEPENDS "${PROTO_SWIG_DEPS}") foreach(test "python_transaction_test" "python_query_test" "python_blocks_query_test") set_tests_properties(${test} @@ -99,7 +99,7 @@ if (SWIG_JAVA) add_test(NAME java_builders_test COMMAND gradle test "-PSWIG_BUILD_DIR=${SWIG_BUILD_DIR}" - "-PSCHEMA_DIR=${IROHA_SCHEMA_DIR}" + "-PSCHEMA_DIR=${PROJECT_SOURCE_DIR}/shared_model/schema" "-PPROTOBUF_PROTOC_EXECUTABLE=${PROTOC_EXEC}" "-PPATH_DIRS=$ENV{PATH}${SEPARATOR}${SWIG_LIB_DIR}" WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) diff --git a/test/module/shared_model/bindings/builder-test.py b/test/module/shared_model/bindings/builder-test.py index 9101678045..bad443bff3 100644 --- a/test/module/shared_model/bindings/builder-test.py +++ b/test/module/shared_model/bindings/builder-test.py @@ -9,7 +9,7 @@ import sys from google.protobuf.message import DecodeError -import block_pb2 as blk +import transaction_pb2 as trx # TODO luckychess 8.08.2018 add test for number of methods # in interface and proto implementation IR-1080 @@ -121,7 +121,7 @@ def check_proto_tx(self, blob): tmp = ''.join(map(chr, blob.blob())) else: tmp = bytes(blob.blob()) - blk.Transaction.FromString(tmp) + trx.Transaction.FromString(tmp) except DecodeError as e: print(e) return False diff --git a/test/module/shared_model/builders/common_objects/CMakeLists.txt b/test/module/shared_model/builders/common_objects/CMakeLists.txt index dd24ae2349..f656480ed2 100644 --- a/test/module/shared_model/builders/common_objects/CMakeLists.txt +++ b/test/module/shared_model/builders/common_objects/CMakeLists.txt @@ -66,10 +66,11 @@ target_link_libraries(account_asset_builder_test shared_model_stateless_validation ) -addtest(query_response_builder_test - query_response_builder_test.cpp - ) - -target_link_libraries(query_response_builder_test - shared_model_proto_builders - ) +if (IROHA_ROOT_PROJECT) + addtest(query_response_builder_test + query_response_builder_test.cpp + ) + target_link_libraries(query_response_builder_test + shared_model_proto_builders + ) +endif () diff --git a/test/module/shared_model/builders/protobuf/CMakeLists.txt b/test/module/shared_model/builders/protobuf/CMakeLists.txt index cf25e2865a..4a4314e8d4 100644 --- a/test/module/shared_model/builders/protobuf/CMakeLists.txt +++ b/test/module/shared_model/builders/protobuf/CMakeLists.txt @@ -12,13 +12,15 @@ # See the License for the specific language governing permissions and # limitations under the License. -addtest(transport_builder_test - transport_builder_test.cpp - ) -target_link_libraries(transport_builder_test - shared_model_proto_builders - shared_model_stateless_validation - ) +if (IROHA_ROOT_PROJECT) + addtest(transport_builder_test + transport_builder_test.cpp + ) + target_link_libraries(transport_builder_test + shared_model_proto_builders + shared_model_stateless_validation + ) +endif () addtest(proto_peer_builder_test common_objects/proto_peer_builder_test.cpp diff --git a/test/module/shared_model/builders/protobuf/transport_builder_test.cpp b/test/module/shared_model/builders/protobuf/transport_builder_test.cpp index ef5fd281d0..c298cd855d 100644 --- a/test/module/shared_model/builders/protobuf/transport_builder_test.cpp +++ b/test/module/shared_model/builders/protobuf/transport_builder_test.cpp @@ -5,7 +5,6 @@ #include -#include "block.pb.h" #include "builders/protobuf/block.hpp" #include "builders/protobuf/block_variant_transport_builder.hpp" #include "builders/protobuf/empty_block.hpp" diff --git a/test/module/shared_model/validators/field_validator_test.cpp b/test/module/shared_model/validators/field_validator_test.cpp index 8c17d28612..93ed894aab 100644 --- a/test/module/shared_model/validators/field_validator_test.cpp +++ b/test/module/shared_model/validators/field_validator_test.cpp @@ -19,7 +19,6 @@ #include #include -#include "block.pb.h" #include #include #include @@ -718,11 +717,13 @@ TEST_F(FieldValidatorTest, CommandFieldsValidation) { * meaningful message */ TEST_F(FieldValidatorTest, TransactionFieldsValidation) { - auto proto_tx = std::make_shared(); + auto proto_tx = std::make_shared(); proto_tx->add_signatures(); // at least one signature in message proto_tx->mutable_payload()->mutable_reduced_payload()->add_commands(); // iterate over all fields in transaction - iterateContainerRecursive(proto_tx, field_validators, + iterateContainerRecursive( + proto_tx, + field_validators, [this](auto field, auto transaction_field) { this->runTestCases(field); }, [] {}); } diff --git a/test/module/shared_model/validators/validators_fixture.hpp b/test/module/shared_model/validators/validators_fixture.hpp index 04fd49aac9..a5e05e3de2 100644 --- a/test/module/shared_model/validators/validators_fixture.hpp +++ b/test/module/shared_model/validators/validators_fixture.hpp @@ -23,11 +23,11 @@ #include #include -#include "block.pb.h" #include "datetime/time.hpp" #include "interfaces/permissions.hpp" #include "primitive.pb.h" #include "queries.pb.h" +#include "transaction.pb.h" class ValidatorsTest : public ::testing::Test { public: @@ -160,7 +160,8 @@ class ValidatorsTest : public ::testing::Test { void *ptr = new std::shared_ptr(mcopy); std::shared_ptr *m = static_cast *>(ptr); - this->iterateContainerRecursive(*m, field_validators, field_op, validator); + this->iterateContainerRecursive( + *m, field_validators, field_op, validator); } }); } From a9cc53251826c166216fa035a82d05d489500e81 Mon Sep 17 00:00:00 2001 From: Akvinikym Date: Thu, 12 Jul 2018 13:34:45 +0300 Subject: [PATCH 38/97] GetAccountDetail: high interface New GetAccountDetail query is now fully functional Signed-off-by: Akvinikym --- docs/source/api/queries.rst | 151 ++++++++++++++++++ irohad/execution/impl/query_execution.cpp | 5 +- .../queries/impl/proto_get_account_detail.cpp | 18 ++- .../queries/proto_get_account_detail.hpp | 4 + shared_model/bindings/model_query_builder.cpp | 7 +- shared_model/bindings/model_query_builder.hpp | 7 +- .../builder_templates/query_template.hpp | 14 +- .../interfaces/queries/get_account_detail.hpp | 23 ++- .../queries/impl/get_account_detail.cpp | 5 +- shared_model/schema/queries.proto | 10 +- .../shared_model/bindings/QueryTest.java | 6 +- .../shared_model/bindings/query-test.py | 6 +- .../validators/field_validator_test.cpp | 5 + .../validators/validators_fixture.hpp | 3 + 14 files changed, 247 insertions(+), 17 deletions(-) diff --git a/docs/source/api/queries.rst b/docs/source/api/queries.rst index a5b6521160..54e5459359 100644 --- a/docs/source/api/queries.rst +++ b/docs/source/api/queries.rst @@ -340,6 +340,157 @@ Response Structure "Account ID", "account which has this balance", "@", "makoto@soramitsu" "Balance", "balance of the asset", "Not less than 0", "200.20" +Get Account Detail +^^^^^^^^^^^^^^^^^^ + +Purpose +------- + +To get details of the account, `GetAccountDetail` query can be used. Account details are key-value pairs, splitted into writers categories. Writers are accounts, which added the corresponding account detail. Example of such structure is: + +.. code-block:: json + + { + "account@a_domain": { + "age": 18, + "hobbies": "crypto" + }, + "account@b_domain": { + "age": 20, + "sports": "basketball" + } + } + +Here, one can see four account details - "age", "hobbies" and "sports" - added by two writers - "account@a_domain" and "account@b_domain". All of these details, obviously, are about the same account. + +Request Schema +-------------- + +.. code-block:: proto + + message GetAccountDetail { + oneof opt_account_id { + string account_id = 1; + } + oneof opt_key { + string key = 2; + } + oneof opt_writer { + string writer = 3; + } + } + +.. note:: + Pay attention, that all fields are optional. Reasons will be described later. + +Request Structure +----------------- + +.. csv-table:: + :header: "Field", "Description", "Constraint", "Example" + :widths: 15, 30, 20, 15 + + "Account ID", "account id to get details from", "@", "account@domain" + "Key", "key, under which to get details", "string", "age" + "Writer", "account id of writer", "@", "account@domain" + +Response Schema +--------------- + +.. code-block:: proto + + message AccountDetailResponse { + string detail = 1; + } + +Response Structure +------------------ + +.. csv-table:: + :header: "Field", "Description", "Constraint", "Example" + :widths: 15, 30, 20, 15 + + "Detail", "key-value pairs with account details", "JSON", "see below" + +Usage Examples +-------------- + +Let's again consider the example of details from the beginning and see, how different variants of `GetAccountDetail` queries will change the resulting response. + +.. code-block:: json + + { + "account@a_domain": { + "age": 18, + "hobbies": "crypto" + }, + "account@b_domain": { + "age": 20, + "sports": "basketball" + } + } + +**account_id is not set** + +If account_id is not set - other fields can be empty or not - it will automatically be substituted with query creator's account, which will lead to one of the next cases. + +**only account_id is set** + +In this case, all details about that account are going to be returned, leading to the following response: + +.. code-block:: json + + { + "account@a_domain": { + "age": 18, + "hobbies": "crypto" + }, + "account@b_domain": { + "age": 20, + "sports": "basketball" + } + } + +**account_id and key are set** + +Here, details added by all writers under the key are going to be returned. For example, if we asked for the key "age", that's the response we would get: + +.. code-block:: json + + { + "account@a_domain": { + "age": 18 + }, + "account@b_domain": { + "age": 20 + } + } + +**account_id and writer are set** + +Now, the response will contain all details about this account, added by one specific writer. For example, if we asked for writer "account@b_domain", we would get: + +.. code-block:: json + + { + "account@b_domain": { + "age": 20, + "sports": "basketball" + } + } + +**account_id, key and writer are set** + +Lastly, if all three field are set, result will contain details, added the specific writer and under the specific key, for example, if we asked for key "age" and writer "account@a_domain", we would get: + +.. code-block:: json + + { + "account@a_domain": { + "age": 18 + } + } + Get Asset Info ^^^^^^^^^^^^^^ diff --git a/irohad/execution/impl/query_execution.cpp b/irohad/execution/impl/query_execution.cpp index 95dc0f95fd..fa45ab2855 100644 --- a/irohad/execution/impl/query_execution.cpp +++ b/irohad/execution/impl/query_execution.cpp @@ -247,7 +247,10 @@ QueryProcessingFactory::executeGetAccountAssets( QueryProcessingFactory::QueryResponseBuilderDone QueryProcessingFactory::executeGetAccountDetail( const shared_model::interface::GetAccountDetail &query) { - auto acct_detail = _wsvQuery->getAccountDetail(query.accountId()); + auto acct_detail = + _wsvQuery->getAccountDetail(query.accountId(), + query.key() ? *query.key() : "", + query.writer() ? *query.writer() : ""); if (not acct_detail) { return buildError(); } diff --git a/shared_model/backend/protobuf/queries/impl/proto_get_account_detail.cpp b/shared_model/backend/protobuf/queries/impl/proto_get_account_detail.cpp index 156f7e3668..9e50bcdff4 100644 --- a/shared_model/backend/protobuf/queries/impl/proto_get_account_detail.cpp +++ b/shared_model/backend/protobuf/queries/impl/proto_get_account_detail.cpp @@ -27,7 +27,23 @@ namespace shared_model { : GetAccountDetail(std::move(o.proto_)) {} const interface::types::AccountIdType &GetAccountDetail::accountId() const { - return account_detail_.account_id(); + return account_detail_.opt_account_id_case() + ? account_detail_.account_id() + : proto_->payload().meta().creator_account_id(); + } + + boost::optional + GetAccountDetail::key() const { + return account_detail_.opt_key_case() + ? boost::make_optional(account_detail_.key()) + : boost::none; + } + + boost::optional GetAccountDetail::writer() + const { + return account_detail_.opt_writer_case() + ? boost::make_optional(account_detail_.writer()) + : boost::none; } } // namespace proto diff --git a/shared_model/backend/protobuf/queries/proto_get_account_detail.hpp b/shared_model/backend/protobuf/queries/proto_get_account_detail.hpp index 066694d9e4..d167f8b48e 100644 --- a/shared_model/backend/protobuf/queries/proto_get_account_detail.hpp +++ b/shared_model/backend/protobuf/queries/proto_get_account_detail.hpp @@ -38,6 +38,10 @@ namespace shared_model { const interface::types::AccountIdType &accountId() const override; + boost::optional key() const override; + + boost::optional writer() const override; + private: // ------------------------------| fields |------------------------------- diff --git a/shared_model/bindings/model_query_builder.cpp b/shared_model/bindings/model_query_builder.cpp index 7cba8db1f3..58e5e37d3e 100644 --- a/shared_model/bindings/model_query_builder.cpp +++ b/shared_model/bindings/model_query_builder.cpp @@ -73,8 +73,11 @@ namespace shared_model { } ModelQueryBuilder ModelQueryBuilder::getAccountDetail( - const interface::types::AccountIdType &account_id) { - return ModelQueryBuilder(builder_.getAccountDetail(account_id)); + const interface::types::AccountIdType &account_id, + const interface::types::AccountDetailKeyType &key, + const interface::types::AccountIdType &writer) { + return ModelQueryBuilder( + builder_.getAccountDetail(account_id, key, writer)); } ModelQueryBuilder ModelQueryBuilder::getPendingTransactions() { diff --git a/shared_model/bindings/model_query_builder.hpp b/shared_model/bindings/model_query_builder.hpp index fb6a2a0112..ed7d256aef 100644 --- a/shared_model/bindings/model_query_builder.hpp +++ b/shared_model/bindings/model_query_builder.hpp @@ -123,11 +123,14 @@ namespace shared_model { /** * Retrieves details for a given account * @param account_id - account to retrieve details from - * @param detail - key to retrieve + * @param key - under which keys data should be returned + * @param writer - from which writers details should be returned * @return builder with getAccountDetail query inside */ ModelQueryBuilder getAccountDetail( - const interface::types::AccountIdType &account_id); + const interface::types::AccountIdType &account_id = "", + const interface::types::AccountDetailKeyType &key = "", + const interface::types::AccountIdType &writer = ""); /** * Retrieves all pending (not fully signed) multisignature transactions or diff --git a/shared_model/builders/protobuf/builder_templates/query_template.hpp b/shared_model/builders/protobuf/builder_templates/query_template.hpp index 33146715ec..f44c92e6b6 100644 --- a/shared_model/builders/protobuf/builder_templates/query_template.hpp +++ b/shared_model/builders/protobuf/builder_templates/query_template.hpp @@ -143,10 +143,20 @@ namespace shared_model { }); } - auto getAccountDetail(const interface::types::AccountIdType &account_id) { + auto getAccountDetail(const interface::types::AccountIdType &account_id = "", + const interface::types::AccountDetailKeyType &key = "", + const interface::types::AccountIdType &writer = "") { return queryField([&](auto proto_query) { auto query = proto_query->mutable_get_account_detail(); - query->set_account_id(account_id); + if (not account_id.empty()) { + query->set_account_id(account_id); + } + if (not key.empty()) { + query->set_key(key); + } + if (not writer.empty()) { + query->set_writer(writer); + } }); } diff --git a/shared_model/interfaces/queries/get_account_detail.hpp b/shared_model/interfaces/queries/get_account_detail.hpp index 08bdd45bc9..e65dc78e58 100644 --- a/shared_model/interfaces/queries/get_account_detail.hpp +++ b/shared_model/interfaces/queries/get_account_detail.hpp @@ -18,13 +18,24 @@ #ifndef IROHA_SHARED_MODEL_GET_ACCOUNT_DETAIL_HPP #define IROHA_SHARED_MODEL_GET_ACCOUNT_DETAIL_HPP +#include + #include "interfaces/base/model_primitive.hpp" #include "interfaces/common_objects/types.hpp" namespace shared_model { namespace interface { /** - * Query for get all account's assets and balance + * Query for get all account's details; the algorithm of retrieving them is + * the following: + * - if query has only accountId, all details about the specified account + * will be returned + * - if there is a key in a query, details written by all writers under + * this key will be returned + * - if there is a writer in a query, all details written by this writer + * will be returned + * - if there are both key and writer in a query, details written by this + * writer AND under this key will be returned */ class GetAccountDetail : public ModelPrimitive { public: @@ -33,6 +44,16 @@ namespace shared_model { */ virtual const types::AccountIdType &accountId() const = 0; + /** + * @return key from key-value storage + */ + virtual boost::optional key() const = 0; + + /** + * @return account identifier of writer + */ + virtual boost::optional writer() const = 0; + std::string toString() const override; bool operator==(const ModelType &rhs) const override; diff --git a/shared_model/interfaces/queries/impl/get_account_detail.cpp b/shared_model/interfaces/queries/impl/get_account_detail.cpp index 57d6557150..f5816a0cc1 100644 --- a/shared_model/interfaces/queries/impl/get_account_detail.cpp +++ b/shared_model/interfaces/queries/impl/get_account_detail.cpp @@ -12,11 +12,14 @@ namespace shared_model { return detail::PrettyStringBuilder() .init("GetAccountDetail") .append("account_id", accountId()) + .append("key", key() ? *key() : "") + .append("writer", writer() ? *writer() : "") .finalize(); } bool GetAccountDetail::operator==(const ModelType &rhs) const { - return accountId() == rhs.accountId(); + return accountId() == rhs.accountId() and key() == rhs.key() + and writer() == rhs.writer(); } } // namespace interface diff --git a/shared_model/schema/queries.proto b/shared_model/schema/queries.proto index 2db0ea4143..1f817cb5f4 100644 --- a/shared_model/schema/queries.proto +++ b/shared_model/schema/queries.proto @@ -34,7 +34,15 @@ message GetAccountAssets { } message GetAccountDetail { - string account_id = 1; + oneof opt_account_id{ + string account_id = 1; + } + oneof opt_key{ + string key = 2; + } + oneof opt_writer{ + string writer = 3; + } } message GetAssetInfo { diff --git a/test/module/shared_model/bindings/QueryTest.java b/test/module/shared_model/bindings/QueryTest.java index 01ffee5346..51003c2335 100644 --- a/test/module/shared_model/bindings/QueryTest.java +++ b/test/module/shared_model/bindings/QueryTest.java @@ -547,8 +547,8 @@ void getAccountDetailWithInvalidDomain() { } @Test - void getAccountDetailWithEmptyName() { - ModelQueryBuilder builder = base().getAccountDetail(""); - assertThrows(IllegalArgumentException.class, builder::build); + void getAccountDetailWithNoArgs() { + UnsignedQuery query = base().getAccountDetail(); + assertTrue(checkProtoQuery(proto(query))); } } diff --git a/test/module/shared_model/bindings/query-test.py b/test/module/shared_model/bindings/query-test.py index 68038b525c..265073f849 100644 --- a/test/module/shared_model/bindings/query-test.py +++ b/test/module/shared_model/bindings/query-test.py @@ -382,9 +382,9 @@ def test_get_account_detail_invalid_domain(self): with self.assertRaises(ValueError): self.base().getAccountDetail("admin@{}".format(domain)).build() - def test_get_account_detail_with_empty_name(self): - with self.assertRaises(ValueError): - self.base().getAccountDetail("").build() + def test_get_account_detail_with_no_args(self): + query = self.builder.getAccountDetail().build() + self.assertTrue(self.check_proto_query(self.proto(query))) if __name__ == '__main__': unittest.main() diff --git a/test/module/shared_model/validators/field_validator_test.cpp b/test/module/shared_model/validators/field_validator_test.cpp index 93ed894aab..998ec124e4 100644 --- a/test/module/shared_model/validators/field_validator_test.cpp +++ b/test/module/shared_model/validators/field_validator_test.cpp @@ -25,6 +25,7 @@ #include #include #include +#include "block.pb.h" #include "backend/protobuf/common_objects/peer.hpp" #include "backend/protobuf/permissions.hpp" @@ -657,6 +658,10 @@ class FieldValidatorTest : public ValidatorsTest { &FieldValidator::validateAccountName, &FieldValidatorTest::account_name, account_name_test_cases), + makeValidator("writer", + &FieldValidator::validateAccountId, + &FieldValidatorTest::account_id, + account_id_test_cases), makeValidator("domain_id", &FieldValidator::validateDomainId, &FieldValidatorTest::domain_id, diff --git a/test/module/shared_model/validators/validators_fixture.hpp b/test/module/shared_model/validators/validators_fixture.hpp index a5e05e3de2..e4e6e39a9b 100644 --- a/test/module/shared_model/validators/validators_fixture.hpp +++ b/test/module/shared_model/validators/validators_fixture.hpp @@ -67,6 +67,7 @@ class ValidatorsTest : public ::testing::Test { field_setters["permission"] = setEnum(role_permission); field_setters["grantable_permission"] = setEnum(grantable_permission); field_setters["key"] = setString(detail_key); + field_setters["writer"] = setString(writer); field_setters["detail"] = setString(detail_key); field_setters["value"] = setString(""); field_setters["tx_hashes"] = addString(hash); @@ -187,6 +188,7 @@ class ValidatorsTest : public ::testing::Test { account_name = "admin"; domain_id = "ru"; detail_key = "key"; + writer = "account@domain"; public_key = std::string(public_key_size, '0'); hash = std::string(public_key_size, '0'); role_permission = iroha::protocol::RolePermission::can_append_role; @@ -215,6 +217,7 @@ class ValidatorsTest : public ::testing::Test { std::string description; std::string public_key; std::string hash; + std::string writer; iroha::protocol::Transaction::Payload::BatchMeta batch_meta; shared_model::interface::permissions::Role model_role_permission; shared_model::interface::permissions::Grantable model_grantable_permission; From e617d7caffc462aecd6467f4d79ca15e6fb409ef Mon Sep 17 00:00:00 2001 From: kamilsa Date: Thu, 12 Jul 2018 17:45:41 +0300 Subject: [PATCH 39/97] Update transaction sequence (#1548) * Update tx sequence Signed-off-by: kamilsa --- shared_model/backend/protobuf/transaction.hpp | 4 +- .../protobuf/transaction_sequence_builder.hpp | 33 ++--- .../builders/protobuf/unsigned_proto.hpp | 2 +- .../transaction_sequence_common.hpp | 12 +- .../interfaces/iroha_internal/batch_meta.hpp | 4 +- .../iroha_internal/transaction_sequence.cpp | 75 ++++++++++- .../iroha_internal/transaction_sequence.hpp | 42 +++++- shared_model/interfaces/transaction.hpp | 12 +- .../validators/transaction_validator.hpp | 4 +- .../any_order_validator.hpp | 2 +- .../batch_order_validator.cpp | 43 +++--- .../batch_order_validator.hpp | 9 +- .../order_validator.hpp | 7 +- ...gned_transactions_collection_validator.cpp | 21 ++- ...gned_transactions_collection_validator.hpp | 4 +- .../transactions_collection_validator.hpp | 19 ++- ...gned_transactions_collection_validator.cpp | 20 +-- ...gned_transactions_collection_validator.hpp | 6 +- test/framework/batch_helper.hpp | 55 ++++++++ .../shared_model/backend_proto/CMakeLists.txt | 7 + .../proto_transaction_sequence_test.cpp | 122 ++++++++++++++++++ .../protobuf/transport_builder_test.cpp | 2 +- 22 files changed, 399 insertions(+), 106 deletions(-) create mode 100644 test/framework/batch_helper.hpp create mode 100644 test/module/shared_model/backend_proto/proto_transaction_sequence_test.cpp diff --git a/shared_model/backend/protobuf/transaction.hpp b/shared_model/backend/protobuf/transaction.hpp index 242dd8d2e3..ea17910493 100644 --- a/shared_model/backend/protobuf/transaction.hpp +++ b/shared_model/backend/protobuf/transaction.hpp @@ -58,7 +58,7 @@ namespace shared_model { return *blobTypePayload_; } - const interface::types::BlobType &reduced_payload() const override { + const interface::types::BlobType &reducedPayload() const override { return *blobTypeReducedPayload_; } @@ -94,7 +94,7 @@ namespace shared_model { return reduced_payload_.quorum(); } - boost::optional> batch_meta() + boost::optional> batchMeta() const override { return *meta_; } diff --git a/shared_model/builders/protobuf/transaction_sequence_builder.hpp b/shared_model/builders/protobuf/transaction_sequence_builder.hpp index 8fa3ab1e65..fe002a246e 100644 --- a/shared_model/builders/protobuf/transaction_sequence_builder.hpp +++ b/shared_model/builders/protobuf/transaction_sequence_builder.hpp @@ -18,30 +18,6 @@ namespace shared_model { */ template class TransportBuilder { - private: - /** - * Creator of transaction sequence - * @param transactions collection of transactions - * @param validator validator of the collections - * @return Result containing transaction sequence if validation successful - * and string message containing error otherwise - */ - template - iroha::expected::Result - createTransactionSequence( - const interface::types::TransactionsForwardCollectionType - &transactions, - const validation::TransactionsCollectionValidator< - TransactionValidator, - OrderValidator> &validator) { - auto answer = validator.validate(transactions); - if (answer.hasErrors()) { - return iroha::expected::makeError(answer.reason()); - } - return iroha::expected::makeValue( - interface::TransactionSequence(transactions)); - } - public: TransportBuilder( SV stateless_validator = SV()) @@ -56,7 +32,14 @@ namespace shared_model { template iroha::expected::Result build(T &transport) { - return createTransactionSequence(transport, stateless_validator_); + std::vector> shm_txs; + std::transform( + transport.begin(), + transport.end(), + std::back_inserter(shm_txs), + [](const auto &tx) { return std::make_shared(tx); }); + return interface::TransactionSequence::createTransactionSequence( + shm_txs, stateless_validator_); } private: diff --git a/shared_model/builders/protobuf/unsigned_proto.hpp b/shared_model/builders/protobuf/unsigned_proto.hpp index 258fbba630..b9d2ca2dd3 100644 --- a/shared_model/builders/protobuf/unsigned_proto.hpp +++ b/shared_model/builders/protobuf/unsigned_proto.hpp @@ -75,7 +75,7 @@ namespace shared_model { std::is_base_of::value, interface::types::HashType>::type reduced_hash() { - return object_.reduced_hash(); + return object_.reducedHash(); } private: diff --git a/shared_model/interfaces/common_objects/transaction_sequence_common.hpp b/shared_model/interfaces/common_objects/transaction_sequence_common.hpp index 30dfc1b150..972aad70be 100644 --- a/shared_model/interfaces/common_objects/transaction_sequence_common.hpp +++ b/shared_model/interfaces/common_objects/transaction_sequence_common.hpp @@ -19,8 +19,16 @@ namespace shared_model { boost::any_range; - } - } // namespace interface + + using SharedTxsCollectionType = std::vector>; + + // TODO: IR-1514 kamilsa 09.07.2018 Introduce batch type with batch + // invariant and return range of them + using BatchesType = boost::any_range; + } // namespace types + } // namespace interface } // namespace shared_model #endif // IROHA_TRANSACTION_SEQUENCE_COMMON_HPP diff --git a/shared_model/interfaces/iroha_internal/batch_meta.hpp b/shared_model/interfaces/iroha_internal/batch_meta.hpp index f65b1ab9d1..46a4152bab 100644 --- a/shared_model/interfaces/iroha_internal/batch_meta.hpp +++ b/shared_model/interfaces/iroha_internal/batch_meta.hpp @@ -41,8 +41,8 @@ namespace shared_model { * @return true, if wrapped objects are same */ bool operator==(const ModelType &rhs) const override { - return type() == rhs.type() - and transactionHashes() == rhs.transactionHashes(); + return boost::equal(transactionHashes(), rhs.transactionHashes()) + and type() == rhs.type(); } }; } // namespace interface diff --git a/shared_model/interfaces/iroha_internal/transaction_sequence.cpp b/shared_model/interfaces/iroha_internal/transaction_sequence.cpp index 8ca3ebc195..0790ff02f5 100644 --- a/shared_model/interfaces/iroha_internal/transaction_sequence.cpp +++ b/shared_model/interfaces/iroha_internal/transaction_sequence.cpp @@ -6,18 +6,87 @@ #include "interfaces/iroha_internal/transaction_sequence.hpp" #include "validators/field_validator.hpp" #include "validators/transaction_validator.hpp" +#include "validators/transactions_collection/any_order_validator.hpp" +#include "validators/transactions_collection/batch_order_validator.hpp" namespace shared_model { namespace interface { - types::TransactionsForwardCollectionType - TransactionSequence::transactions() { + template + iroha::expected::Result + TransactionSequence::createTransactionSequence( + const types::SharedTxsCollectionType &transactions, + const validation::TransactionsCollectionValidator + &validator) { + auto answer = validator.validatePointers(transactions); + if (answer.hasErrors()) { + return iroha::expected::makeError(answer.reason()); + } + return iroha::expected::makeValue(TransactionSequence(transactions)); + } + + template iroha::expected::Result + TransactionSequence::createTransactionSequence( + const types::SharedTxsCollectionType &transactions, + const validation::TransactionsCollectionValidator< + validation::TransactionValidator< + validation::FieldValidator, + validation::CommandValidatorVisitor< + validation::FieldValidator>>, + validation::AnyOrderValidator> &validator); + + template iroha::expected::Result + TransactionSequence::createTransactionSequence( + const types::SharedTxsCollectionType &transactions, + const validation::TransactionsCollectionValidator< + validation::TransactionValidator< + validation::FieldValidator, + validation::CommandValidatorVisitor< + validation::FieldValidator>>, + validation::BatchOrderValidator> &validator); + + types::SharedTxsCollectionType TransactionSequence::transactions() const { return transactions_; } TransactionSequence::TransactionSequence( - const types::TransactionsForwardCollectionType &transactions) + const types::SharedTxsCollectionType &transactions) : transactions_(transactions) {} + types::BatchesType TransactionSequence::batches() const { + if (batches_) { + return batches_.value(); + } + + std::unordered_map>> + extracted_batches; + std::vector batches; + for (const auto &tx : transactions_) { + if (auto meta = tx->batchMeta()) { + auto hashes = meta.get()->transactionHashes(); + auto batch_hash = this->calculateBatchHash(hashes); + extracted_batches[batch_hash].push_back(tx); + } else { + batches.emplace_back(std::vector>{tx}); + } + } + for (auto it : extracted_batches) { + batches.emplace_back(it.second); + } + + batches_.emplace(batches); + return batches; + } + + std::string TransactionSequence::calculateBatchHash( + std::vector reduced_hashes) const { + std::stringstream concatenated_hashes_stream; + for (const auto &hash : reduced_hashes) { + concatenated_hashes_stream << hash.hex(); + } + return concatenated_hashes_stream.str(); + } + } // namespace interface } // namespace shared_model diff --git a/shared_model/interfaces/iroha_internal/transaction_sequence.hpp b/shared_model/interfaces/iroha_internal/transaction_sequence.hpp index 8e693dfbe5..c28928724b 100644 --- a/shared_model/interfaces/iroha_internal/transaction_sequence.hpp +++ b/shared_model/interfaces/iroha_internal/transaction_sequence.hpp @@ -21,16 +21,52 @@ namespace shared_model { */ class TransactionSequence { public: + TransactionSequence() = delete; + + /** + * Creator of transaction sequence + * @param transactions collection of transactions + * @param validator validator of the collections + * @return Result containing transaction sequence if validation + * successful and string message containing error otherwise + */ + template + static iroha::expected::Result + createTransactionSequence( + const types::SharedTxsCollectionType &transactions, + const validation::TransactionsCollectionValidator< + TransactionValidator, + OrderValidator> &validator); + /** * Get transactions collection * @return transactions collection */ - types::TransactionsForwardCollectionType transactions(); + types::SharedTxsCollectionType transactions() const; + /** + * Get batches in transaction sequence + * Note that transaction without batch meta are returned as batch with + * single transaction + * @return collection of batches from transaction sequence + */ + types::BatchesType batches() const; + + private: explicit TransactionSequence( - const types::TransactionsForwardCollectionType &transactions); + const types::SharedTxsCollectionType &transactions); + + /** + * Get the concatenation of reduced hashes + * @param reduced_hashes collection of reduced hashes + * @return concatenated redueced hashes + */ + std::string calculateBatchHash( + std::vector reduced_hashes) const; + + types::SharedTxsCollectionType transactions_; - types::TransactionsForwardCollectionType transactions_; + mutable boost::optional batches_; }; } // namespace interface diff --git a/shared_model/interfaces/transaction.hpp b/shared_model/interfaces/transaction.hpp index f15a022b3d..18d4222108 100644 --- a/shared_model/interfaces/transaction.hpp +++ b/shared_model/interfaces/transaction.hpp @@ -57,19 +57,18 @@ namespace shared_model { /** * @return object payload (everything except signatures) */ - virtual const types::BlobType &reduced_payload() const = 0; + virtual const types::BlobType &reducedPayload() const = 0; - const types::HashType &reduced_hash() const { + const types::HashType &reducedHash() const { if (reduced_hash_ == boost::none) { - reduced_hash_.emplace(HashProvider::makeHash(reduced_payload())); + reduced_hash_.emplace(HashProvider::makeHash(reducedPayload())); } return *reduced_hash_; } /* * @return Batch Meta if exists */ - virtual boost::optional> batch_meta() - const = 0; + virtual boost::optional> batchMeta() const = 0; std::string toString() const override { return detail::PrettyStringBuilder() @@ -81,7 +80,8 @@ namespace shared_model { .append("commands") .appendAll(commands(), [](auto &command) { return command.toString(); }) - .append(batch_meta()->get()->toString()) + .append("batch_meta", + batchMeta() ? batchMeta()->get()->toString() : "") .append("signatures") .appendAll(signatures(), [](auto &sig) { return sig.toString(); }) .finalize(); diff --git a/shared_model/validators/transaction_validator.hpp b/shared_model/validators/transaction_validator.hpp index 83881832a4..b34c96b4b6 100644 --- a/shared_model/validators/transaction_validator.hpp +++ b/shared_model/validators/transaction_validator.hpp @@ -266,8 +266,8 @@ namespace shared_model { tx.creatorAccountId()); field_validator_.validateCreatedTime(tx_reason, tx.createdTime()); field_validator_.validateQuorum(tx_reason, tx.quorum()); - if (tx.batch_meta() != boost::none) - field_validator_.validateBatchMeta(tx_reason, **tx.batch_meta()); + if (tx.batchMeta() != boost::none) + field_validator_.validateBatchMeta(tx_reason, **tx.batchMeta()); if (not tx_reason.second.empty()) { answer.addReason(std::move(tx_reason)); diff --git a/shared_model/validators/transactions_collection/any_order_validator.hpp b/shared_model/validators/transactions_collection/any_order_validator.hpp index 36cf36a333..14409055b5 100644 --- a/shared_model/validators/transactions_collection/any_order_validator.hpp +++ b/shared_model/validators/transactions_collection/any_order_validator.hpp @@ -12,7 +12,7 @@ namespace shared_model { namespace validation { class AnyOrderValidator : public OrderValidator { public: - Answer validate(const interface::types::TransactionsForwardCollectionType + Answer validate(const interface::types::SharedTxsCollectionType &transactions) const override { return Answer(); }; diff --git a/shared_model/validators/transactions_collection/batch_order_validator.cpp b/shared_model/validators/transactions_collection/batch_order_validator.cpp index d103e02dd7..32eddd46b0 100644 --- a/shared_model/validators/transactions_collection/batch_order_validator.cpp +++ b/shared_model/validators/transactions_collection/batch_order_validator.cpp @@ -12,12 +12,12 @@ namespace shared_model { namespace validation { std::string BatchOrderValidator::canFollow( - boost::optional tr1, - boost::optional tr2) const { + boost::optional> tr1, + boost::optional> tr2) const { boost::optional> batch1 = - tr1 ? tr1->batch_meta() : boost::none; + tr1 ? tr1.value()->batchMeta() : boost::none; boost::optional> batch2 = - tr2 ? tr2->batch_meta() : boost::none; + tr2 ? tr2.value()->batchMeta() : boost::none; // both transactions are not a part of any batch if (not batch1 and not batch2) { return ""; @@ -26,38 +26,40 @@ namespace shared_model { if (not batch1) { if (batch2.get()->transactionHashes().size() == 0) { return (boost::format("Tx %s has a batch of 0 transactions") - % tr2->hash().hex()) + % tr2.value()->hash().hex()) .str(); } - if (batch2.get()->transactionHashes().front() != tr2->reduced_hash()) { + if (batch2.get()->transactionHashes().front() + != tr2.value()->reducedHash()) { return (boost::format("Tx %s is a first transaction of a batch, but " "it's reduced hash %s doesn't match the first " "reduced hash in batch %s") - % tr2->hash().hex() + % tr2.value()->hash().hex() % batch2.get()->transactionHashes().front().hex() - % tr2->reduced_hash().hex()) + % tr2.value()->reducedHash().hex()) .str(); } return ""; } // end of a batch if (not batch2) { - if (batch1.get()->transactionHashes().back() != tr1->reduced_hash()) { + if (batch1.get()->transactionHashes().back() + != tr1.value()->reducedHash()) { return (boost::format("Tx %s is a last transaction of a batch, but " "it's reduced hash %s doesn't match the last " "reduced hash in batch %s") - % tr1->hash().hex() + % tr1.value()->hash().hex() % batch1.get()->transactionHashes().back().hex() - % tr1->reduced_hash().hex()) + % tr1.value()->reducedHash().hex()) .str(); } return ""; } // inside of a batch - auto it1 = - boost::find(batch1.get()->transactionHashes(), tr1->reduced_hash()); - auto it2 = - boost::find(batch2.get()->transactionHashes(), tr2->reduced_hash()); + auto it1 = boost::find(batch1.get()->transactionHashes(), + tr1.value()->reducedHash()); + auto it2 = boost::find(batch2.get()->transactionHashes(), + tr2.value()->reducedHash()); if (it1 == end(batch1.get()->transactionHashes()) or it2 == end(batch2.get()->transactionHashes()) or next(it1) == end(batch1.get()->transactionHashes()) @@ -70,26 +72,25 @@ namespace shared_model { return (boost::format("Tx %s is followed by %s, but their reduced" "hashed doesn't follow each other in their batch" "meta") - % tr1->hash().hex() % tr2->hash().hex()) + % tr1.value()->hash().hex() % tr2.value()->hash().hex()) .str(); } if (**batch1 != **batch2) { return (boost::format("Tx %s and %s are part of the same batch, but " "their batch metas doesn't match") - % tr1->hash().hex() % tr2->hash().hex()) + % tr1.value()->hash().hex() % tr2.value()->hash().hex()) .str(); } return ""; } Answer BatchOrderValidator::validate( - const interface::types::TransactionsForwardCollectionType &transactions) - const { + const interface::types::SharedTxsCollectionType &transactions) const { Answer res; ReasonsGroupType reason; reason.first = "Transaction order"; - boost::optional prev_transaction = - boost::none; + boost::optional + prev_transaction = boost::none; for (auto &transaction : transactions) { auto message = canFollow(prev_transaction, transaction); diff --git a/shared_model/validators/transactions_collection/batch_order_validator.hpp b/shared_model/validators/transactions_collection/batch_order_validator.hpp index 6e4ab74cc0..25eb427b67 100644 --- a/shared_model/validators/transactions_collection/batch_order_validator.hpp +++ b/shared_model/validators/transactions_collection/batch_order_validator.hpp @@ -20,13 +20,12 @@ namespace shared_model { * @return empty string if order is correct or error message */ std::string canFollow( - boost::optional tr1, - boost::optional tr2) const; + boost::optional> tr1, + boost::optional> tr2) const; public: - virtual Answer validate( - const interface::types::TransactionsForwardCollectionType - &transactions) const override; + virtual Answer validate(const interface::types::SharedTxsCollectionType + &transactions) const override; }; } // namespace validation } // namespace shared_model diff --git a/shared_model/validators/transactions_collection/order_validator.hpp b/shared_model/validators/transactions_collection/order_validator.hpp index a828d738d7..de59c487df 100644 --- a/shared_model/validators/transactions_collection/order_validator.hpp +++ b/shared_model/validators/transactions_collection/order_validator.hpp @@ -6,16 +6,15 @@ #ifndef IROHA_ORDER_VALIDATOR_HPP #define IROHA_ORDER_VALIDATOR_HPP -#include "validators/answer.hpp" #include "interfaces/common_objects/transaction_sequence_common.hpp" +#include "validators/answer.hpp" namespace shared_model { namespace validation { class OrderValidator { public: - virtual Answer validate( - const interface::types::TransactionsForwardCollectionType - &transactions) const = 0; + virtual Answer validate(const interface::types::SharedTxsCollectionType + &transactions) const = 0; }; } // namespace validation } // namespace shared_model diff --git a/shared_model/validators/transactions_collection/signed_transactions_collection_validator.cpp b/shared_model/validators/transactions_collection/signed_transactions_collection_validator.cpp index 58ec6366f3..4a91d9ad51 100644 --- a/shared_model/validators/transactions_collection/signed_transactions_collection_validator.cpp +++ b/shared_model/validators/transactions_collection/signed_transactions_collection_validator.cpp @@ -16,8 +16,8 @@ namespace shared_model { template Answer SignedTransactionsCollectionValidator:: - validate(const interface::types::TransactionsForwardCollectionType - &transactions) const { + validatePointers(const interface::types::SharedTxsCollectionType + &transactions) const { Answer res = SignedTransactionsCollectionValidator::order_validator_.validate( transactions); @@ -26,10 +26,10 @@ namespace shared_model { for (const auto &tx : transactions) { auto answer = SignedTransactionsCollectionValidator::transaction_validator_ - .validate(tx); + .validate(*tx); if (answer.hasErrors()) { auto message = - (boost::format("Tx %s : %s") % tx.hash().hex() % answer.reason()) + (boost::format("Tx %s : %s") % tx->hash().hex() % answer.reason()) .str(); reason.second.push_back(message); } @@ -40,18 +40,15 @@ namespace shared_model { return res; } - template Answer SignedTransactionsCollectionValidator< + template class SignedTransactionsCollectionValidator< TransactionValidator>>:: - validate(const interface::types::TransactionsForwardCollectionType - &transactions) const; + CommandValidatorVisitor>, + AnyOrderValidator>; - template Answer SignedTransactionsCollectionValidator< + template class SignedTransactionsCollectionValidator< TransactionValidator>, - BatchOrderValidator>:: - validate(const interface::types::TransactionsForwardCollectionType - &transactions) const; + BatchOrderValidator>; } // namespace validation } // namespace shared_model diff --git a/shared_model/validators/transactions_collection/signed_transactions_collection_validator.hpp b/shared_model/validators/transactions_collection/signed_transactions_collection_validator.hpp index f8a65c9046..2bb35a3274 100644 --- a/shared_model/validators/transactions_collection/signed_transactions_collection_validator.hpp +++ b/shared_model/validators/transactions_collection/signed_transactions_collection_validator.hpp @@ -26,8 +26,8 @@ namespace shared_model { using TransactionsCollectionValidator< TransactionValidator, OrderValidator>::TransactionsCollectionValidator; - Answer validate(const interface::types::TransactionsForwardCollectionType - &transactions) const override; + Answer validatePointers(const interface::types::SharedTxsCollectionType + &transactions) const override; }; } // namespace validation diff --git a/shared_model/validators/transactions_collection/transactions_collection_validator.hpp b/shared_model/validators/transactions_collection/transactions_collection_validator.hpp index 904439b819..96d76ffe18 100644 --- a/shared_model/validators/transactions_collection/transactions_collection_validator.hpp +++ b/shared_model/validators/transactions_collection/transactions_collection_validator.hpp @@ -6,6 +6,7 @@ #ifndef IROHA_TRANSACTION_SEQUENCE_VALIDATOR_HPP #define IROHA_TRANSACTION_SEQUENCE_VALIDATOR_HPP +#include #include "interfaces/common_objects/transaction_sequence_common.hpp" #include "validators/answer.hpp" #include "validators/transactions_collection/any_order_validator.hpp" @@ -31,14 +32,26 @@ namespace shared_model { const OrderValidator &order_validator = OrderValidator()) : order_validator_(order_validator) {} + // TODO: IR-1505, igor-egorov, 2018-07-05 Remove method below when + // proposal and block will return collection of shared transactions /** * Validates collection of transactions * @param transactions collection of transactions * @return Answer containing errors if any */ - virtual Answer validate( - const interface::types::TransactionsForwardCollectionType - &transactions) const = 0; + Answer validate(const interface::types::TransactionsForwardCollectionType + &transactions) const { + interface::types::SharedTxsCollectionType res; + std::transform(std::begin(transactions), + std::end(transactions), + std::back_inserter(res), + [](const auto &tx) { return clone(tx); }); + return validatePointers(res); + } + + virtual Answer validatePointers( + const interface::types::SharedTxsCollectionType &transactions) + const = 0; }; } // namespace validation diff --git a/shared_model/validators/transactions_collection/unsigned_transactions_collection_validator.cpp b/shared_model/validators/transactions_collection/unsigned_transactions_collection_validator.cpp index cdefe57d89..b8027fb863 100644 --- a/shared_model/validators/transactions_collection/unsigned_transactions_collection_validator.cpp +++ b/shared_model/validators/transactions_collection/unsigned_transactions_collection_validator.cpp @@ -16,8 +16,8 @@ namespace shared_model { template Answer UnsignedTransactionsCollectionValidator:: - validate(const interface::types::TransactionsForwardCollectionType - &transactions) const { + validatePointers(const interface::types::SharedTxsCollectionType + &transactions) const { Answer res = UnsignedTransactionsCollectionValidator::order_validator_.validate( transactions); @@ -26,10 +26,10 @@ namespace shared_model { for (const auto &tx : transactions) { auto answer = UnsignedTransactionsCollectionValidator::transaction_validator_ - .validate(tx); + .validate(*tx); if (answer.hasErrors()) { auto message = - (boost::format("Tx %s : %s") % tx.hash().hex() % answer.reason()) + (boost::format("Tx %s : %s") % tx->hash().hex() % answer.reason()) .str(); reason.second.push_back(message); } @@ -41,11 +41,15 @@ namespace shared_model { return res; } - template Answer UnsignedTransactionsCollectionValidator< + template class UnsignedTransactionsCollectionValidator< TransactionValidator>>:: - validate(const interface::types::TransactionsForwardCollectionType - &transactions) const; + CommandValidatorVisitor>, + AnyOrderValidator>; + + template class UnsignedTransactionsCollectionValidator< + TransactionValidator>, + BatchOrderValidator>; } // namespace validation } // namespace shared_model diff --git a/shared_model/validators/transactions_collection/unsigned_transactions_collection_validator.hpp b/shared_model/validators/transactions_collection/unsigned_transactions_collection_validator.hpp index 515bd11127..b25bc665b9 100644 --- a/shared_model/validators/transactions_collection/unsigned_transactions_collection_validator.hpp +++ b/shared_model/validators/transactions_collection/unsigned_transactions_collection_validator.hpp @@ -6,8 +6,8 @@ #ifndef IROHA_UNSIGNED_TRANSACTIONS_SEQUENCE_VALIDATOR_HPP #define IROHA_UNSIGNED_TRANSACTIONS_SEQUENCE_VALIDATOR_HPP -#include "validators/transactions_collection/transactions_collection_validator.hpp" #include "validators/transactions_collection/any_order_validator.hpp" +#include "validators/transactions_collection/transactions_collection_validator.hpp" namespace shared_model { namespace validation { @@ -25,8 +25,8 @@ namespace shared_model { using TransactionsCollectionValidator< TransactionValidator, OrderValidator>::TransactionsCollectionValidator; - Answer validate(const interface::types::TransactionsForwardCollectionType - &transactions) const override; + Answer validatePointers(const interface::types::SharedTxsCollectionType + &transactions) const override; }; } // namespace validation diff --git a/test/framework/batch_helper.hpp b/test/framework/batch_helper.hpp new file mode 100644 index 0000000000..8d14a6e8e3 --- /dev/null +++ b/test/framework/batch_helper.hpp @@ -0,0 +1,55 @@ +/** + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef IROHA_BATCH_HELPER_HPP +#define IROHA_BATCH_HELPER_HPP + +#include "module/shared_model/builders/protobuf/test_transaction_builder.hpp" + +namespace framework { + namespace batch { + + /** + * Creates transaction builder with set creator + * @return prepared transaction builder + */ + auto prepareTransactionBuilder(const std::string &creator) { + return TestTransactionBuilder().creatorAccountId(creator); + } + + /** + * Creates atomic batch from provided creator accounts + * @param creators vector of creator account ids + * @return atomic batch of the same size as the size of creator account ids + */ + auto createUnsignedBatch(shared_model::interface::types::BatchType, + std::vector creators) { + std::vector reduced_hashes; + + for (const auto &creator : creators) { + auto tx = prepareTransactionBuilder(creator).build(); + reduced_hashes.push_back(tx.reducedHash()); + } + + shared_model::interface::types::SharedTxsCollectionType txs; + std::for_each( + creators.begin(), + creators.end(), + [&txs, &reduced_hashes](const auto &creator) { + txs.emplace_back( + clone(prepareTransactionBuilder(creator) + .batchMeta( + shared_model::interface::types::BatchType::ATOMIC, + reduced_hashes) + .build())); + }); + + return txs; + } + + } // namespace batch +} // namespace framework + +#endif // IROHA_BATCH_HELPER_HPP diff --git a/test/module/shared_model/backend_proto/CMakeLists.txt b/test/module/shared_model/backend_proto/CMakeLists.txt index 2f2b00525e..f64dee82b5 100644 --- a/test/module/shared_model/backend_proto/CMakeLists.txt +++ b/test/module/shared_model/backend_proto/CMakeLists.txt @@ -80,3 +80,10 @@ target_link_libraries(permissions_test shared_model_proto_backend ) +addtest(proto_transaction_sequence_test + proto_transaction_sequence_test.cpp + ) +target_link_libraries(proto_transaction_sequence_test + shared_model_proto_backend + shared_model_stateless_validation + ) diff --git a/test/module/shared_model/backend_proto/proto_transaction_sequence_test.cpp b/test/module/shared_model/backend_proto/proto_transaction_sequence_test.cpp new file mode 100644 index 0000000000..9d942cc687 --- /dev/null +++ b/test/module/shared_model/backend_proto/proto_transaction_sequence_test.cpp @@ -0,0 +1,122 @@ +/** + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include "framework/batch_helper.hpp" +#include "framework/result_fixture.hpp" +#include "interfaces/iroha_internal/transaction_sequence.hpp" + +using namespace shared_model; +using ::testing::_; +using ::testing::Return; +using ::testing::Test; + +class MockTransactionCollectionValidator + : public validation::SignedTransactionsCollectionValidator< + validation::TransactionValidator>> { + public: + MOCK_CONST_METHOD1( + validatePointers, + validation::Answer(const interface::types::SharedTxsCollectionType &)); +}; + +/** + * @given Transaction collection of several transactions + * @when create transaction sequence + * @and transactions validator returns empty answer + * @then TransactionSequence is created + */ +TEST(TransactionSequenceTest, CreateTransactionSequenceWhenValid) { + MockTransactionCollectionValidator transactions_validator; + + EXPECT_CALL(transactions_validator, validatePointers(_)) + .WillOnce(Return(validation::Answer())); + + std::shared_ptr tx(clone( + framework::batch::prepareTransactionBuilder("account@domain").build())); + + auto tx_sequence = interface::TransactionSequence::createTransactionSequence( + std::vector{tx, tx, tx}, transactions_validator); + + ASSERT_TRUE(framework::expected::val(tx_sequence)); +} + +/** + * @given Transaction collection of several transactions + * @when create transaction sequence + * @and transactions validator returns non empty answer + * @then TransactionSequence is not created + */ +TEST(TransactionSequenceTest, CreateTransactionSequenceWhenInvalid) { + MockTransactionCollectionValidator res; + + validation::Answer answer; + answer.addReason( + std::make_pair("transaction", std::vector{"some reason"})); + + EXPECT_CALL(res, validatePointers(_)).WillOnce(Return(answer)); + + std::shared_ptr tx(clone( + framework::batch::prepareTransactionBuilder("account@domain").build())); + + auto tx_sequence = interface::TransactionSequence::createTransactionSequence( + std::vector{tx, tx, tx}, res); + + ASSERT_TRUE(framework::expected::err(tx_sequence)); +} + +/** + * @given Transaction collection of several transactions, including some of the + * united into the batches + * @when transactions validator returns empty answer + * @and create transaction sequence + * @then expected number of batches is created + */ +TEST(TransactionSequenceTest, CreateBatches) { + MockTransactionCollectionValidator txs_validator; + + EXPECT_CALL(txs_validator, validatePointers(_)) + .WillOnce(Return(validation::Answer())); + + size_t batches_number = 3; + size_t txs_in_batch = 2; + size_t single_transactions = 1; + + interface::types::SharedTxsCollectionType tx_collection; + for (size_t i = 0; i < batches_number; i++) { + std::vector creators; + for (size_t j = 0; j < txs_in_batch; j++) { + creators.push_back("batch" + std::to_string(i) + "account" + + std::to_string(j) + "@domain"); + } + + auto batch = framework::batch::createUnsignedBatch( + shared_model::interface::types::BatchType::ATOMIC, creators); + tx_collection.insert(tx_collection.begin(), batch.begin(), batch.end()); + } + + for (size_t i = 0; i < single_transactions; i++) { + tx_collection.emplace_back( + clone(framework::batch::prepareTransactionBuilder( + "single_tx_account@domain" + std::to_string(i)) + .build())); + } + + auto tx_sequence_opt = + interface::TransactionSequence::createTransactionSequence(tx_collection, + txs_validator); + + auto tx_sequence = framework::expected::val(tx_sequence_opt); + ASSERT_TRUE(tx_sequence); + + ASSERT_EQ(boost::size(tx_sequence->value.transactions()), + batches_number * txs_in_batch + single_transactions); + ASSERT_EQ(boost::size(tx_sequence->value.batches()), + batches_number + single_transactions); +} diff --git a/test/module/shared_model/builders/protobuf/transport_builder_test.cpp b/test/module/shared_model/builders/protobuf/transport_builder_test.cpp index c298cd855d..2a8c932f5d 100644 --- a/test/module/shared_model/builders/protobuf/transport_builder_test.cpp +++ b/test/module/shared_model/builders/protobuf/transport_builder_test.cpp @@ -474,7 +474,7 @@ TEST_F(TransportBuilderTest, BlockVariantWithInvalidBlock) { * AND it containcs 0 transactions */ TEST_F(TransportBuilderTest, TransactionSequenceEmpty) { - std::vector tr; + std::vector tr; auto val = framework::expected::val( TransportBuilder Date: Thu, 12 Jul 2018 18:02:43 +0300 Subject: [PATCH 40/97] Fix build of Binary Testing Framework (#1558) Additionally, gmock removed as unnecessary dependency Signed-off-by: Igor Egorov --- test/integration/binary/CMakeLists.txt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/integration/binary/CMakeLists.txt b/test/integration/binary/CMakeLists.txt index e3daa79633..96aa78ec4b 100644 --- a/test/integration/binary/CMakeLists.txt +++ b/test/integration/binary/CMakeLists.txt @@ -21,8 +21,7 @@ if (SWIG_PYTHON) binaries_test.cpp ) target_link_libraries(binary_test - gtest - gmock + gtest::main shared_model_proto_backend integration_framework bindings From 6164acc3c5dbc8cf3e77040244007f883ba9444f Mon Sep 17 00:00:00 2001 From: Nikita Alekseev Date: Fri, 13 Jul 2018 11:35:22 +0300 Subject: [PATCH 41/97] Implement non-copyable block (#1542) Implement non-copyable block Signed-off-by: Nikita Alekseev --- CMakeLists.txt | 5 + .../ametsuchi/impl/postgres_block_query.cpp | 24 ++-- irohad/network/impl/block_loader_impl.cpp | 7 +- shared_model/backend/protobuf/CMakeLists.txt | 1 + shared_model/backend/protobuf/block.hpp | 100 +++++--------- .../common_objects/noncopyable_proto.hpp | 41 ++++++ shared_model/backend/protobuf/impl/block.cpp | 80 +++++++++++ .../builders/protobuf/unsigned_proto.hpp | 31 ++++- .../protobuf/json_proto_converter.hpp | 2 +- shared_model/utils/lazy_initializer.hpp | 17 +++ test/benchmark/bm_proto_creation.cpp | 125 +++++++++++++----- .../irohad/ametsuchi/block_query_test.cpp | 2 +- .../irohad/network/block_loader_test.cpp | 9 +- .../validation/chain_validation_test.cpp | 8 +- .../protobuf/transport_builder_test.cpp | 37 +++--- 15 files changed, 339 insertions(+), 150 deletions(-) create mode 100644 shared_model/backend/protobuf/common_objects/noncopyable_proto.hpp create mode 100644 shared_model/backend/protobuf/impl/block.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index d67b73096c..b3236b7b34 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -113,6 +113,11 @@ include_directories( SET(IROHA_ROOT_PROJECT ON) +# Boost uses RTTI to perform some actions (such as type erasure). +# This is slow. This flag forces boost to use other methods, +# which are generally faster +add_definitions(-DBOOST_NO_RTTI) + include(FeatureSummary) include(cmake/functions.cmake) include(cmake/dependencies.cmake) diff --git a/irohad/ametsuchi/impl/postgres_block_query.cpp b/irohad/ametsuchi/impl/postgres_block_query.cpp index 079aa466ad..82e885f82b 100644 --- a/irohad/ametsuchi/impl/postgres_block_query.cpp +++ b/irohad/ametsuchi/impl/postgres_block_query.cpp @@ -53,21 +53,17 @@ namespace iroha { } return rxcpp::observable<>::range(height, to) .flat_map([this](const auto &i) { - auto block = block_store_.get(i) | [](const auto &bytes) { - return shared_model::converters::protobuf::jsonToModel< - shared_model::proto::Block>(bytesToString(bytes)); - }; - if (not block) { - log_->error("error while converting from JSON"); - } - return rxcpp::observable<>::create( - [block{std::move(block)}](const auto &s) { - if (block) { - s.on_next(std::make_shared( - block.value())); - } - s.on_completed(); + [i, this](const auto &block_subscriber) { + block_store_.get(i) | [](const auto &bytes) { + return shared_model::converters::protobuf::jsonToModel< + shared_model::proto::Block>(bytesToString(bytes)); + } | [&block_subscriber](auto &&block) { + block_subscriber.on_next( + std::make_shared( + std::move(block))); + }; + block_subscriber.on_completed(); }); }); } diff --git a/irohad/network/impl/block_loader_impl.cpp b/irohad/network/impl/block_loader_impl.cpp index 1f31f2f612..9d74f17480 100644 --- a/irohad/network/impl/block_loader_impl.cpp +++ b/irohad/network/impl/block_loader_impl.cpp @@ -98,16 +98,15 @@ rxcpp::observable> BlockLoaderImpl::retrieveBlocks( .build(block) .match( // success case - [&subscriber]( - const iroha::expected::Value + [&subscriber](iroha::expected::Value &result) { subscriber.on_next( std::move(std::make_shared( - result.value))); + std::move(result.value)))); }, // fail case [this, - &context](const iroha::expected::Error &error) { + &context](iroha::expected::Error &error) { log_->error(error.error); context.TryCancel(); }); diff --git a/shared_model/backend/protobuf/CMakeLists.txt b/shared_model/backend/protobuf/CMakeLists.txt index 57fd1ede4e..5dee8f6609 100644 --- a/shared_model/backend/protobuf/CMakeLists.txt +++ b/shared_model/backend/protobuf/CMakeLists.txt @@ -4,6 +4,7 @@ # add_library(shared_model_proto_backend + impl/block.cpp impl/permissions.cpp commands/impl/proto_add_asset_quantity.cpp commands/impl/proto_add_peer.cpp diff --git a/shared_model/backend/protobuf/block.hpp b/shared_model/backend/protobuf/block.hpp index bed9ed4952..061c105a78 100644 --- a/shared_model/backend/protobuf/block.hpp +++ b/shared_model/backend/protobuf/block.hpp @@ -23,7 +23,7 @@ #include "backend/protobuf/common_objects/signature.hpp" #include "backend/protobuf/transaction.hpp" #include "backend/protobuf/util.hpp" -#include "common_objects/trivial_proto.hpp" +#include "common_objects/noncopyable_proto.hpp" #include "interfaces/common_objects/types.hpp" #include "block.pb.h" @@ -31,95 +31,57 @@ namespace shared_model { namespace proto { - class Block final : public CopyableProto { + class Block final : public NonCopyableProto { public: - template - explicit Block(BlockType &&block) - : CopyableProto(std::forward(block)) {} + using NonCopyableProto::NonCopyableProto; - Block(const Block &o) : Block(o.proto_) {} - - Block(Block &&o) noexcept : Block(std::move(o.proto_)) {} + Block(Block &&o) noexcept; + Block &operator=(Block &&o) noexcept; interface::types::TransactionsCollectionType transactions() - const override { - return *transactions_; - } + const override; - interface::types::HeightType height() const override { - return payload_.height(); - } + interface::types::HeightType height() const override; - const interface::types::HashType &prevHash() const override { - return *prev_hash_; - } + const interface::types::HashType &prevHash() const override; - const interface::types::BlobType &blob() const override { - return *blob_; - } + const interface::types::BlobType &blob() const override; - interface::types::SignatureRangeType signatures() const override { - return *signatures_; - } + interface::types::SignatureRangeType signatures() const override; - // TODO Alexey Chernyshov - 2018-03-28 - - // rework code duplication after fix protobuf - // https://soramitsu.atlassian.net/browse/IR-1175 bool addSignature(const crypto::Signed &signed_blob, - const crypto::PublicKey &public_key) override { - // if already has such signature - if (std::find_if(signatures_->begin(), - signatures_->end(), - [&public_key](const auto &signature) { - return signature.publicKey() == public_key; - }) - != signatures_->end()) { - return false; - } - - auto sig = proto_->add_signatures(); - sig->set_signature(crypto::toBinaryString(signed_blob)); - sig->set_pubkey(crypto::toBinaryString(public_key)); - - signatures_.invalidate(); - return true; - } - - interface::types::TimestampType createdTime() const override { - return payload_.created_time(); - } - - interface::types::TransactionsNumberType txsNumber() const override { - return payload_.tx_number(); - } - - const interface::types::BlobType &payload() const override { - return *payload_blob_; - } + const crypto::PublicKey &public_key) override; + + interface::types::TimestampType createdTime() const override; + + interface::types::TransactionsNumberType txsNumber() const override; + + const interface::types::BlobType &payload() const override; private: // lazy template using Lazy = detail::LazyInitializer; - const iroha::protocol::Block::Payload &payload_{proto_->payload()}; + iroha::protocol::Block::Payload &payload_{*proto_.mutable_payload()}; - const Lazy> transactions_{[this] { - return std::vector(payload_.transactions().begin(), - payload_.transactions().end()); + Lazy> transactions_{[this] { + return std::vector( + payload_.mutable_transactions()->begin(), + payload_.mutable_transactions()->end()); }}; - const Lazy blob_{ - [this] { return makeBlob(*proto_); }}; + Lazy blob_{ + [this] { return makeBlob(proto_); }}; - const Lazy prev_hash_{[this] { - return interface::types::HashType(proto_->payload().prev_block_hash()); + Lazy prev_hash_{[this] { + return interface::types::HashType(proto_.payload().prev_block_hash()); }}; - const Lazy> signatures_{[this] { - auto signatures = proto_->signatures() + Lazy> signatures_{[this] { + auto signatures = proto_.signatures() | boost::adaptors::transformed([](const auto &x) { return proto::Signature(x); }); @@ -127,7 +89,7 @@ namespace shared_model { signatures.end()); }}; - const Lazy payload_blob_{ + Lazy payload_blob_{ [this] { return makeBlob(payload_); }}; }; } // namespace proto diff --git a/shared_model/backend/protobuf/common_objects/noncopyable_proto.hpp b/shared_model/backend/protobuf/common_objects/noncopyable_proto.hpp new file mode 100644 index 0000000000..1ccb970f67 --- /dev/null +++ b/shared_model/backend/protobuf/common_objects/noncopyable_proto.hpp @@ -0,0 +1,41 @@ +/** + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef IROHA_NONCOPYABLE_PROTO_HPP +#define IROHA_NONCOPYABLE_PROTO_HPP + +/** + * Generic class for handling proto objects which are not intended to be copied. + * @tparam Iface is interface to inherit from + * @tparam Proto is protobuf container + * @tparam Impl is implementation of Iface + */ +template +class NonCopyableProto : public Iface { + public: + using TransportType = Proto; + + /* + * Construct object from transport. Transport can be moved or copied. + */ + template + NonCopyableProto(Transport &&ref) : proto_(std::forward(ref)){} + + NonCopyableProto(const NonCopyableProto &o) = delete; + NonCopyableProto &operator=(const NonCopyableProto &o) = delete; + + const Proto &getTransport() const { + return proto_; + } + + protected: + typename Iface::ModelType *clone() const override final { + return new Impl(proto_); + } + + Proto proto_; +}; + +#endif // IROHA_NONCOPYABLE_PROTO_HPP diff --git a/shared_model/backend/protobuf/impl/block.cpp b/shared_model/backend/protobuf/impl/block.cpp new file mode 100644 index 0000000000..759c9d8d18 --- /dev/null +++ b/shared_model/backend/protobuf/impl/block.cpp @@ -0,0 +1,80 @@ +/** + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "backend/protobuf/block.hpp" + +namespace shared_model { + namespace proto { + Block::Block(Block &&o) noexcept : NonCopyableProto(std::move(o.proto_)) {} + + Block &Block::operator=(Block &&o) noexcept { + proto_ = std::move(o.proto_); + payload_ = *proto_.mutable_payload(); + + transactions_.invalidate(); + blob_.invalidate(); + prev_hash_.invalidate(); + signatures_.invalidate(); + payload_blob_.invalidate(); + + return *this; + } + + interface::types::TransactionsCollectionType Block::transactions() const { + return *transactions_; + } + + interface::types::HeightType Block::height() const { + return payload_.height(); + } + + const interface::types::HashType &Block::prevHash() const { + return *prev_hash_; + } + + const interface::types::BlobType &Block::blob() const { + return *blob_; + } + + interface::types::SignatureRangeType Block::signatures() const { + return *signatures_; + } + + // TODO Alexey Chernyshov - 2018-03-28 - + // rework code duplication from transaction, block after fix protobuf + // https://soramitsu.atlassian.net/browse/IR-1175 + bool Block::addSignature(const crypto::Signed &signed_blob, + const crypto::PublicKey &public_key) { + // if already has such signature + if (std::find_if(signatures_->begin(), + signatures_->end(), + [&public_key](const auto &signature) { + return signature.publicKey() == public_key; + }) + != signatures_->end()) { + return false; + } + + auto sig = proto_.add_signatures(); + sig->set_signature(crypto::toBinaryString(signed_blob)); + sig->set_pubkey(crypto::toBinaryString(public_key)); + + signatures_.invalidate(); + return true; + } + + interface::types::TimestampType Block::createdTime() const { + return payload_.created_time(); + } + + interface::types::TransactionsNumberType Block::txsNumber() const { + return payload_.tx_number(); + } + + const interface::types::BlobType &Block::payload() const { + return *payload_blob_; + } + } // namespace proto +} // namespace shared_model diff --git a/shared_model/builders/protobuf/unsigned_proto.hpp b/shared_model/builders/protobuf/unsigned_proto.hpp index b9d2ca2dd3..fde59bcc43 100644 --- a/shared_model/builders/protobuf/unsigned_proto.hpp +++ b/shared_model/builders/protobuf/unsigned_proto.hpp @@ -28,6 +28,9 @@ namespace shared_model { /** * Class for holding built but still unsigned objects * @tparam T - type of object received from builder + * + * NOTE: finish() moves internal object, so calling methods after + * finish() throws an exception */ template class UnsignedWrapper { @@ -42,6 +45,23 @@ namespace shared_model { explicit UnsignedWrapper(T &&o) : object_(std::move(o)) {} + UnsignedWrapper(UnsignedWrapper &&w) + : object_(std::move(w.object_)), + object_finalized_(w.object_finalized_) { + w.object_finalized_ = true; + } + + UnsignedWrapper &operator=(UnsignedWrapper &&w) { + object_ = std::move(w.object_); + object_finalized_ = w.object_finalized_; + w.object_finalized_ = true; + + return *this; + } + + UnsignedWrapper(const UnsignedWrapper &o) = default; + UnsignedWrapper &operator=(const UnsignedWrapper &w) = default; + /** * Add signature and retrieve signed result * @param signature - signature to add @@ -50,6 +70,9 @@ namespace shared_model { UnsignedWrapper &signAndAddSignature(const crypto::Keypair &keypair) { auto signedBlob = shared_model::crypto::CryptoSigner<>::sign( shared_model::crypto::Blob(object_.payload()), keypair); + if (object_finalized_) { + throw std::runtime_error("object has already been finalized"); + } object_.addSignature(signedBlob, keypair.publicKey()); // TODO: 05.12.2017 luckychess think about false case return *this; @@ -63,7 +86,12 @@ namespace shared_model { if (boost::size(object_.signatures()) == 0) { throw std::invalid_argument("Cannot get object without signatures"); } - return object_; + if (object_finalized_) { + throw std::runtime_error("object has already been finalized"); + } + + object_finalized_ = true; + return std::move(object_); } interface::types::HashType hash() { @@ -80,6 +108,7 @@ namespace shared_model { private: T object_; + bool object_finalized_{false}; }; } // namespace proto } // namespace shared_model diff --git a/shared_model/converters/protobuf/json_proto_converter.hpp b/shared_model/converters/protobuf/json_proto_converter.hpp index 4444a31e17..5854b0728c 100644 --- a/shared_model/converters/protobuf/json_proto_converter.hpp +++ b/shared_model/converters/protobuf/json_proto_converter.hpp @@ -34,7 +34,7 @@ namespace shared_model { * @return json string */ template - std::string modelToJson(T message) { + std::string modelToJson(const T &message) { std::string result; google::protobuf::util::MessageToJsonString(message.getTransport(), &result); diff --git a/shared_model/utils/lazy_initializer.hpp b/shared_model/utils/lazy_initializer.hpp index bdcf135294..f299bde6c8 100644 --- a/shared_model/utils/lazy_initializer.hpp +++ b/shared_model/utils/lazy_initializer.hpp @@ -35,6 +35,23 @@ namespace shared_model { using GeneratorType = std::function; public: + /** + * Default constructor prevents compilation error in pre 7.2 gcc. + * + * Short explanation: gcc needs default constructor + * when the constructor is inherited (via using Base::Base) + * if the inherited class has default initalizers for any of its members. + * This can be found in shared_model/backend/protobuf/protobuf/block.hpp + * where Lazy fields are initialized via default initializers. + * + * Note that this does not result in default constructor being called, so + * the resulting code is still correct. This is an issue of gcc compiler + * which is resolved in 7.2 + * + * For more details: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=67054 + */ + LazyInitializer(); + template explicit LazyInitializer(T &&generator) : generator_(std::forward(generator)) {} diff --git a/test/benchmark/bm_proto_creation.cpp b/test/benchmark/bm_proto_creation.cpp index a0cdcf9d15..521442eafa 100644 --- a/test/benchmark/bm_proto_creation.cpp +++ b/test/benchmark/bm_proto_creation.cpp @@ -11,8 +11,8 @@ * * The purpose of this benchmark is to keep track of performance costs related * to blocks and proposals copying/moving. - * - * Each benchmark runs transaction() and commands() call to + * + * Each benchmark runs transaction() and commands() call to * initialize possibly lazy fields. */ @@ -97,64 +97,127 @@ void checkLoop(const T &obj) { } } -BENCHMARK_F(BlockBenchmark, CopyTest)(benchmark::State &st) { - auto block = complete_builder.build(); +/** + * Runs a function and updates timer of the given state + */ +template +void runBenchmark(benchmark::State &st, Func &&f) { + auto start = std::chrono::high_resolution_clock::now(); + f(); + auto end = std::chrono::high_resolution_clock::now(); + + auto elapsed_seconds = + std::chrono::duration_cast>( + end - start); + st.SetIterationTime(elapsed_seconds.count()); +} + +/** + * Benchmark block creation by copying protobuf object + */ +BENCHMARK_DEFINE_F(BlockBenchmark, TransportCopyTest)(benchmark::State &st) { while (st.KeepRunning()) { - shared_model::proto::Block copy(block.getTransport()); + auto block = complete_builder.build(); - checkLoop(copy); + runBenchmark(st, [&block] { + shared_model::proto::Block copy(block.getTransport()); + checkLoop(copy); + }); } } -BENCHMARK_F(BlockBenchmark, MoveTest)(benchmark::State &st) { - auto block = complete_builder.build(); - +/** + * Benchmark block creation by moving protobuf object + */ +BENCHMARK_DEFINE_F(BlockBenchmark, TransportMoveTest)(benchmark::State &st) { while (st.KeepRunning()) { - shared_model::proto::Block copy(std::move(block.getTransport())); + auto block = complete_builder.build(); + iroha::protocol::Block proto_block = block.getTransport(); - checkLoop(copy); + runBenchmark(st, [&proto_block] { + shared_model::proto::Block copy(std::move(proto_block)); + checkLoop(copy); + }); } } -BENCHMARK_F(BlockBenchmark, CloneTest)(benchmark::State &st) { - auto block = complete_builder.build(); - +/** + * Benchmark block creation by moving another block + */ +BENCHMARK_DEFINE_F(BlockBenchmark, MoveTest)(benchmark::State &st) { while (st.KeepRunning()) { - auto copy = clone(block); + auto block = complete_builder.build(); - checkLoop(*copy); + runBenchmark(st, [&block] { + shared_model::proto::Block copy = std::move(block); + checkLoop(copy); + }); } } -BENCHMARK_F(ProposalBenchmark, CopyTest)(benchmark::State &st) { - auto proposal = complete_builder.build(); - +/** + * Benchmark block creation by cloning another block + */ +BENCHMARK_DEFINE_F(BlockBenchmark, CloneTest)(benchmark::State &st) { while (st.KeepRunning()) { - shared_model::proto::Proposal copy(proposal.getTransport()); + auto block = complete_builder.build(); - checkLoop(copy); + runBenchmark(st, [&block] { + auto copy = clone(block); + checkLoop(*copy); + }); } } -BENCHMARK_F(ProposalBenchmark, MoveTest)(benchmark::State &state) { - auto proposal = complete_builder.build(); - - while (state.KeepRunning()) { - shared_model::proto::Proposal copy(std::move(proposal.getTransport())); +/** + * Benchmark proposal creation by copying another proposal + */ +BENCHMARK_DEFINE_F(ProposalBenchmark, CopyTest)(benchmark::State &st) { + while (st.KeepRunning()) { + auto proposal = complete_builder.build(); - checkLoop(copy); + runBenchmark(st, [&proposal] { + shared_model::proto::Proposal copy(proposal.getTransport()); + checkLoop(copy); + }); } } -BENCHMARK_F(ProposalBenchmark, CloneTest)(benchmark::State &st) { - auto proposal = complete_builder.build(); +/** + * Benchmark proposal creation by moving another proposal + */ +BENCHMARK_DEFINE_F(ProposalBenchmark, MoveTest)(benchmark::State &st) { + while (st.KeepRunning()) { + auto proposal = complete_builder.build(); + + runBenchmark(st, [&] { + shared_model::proto::Proposal copy(std::move(proposal.getTransport())); + checkLoop(copy); + }); + } +} +/** + * Benchmark proposal creation by cloning another proposal + */ +BENCHMARK_DEFINE_F(ProposalBenchmark, CloneTest)(benchmark::State &st) { while (st.KeepRunning()) { - auto copy = clone(proposal); + auto proposal = complete_builder.build(); - checkLoop(*copy); + runBenchmark(st, [&proposal] { + auto copy = clone(proposal); + checkLoop(*copy); + }); } } +BENCHMARK_REGISTER_F(BlockBenchmark, MoveTest)->UseManualTime(); +BENCHMARK_REGISTER_F(BlockBenchmark, TransportMoveTest)->UseManualTime(); +BENCHMARK_REGISTER_F(BlockBenchmark, TransportCopyTest)->UseManualTime(); +BENCHMARK_REGISTER_F(BlockBenchmark, CloneTest)->UseManualTime(); +BENCHMARK_REGISTER_F(ProposalBenchmark, CopyTest)->UseManualTime(); +BENCHMARK_REGISTER_F(ProposalBenchmark, MoveTest)->UseManualTime(); +BENCHMARK_REGISTER_F(ProposalBenchmark, CloneTest)->UseManualTime(); + BENCHMARK_MAIN(); diff --git a/test/module/irohad/ametsuchi/block_query_test.cpp b/test/module/irohad/ametsuchi/block_query_test.cpp index 471b7d4751..0e38d77b7a 100644 --- a/test/module/irohad/ametsuchi/block_query_test.cpp +++ b/test/module/irohad/ametsuchi/block_query_test.cpp @@ -89,7 +89,7 @@ class BlockQueryTest : public AmetsuchiTest { .prevHash(block1.hash()) .build(); - for (const auto &b : {block1, block2}) { + for (const auto &b : {std::move(block1), std::move(block2)}) { file->add(b.height(), iroha::stringToBytes( shared_model::converters::protobuf::modelToJson(b))); diff --git a/test/module/irohad/network/block_loader_test.cpp b/test/module/irohad/network/block_loader_test.cpp index 36f8bacefd..44af03020a 100644 --- a/test/module/irohad/network/block_loader_test.cpp +++ b/test/module/irohad/network/block_loader_test.cpp @@ -149,8 +149,7 @@ TEST_F(BlockLoaderTest, ValidWhenOneBlock) { EXPECT_CALL(*storage, getTopBlock()) .WillOnce(Return(iroha::expected::makeValue(wBlock(clone(block))))); EXPECT_CALL(*storage, getBlocksFrom(block.height() + 1)) - .WillOnce(Return(rxcpp::observable<>::just(top_block).map( - [](auto &&x) { return wBlock(clone(x)); }))); + .WillOnce(Return(rxcpp::observable<>::just(wBlock(clone(top_block))))); auto wrapper = make_test_subscriber(loader->retrieveBlocks(peer_key), 1); wrapper.subscribe( @@ -215,8 +214,7 @@ TEST_F(BlockLoaderTest, ValidWhenBlockPresent) { EXPECT_CALL(*peer_query, getLedgerPeers()) .WillOnce(Return(std::vector{peer})); EXPECT_CALL(*storage, getBlocksFrom(1)) - .WillOnce(Return(rxcpp::observable<>::just(requested).map( - [](auto &&x) { return wBlock(clone(x)); }))); + .WillOnce(Return(rxcpp::observable<>::just(wBlock(clone(requested))))); auto block = loader->retrieveBlock(peer_key, requested.hash()); ASSERT_TRUE(block); @@ -236,8 +234,7 @@ TEST_F(BlockLoaderTest, ValidWhenBlockMissing) { EXPECT_CALL(*peer_query, getLedgerPeers()) .WillOnce(Return(std::vector{peer})); EXPECT_CALL(*storage, getBlocksFrom(1)) - .WillOnce(Return(rxcpp::observable<>::just(present).map( - [](auto &&x) { return wBlock(clone(x)); }))); + .WillOnce(Return(rxcpp::observable<>::just(wBlock(clone(present))))); auto block = loader->retrieveBlock(peer_key, kPrevHash); ASSERT_FALSE(block); diff --git a/test/module/irohad/validation/chain_validation_test.cpp b/test/module/irohad/validation/chain_validation_test.cpp index f14c4df853..7ecacb8733 100644 --- a/test/module/irohad/validation/chain_validation_test.cpp +++ b/test/module/irohad/validation/chain_validation_test.cpp @@ -135,10 +135,10 @@ TEST_F(ChainValidationTest, FailWhenNoSupermajority) { */ TEST_F(ChainValidationTest, ValidWhenValidateChainFromOnePeer) { // Valid previous hash, has supermajority, correct peers subset => valid - auto block = getBlockBuilder().build(); + auto block = std::make_shared(getBlockBuilder().build()); rxcpp::observable> block_observable = rxcpp::observable<>::just(block).map([](auto &&x) { - return std::shared_ptr(clone(x)); + return std::shared_ptr(x); }); EXPECT_CALL(*supermajority_checker, hasSupermajority(_, _)) @@ -149,10 +149,10 @@ TEST_F(ChainValidationTest, ValidWhenValidateChainFromOnePeer) { EXPECT_CALL( *storage, apply(testing::Truly([&](const shared_model::interface::Block &rhs) { - return rhs == block; + return rhs == *block; }), _)) - .WillOnce(InvokeArgument<1>(ByRef(block), ByRef(*query), ByRef(hash))); + .WillOnce(InvokeArgument<1>(ByRef(*block), ByRef(*query), ByRef(hash))); ASSERT_TRUE(validator->validateChain(block_observable, *storage)); } diff --git a/test/module/shared_model/builders/protobuf/transport_builder_test.cpp b/test/module/shared_model/builders/protobuf/transport_builder_test.cpp index 2a8c932f5d..ec5b7dc1ba 100644 --- a/test/module/shared_model/builders/protobuf/transport_builder_test.cpp +++ b/test/module/shared_model/builders/protobuf/transport_builder_test.cpp @@ -203,10 +203,13 @@ class TransportBuilderTest : public ::testing::Test { * @param successCase function invoking if value exists * @param failCase function invoking when error returned */ - template - void testTransport(T orig_model, - std::function &)> successCase, - std::function &)> failCase) { + template + void testTransport(const T &orig_model, + SuccessCase &&successCase, + FailCase &&failCase) { auto proto_model = orig_model.getTransport(); auto built_model = TransportBuilder().build(proto_model); @@ -238,8 +241,7 @@ class TransportBuilderTest : public ::testing::Test { */ TEST_F(TransportBuilderTest, TransactionCreationTest) { auto orig_model = createTransaction(); - testTransport( + testTransport( orig_model, [&orig_model](const Value &model) { ASSERT_EQ(model.value.getTransport().SerializeAsString(), @@ -258,8 +260,7 @@ TEST_F(TransportBuilderTest, TransactionCreationTest) { */ TEST_F(TransportBuilderTest, InvalidTransactionCreationTest) { auto orig_model = createInvalidTransaction(); - testTransport( + testTransport( orig_model, [](const Value) { FAIL(); }, [](const Error &) { SUCCEED(); }); @@ -274,8 +275,7 @@ TEST_F(TransportBuilderTest, InvalidTransactionCreationTest) { */ TEST_F(TransportBuilderTest, QueryCreationTest) { auto orig_model = createQuery(); - testTransport( + testTransport( orig_model, [&orig_model](const Value &model) { ASSERT_EQ(model.value.getTransport().SerializeAsString(), @@ -291,8 +291,7 @@ TEST_F(TransportBuilderTest, QueryCreationTest) { */ TEST_F(TransportBuilderTest, InvalidQueryCreationTest) { auto orig_model = createInvalidQuery(); - testTransport( + testTransport( orig_model, [](const Value) { FAIL(); }, [](const Error &) { SUCCEED(); }); @@ -307,7 +306,7 @@ TEST_F(TransportBuilderTest, InvalidQueryCreationTest) { */ TEST_F(TransportBuilderTest, BlockCreationTest) { auto orig_model = createBlock(); - testTransport( + testTransport( orig_model, [&orig_model](const Value &model) { ASSERT_EQ(model.value.getTransport().SerializeAsString(), @@ -323,10 +322,10 @@ TEST_F(TransportBuilderTest, BlockCreationTest) { */ TEST_F(TransportBuilderTest, InvalidBlockCreationTest) { auto orig_model = createInvalidBlock(); - testTransport( + testTransport( orig_model, - [](const Value) { FAIL(); }, - [](const Error &) { SUCCEED(); }); + [](const Value> &) { FAIL(); }, + [](const Error &) { SUCCEED(); }); } //-------------------------------------PROPOSAL------------------------------------- @@ -338,7 +337,7 @@ TEST_F(TransportBuilderTest, InvalidBlockCreationTest) { */ TEST_F(TransportBuilderTest, ProposalCreationTest) { auto orig_model = createProposal(); - testTransport( + testTransport( orig_model, [&orig_model](const Value &model) { ASSERT_EQ(model.value.getTransport().SerializeAsString(), @@ -355,7 +354,7 @@ TEST_F(TransportBuilderTest, ProposalCreationTest) { */ TEST_F(TransportBuilderTest, DISABLED_EmptyProposalCreationTest) { auto orig_model = createEmptyProposal(); - testTransport( + testTransport( orig_model, [](const Value) { FAIL(); }, [](const Error &) { SUCCEED(); }); @@ -370,7 +369,7 @@ TEST_F(TransportBuilderTest, DISABLED_EmptyProposalCreationTest) { */ TEST_F(TransportBuilderTest, EmptyBlockCreationTest) { auto orig_model = createEmptyBlock(); - testTransport( + testTransport( orig_model, [&orig_model](const Value &model) { ASSERT_EQ(model.value.getTransport().SerializeAsString(), From 617c49672284e276ab72a50bf4a91b18f90c9a7c Mon Sep 17 00:00:00 2001 From: Andrei Lebedev Date: Fri, 13 Jul 2018 12:42:02 +0300 Subject: [PATCH 42/97] Rewrite StatusStream with rxcpp (#1541) * Rewrite StatusStream with rxcpp * Minor fixes for types and logger calls * Review fixes; fix irohad_test using wrong example path * Fix tx hash setter in stateless valid case * Add client id to debug log Signed-off-by: Andrei Lebedev --- irohad/main/application.cpp | 2 +- irohad/torii/command_service.hpp | 63 +++-- irohad/torii/impl/command_service.cpp | 258 +++++++++--------- libs/common/is_any.hpp | 34 +++ libs/common/timeout.hpp | 216 +++++++++++++++ .../transaction_responses/tx_response.hpp | 6 +- test/module/iroha-cli/client_test.cpp | 5 +- .../irohad/torii/torii_service_test.cpp | 5 +- test/system/CMakeLists.txt | 1 + test/system/irohad_test.cpp | 2 +- 10 files changed, 435 insertions(+), 157 deletions(-) create mode 100644 libs/common/is_any.hpp create mode 100644 libs/common/timeout.hpp diff --git a/irohad/main/application.cpp b/irohad/main/application.cpp index e23db789bf..b24fa545fc 100644 --- a/irohad/main/application.cpp +++ b/irohad/main/application.cpp @@ -268,7 +268,7 @@ void Irohad::initTransactionCommandService() { std::make_shared(pcs, mst_processor); command_service = std::make_shared<::torii::CommandService>( - tx_processor, storage, proposal_delay_); + tx_processor, storage, std::chrono::seconds(1), 2 * proposal_delay_); log_->info("[Init] => command service"); } diff --git a/irohad/torii/command_service.hpp b/irohad/torii/command_service.hpp index 87ea0c69f0..468710d6c3 100644 --- a/irohad/torii/command_service.hpp +++ b/irohad/torii/command_service.hpp @@ -41,12 +41,14 @@ namespace torii { * @param pb_factory - model->protobuf and vice versa converter * @param tx_processor - processor of received transactions * @param block_query - to query transactions outside the cache - * @param proposal_delay - time of a one proposal propagation. + * @param initial_timeout - streaming timeout when tx is not received + * @param nonfinal_timeout - streaming timeout when tx is being processed */ CommandService( std::shared_ptr tx_processor, std::shared_ptr storage, - std::chrono::milliseconds proposal_delay); + std::chrono::milliseconds initial_timeout, + std::chrono::milliseconds nonfinal_timeout); /** * Disable copying in any way to prevent potential issues with common @@ -102,12 +104,11 @@ namespace torii { * the some final transaction status (which cannot change anymore) * @param request- TxStatusRequest object which identifies transaction * uniquely - * @param response_writer - grpc::ServerWriter which can repeatedly send - * transaction statuses back to the client + * @return observable with transaction statuses */ - void StatusStream( - iroha::protocol::TxStatusRequest const &request, - grpc::ServerWriter &response_writer); + rxcpp::observable< + std::shared_ptr> + StatusStream(const shared_model::crypto::Hash &hash); /** * StatusStream call via grpc @@ -118,30 +119,50 @@ namespace torii { * transaction statuses back to the client * @return - grpc::Status */ - virtual grpc::Status StatusStream( - grpc::ServerContext *context, - const iroha::protocol::TxStatusRequest *request, - grpc::ServerWriter *response_writer) - override; + grpc::Status StatusStream(grpc::ServerContext *context, + const iroha::protocol::TxStatusRequest *request, + grpc::ServerWriter + *response_writer) override; private: - bool checkCacheAndSend( - const boost::optional &resp, - grpc::ServerWriter &response_writer) - const; - - bool isFinalStatus(const iroha::protocol::TxStatus &status) const; + /** + * Execute events scheduled in run loop until it is not empty and the + * subscriber is active + * @param subscription - tx status subscription + * @param run_loop - gRPC thread run loop + */ + inline void handleEvents(rxcpp::composite_subscription &subscription, + rxcpp::schedulers::run_loop &run_loop); - private: using CacheType = iroha::cache::Cache; std::shared_ptr tx_processor_; std::shared_ptr storage_; - std::chrono::milliseconds proposal_delay_; - std::chrono::milliseconds start_tx_processing_duration_; + std::chrono::milliseconds initial_timeout_; + std::chrono::milliseconds nonfinal_timeout_; std::shared_ptr cache_; + + /** + * Mutex for propagating stateless validation status + */ + std::mutex stateless_tx_status_notifier_mutex_; + + /** + * Subject with stateless validation statuses + */ + rxcpp::subjects::subject< + std::shared_ptr> + stateless_notifier_; + + /** + * Observable with all transaction statuses + */ + rxcpp::observable< + std::shared_ptr> + responses_; + logger::Logger log_; }; diff --git a/irohad/torii/impl/command_service.cpp b/irohad/torii/impl/command_service.cpp index 5a682f9932..fc357ff63d 100644 --- a/irohad/torii/impl/command_service.cpp +++ b/irohad/torii/impl/command_service.cpp @@ -26,27 +26,35 @@ #include "builders/protobuf/transaction_responses/proto_transaction_status_builder.hpp" #include "builders/protobuf/transport_builder.hpp" #include "common/byteutils.hpp" +#include "common/is_any.hpp" +#include "common/timeout.hpp" #include "common/types.hpp" #include "cryptography/default_hash_provider.hpp" #include "endpoint.pb.h" #include "validators/default_validator.hpp" -using namespace std::chrono_literals; - namespace torii { CommandService::CommandService( std::shared_ptr tx_processor, std::shared_ptr storage, - std::chrono::milliseconds proposal_delay) + std::chrono::milliseconds initial_timeout, + std::chrono::milliseconds nonfinal_timeout) : tx_processor_(tx_processor), storage_(storage), - proposal_delay_(proposal_delay), - start_tx_processing_duration_(1s), + initial_timeout_(initial_timeout), + nonfinal_timeout_(nonfinal_timeout), cache_(std::make_shared()), + // merge with mutex, since notifications can be made from different + // threads + // TODO 11.07.2018 andrei rework status handling with event bus IR-1517 + responses_(tx_processor_->transactionNotifier().merge( + rxcpp::serialize_one_worker( + rxcpp::schedulers::make_current_thread()), + stateless_notifier_.get_observable())), log_(logger::log("CommandService")) { // Notifier for all clients - tx_processor_->transactionNotifier().subscribe([this](auto iroha_response) { + responses_.subscribe([this](auto iroha_response) { // find response for this tx in cache; if status of received response // isn't "greater" than cached one, dismiss received one auto proto_response = @@ -84,7 +92,8 @@ namespace torii { } // setting response - response.set_tx_hash(tx_hash.toString()); + response.set_tx_hash( + shared_model::crypto::toBinaryString(tx_hash)); response.set_tx_status( iroha::protocol::TxStatus::STATELESS_VALIDATION_SUCCESS); @@ -113,7 +122,12 @@ namespace torii { log_->debug("Torii: adding item to cache: {}, status {} ", tx_hash.hex(), response.tx_status()); - cache_->addItem(tx_hash, response); + // transactions can be handled from multiple threads, therefore a lock is + // required + std::lock_guard lock(stateless_tx_status_notifier_mutex_); + stateless_notifier_.get_subscriber().on_next( + std::make_shared( + std::move(response))); } grpc::Status CommandService::Torii( @@ -155,140 +169,126 @@ namespace torii { return grpc::Status::OK; } - void CommandService::StatusStream( - iroha::protocol::TxStatusRequest const &request, - grpc::ServerWriter &response_writer) { - auto resp = cache_->findItem(shared_model::crypto::Hash(request.tx_hash())); - if (checkCacheAndSend(resp, response_writer)) { - return; - } - auto finished = std::make_shared>(false); - auto subscription = rxcpp::composite_subscription(); - auto request_hash = - std::make_shared(request.tx_hash()); - - /// Condition variable to ensure that current method will not return before - /// transaction is processed or a timeout reached. It blocks current thread - /// and waits for thread from subscribe() to unblock. - auto cv = std::make_shared(); - - log_->debug("StatusStream before subscribe(), hash: {}", - request_hash->hex()); - - tx_processor_->transactionNotifier() - .filter([&request_hash](auto response) { - return response->transactionHash() == *request_hash; - }) - .subscribe( - subscription, - [&, finished]( - std::shared_ptr - iroha_response) { - auto proto_response = std::static_pointer_cast< - shared_model::proto::TransactionResponse>(iroha_response); + /** + * Statuses considered final for streaming. Observable stops value emission + * after receiving a value of one of the following types + * @tparam T concrete response type + */ + template + constexpr bool FinalStatusValue = + iroha::is_any, + shared_model::interface::StatelessFailedTxResponse, + shared_model::interface::StatefulFailedTxResponse, + shared_model::interface::CommittedTxResponse, + shared_model::interface::MstExpiredResponse>::value; - log_->debug("subscribe new status: {}, hash {}", - proto_response->toString(), - proto_response->transactionHash().hex()); - - iroha::protocol::ToriiResponse resp_sub = - proto_response->getTransport(); + rxcpp::observable< + std::shared_ptr> + CommandService::StatusStream(const shared_model::crypto::Hash &hash) { + using ResponsePtrType = + std::shared_ptr; + ResponsePtrType initial_status = + clone(shared_model::proto::TransactionResponse( + cache_->findItem(hash).value_or([&] { + log_->debug("tx not received: {}", hash.toString()); + return shared_model::proto::TransactionStatusBuilder() + .txHash(hash) + .notReceived() + .build() + .getTransport(); + }()))); + return responses_ + // prepend initial status + .start_with(initial_status) + // select statuses with requested hash + .filter( + [&](auto response) { return response->transactionHash() == hash; }) + // successfully complete the observable if final status is received. + // final status is included in the observable + .lift([](rxcpp::subscriber dest) { + return rxcpp::make_subscriber( + dest, [=](ResponsePtrType response) { + dest.on_next(response); + iroha::visit_in_place( + response->get(), + [dest](const auto &resp) + -> std::enable_if_t> { + dest.on_completed(); + }, + [](const auto &resp) + -> std::enable_if_t< + not FinalStatusValue>{}); + }); + }); + } - if (isFinalStatus(resp_sub.tx_status())) { - response_writer.WriteLast(resp_sub, grpc::WriteOptions()); - *finished = true; - cv->notify_one(); - } else { - response_writer.Write(resp_sub); - } - }); + grpc::Status CommandService::StatusStream( + grpc::ServerContext *context, + const iroha::protocol::TxStatusRequest *request, + grpc::ServerWriter *response_writer) { + rxcpp::schedulers::run_loop rl; - std::mutex wait_subscription; - std::unique_lock lock(wait_subscription); + auto current_thread = + rxcpp::observe_on_one_worker(rxcpp::schedulers::make_run_loop(rl)); - log_->debug("StatusStream waiting start, hash: {}", request_hash->hex()); + rxcpp::composite_subscription subscription; - /// we expect that start_tx_processing_duration_ will be enough - /// to at least start tx processing. - /// Otherwise we think there is no such tx at all. - cv->wait_for(lock, start_tx_processing_duration_); + auto hash = shared_model::crypto::Hash(request->tx_hash()); - log_->debug("StatusStream waiting finish, hash: {}", request_hash->hex()); + static auto client_id_format = boost::format("Peer: '%s', %s"); + std::string client_id = + (client_id_format % context->peer() % hash.toString()).str(); - if (not*finished) { - resp = cache_->findItem(shared_model::crypto::Hash(request.tx_hash())); - if (not resp) { - log_->warn("StatusStream request processing timeout, hash: {}", - request_hash->hex()); - // TODO 05/03/2018 andrei IR-1046 Server-side shared model object - // factories with move semantics - auto resp_none = shared_model::proto::TransactionStatusBuilder() - .txHash(*request_hash) - .notReceived() - .build(); - response_writer.WriteLast(resp_none.getTransport(), - grpc::WriteOptions()); - } else { - log_->debug( - "Tx processing was started but unfinished, awaiting more, hash: {}", - request_hash->hex()); - /// We give it 2*proposal_delay time until timeout. - cv->wait_for(lock, 2 * proposal_delay_); + StatusStream(hash) + // convert to transport objects + .map([&](auto response) { + log_->debug("mapped {}, {}", response->toString(), client_id); + return std::static_pointer_cast< + shared_model::proto::TransactionResponse>(response) + ->getTransport(); + }) + // set a corresponding observable timeout based on status value + .lift( + iroha::makeTimeout( + [&](const auto &response) { + return response.tx_status() + == iroha::protocol::TxStatus::NOT_RECEIVED + ? initial_timeout_ + : nonfinal_timeout_; + }, + current_thread)) + // complete the observable if client is disconnected + .take_while([=](const auto &) { + auto is_cancelled = context->IsCancelled(); + if (is_cancelled) { + log_->debug("client unsubscribed, {}", client_id); + } + return not is_cancelled; + }) + .subscribe(subscription, + [&](iroha::protocol::ToriiResponse response) { + if (response_writer->Write(response)) { + log_->debug("status written, {}", client_id); + } + }, + [&](std::exception_ptr ep) { + log_->debug("processing timeout, {}", client_id); + }, + [&] { log_->debug("stream done, {}", client_id); }); - /// status can be in the cache if it was finalized before we subscribed - if (not*finished) { - log_->debug("Transaction {} still not finished", request_hash->hex()); - resp = - cache_->findItem(shared_model::crypto::Hash(request.tx_hash())); - log_->debug("Status of tx {} in cache: {}", - request_hash->hex(), - resp->tx_status()); - } - } - } else { - log_->debug("StatusStream request processed successfully, hash: {}", - request_hash->hex()); - } - subscription.unsubscribe(); - log_->debug("StatusStream unsubscribed"); - } + // run loop while subscription is active or there are pending events in the + // queue + handleEvents(subscription, rl); - grpc::Status CommandService::StatusStream( - grpc::ServerContext *context, - const iroha::protocol::TxStatusRequest *request, - grpc::ServerWriter *response_writer) { - StatusStream(*request, *response_writer); + log_->debug("status stream done, {}", client_id); return grpc::Status::OK; } - bool CommandService::checkCacheAndSend( - const boost::optional &resp, - grpc::ServerWriter &response_writer) - const { - if (resp) { - if (isFinalStatus(resp->tx_status())) { - log_->debug("Transaction {} in service cache and final", - iroha::bytestringToHexstring(resp->tx_hash())); - response_writer.WriteLast(*resp, grpc::WriteOptions()); - return true; - } - log_->debug("Transaction {} in service cache and not final", - iroha::bytestringToHexstring(resp->tx_hash())); - response_writer.Write(*resp); + void CommandService::handleEvents(rxcpp::composite_subscription &subscription, + rxcpp::schedulers::run_loop &run_loop) { + while (subscription.is_subscribed() or not run_loop.empty()) { + run_loop.dispatch(); } - return false; } - bool CommandService::isFinalStatus( - const iroha::protocol::TxStatus &status) const { - using namespace iroha::protocol; - std::vector final_statuses = { - TxStatus::STATELESS_VALIDATION_FAILED, - TxStatus::STATEFUL_VALIDATION_FAILED, - TxStatus::NOT_RECEIVED, - TxStatus::COMMITTED}; - return (std::find( - std::begin(final_statuses), std::end(final_statuses), status)) - != std::end(final_statuses); - } } // namespace torii diff --git a/libs/common/is_any.hpp b/libs/common/is_any.hpp new file mode 100644 index 0000000000..bf2d560719 --- /dev/null +++ b/libs/common/is_any.hpp @@ -0,0 +1,34 @@ +/** + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef IROHA_IS_ANY_HPP +#define IROHA_IS_ANY_HPP + +#include + +namespace iroha { + + template + struct is_any : std::false_type {}; + + template + struct is_any : std::is_same {}; + + /** + * Disjunctive type check. Returns true if first type is contained in provided + * list, false otherwise + * @tparam T first type + * @tparam First head of types list + * @tparam Rest tail of types list + */ + template + struct is_any + : std::integral_constant::value + || is_any::value> {}; + +} // namespace iroha + +#endif // IROHA_IS_ANY_HPP diff --git a/libs/common/timeout.hpp b/libs/common/timeout.hpp new file mode 100644 index 0000000000..fb66605758 --- /dev/null +++ b/libs/common/timeout.hpp @@ -0,0 +1,216 @@ +/** + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef IROHA_TIMEOUT_HPP +#define IROHA_TIMEOUT_HPP + +#include + +namespace iroha { + + /** + * This class is mostly the same as rxcpp::operators::timeout, + * the only change is that it accepts a selector lambda which generates + * a duration based on observable value instead of a fixed duration + * Return an observable that terminates with timeout_error if a particular + * timespan has passed without emitting another item from the source + * observable + * Timespan is generated with selector from the last received value + * @tparam T value type + * @tparam Selector the type of the transforming function + * which returns time interval + * @tparam Coordination the type of the scheduler + */ + template + struct timeout { + typedef rxcpp::util::decay_t source_value_type; + typedef rxcpp::util::decay_t coordination_type; + typedef typename coordination_type::coordinator_type coordinator_type; + typedef rxcpp::util::decay_t select_type; + + struct timeout_values { + timeout_values(select_type s, coordination_type c) + : selector(std::move(s)), coordination(c) {} + + select_type selector; + coordination_type coordination; + }; + timeout_values initial; + + timeout(select_type s, coordination_type coordination) + : initial(std::move(s), coordination) {} + + template + struct timeout_observer { + typedef timeout_observer this_type; + typedef rxcpp::util::decay_t value_type; + typedef rxcpp::util::decay_t dest_type; + typedef rxcpp::observer observer_type; + + struct timeout_subscriber_values : public timeout_values { + timeout_subscriber_values(rxcpp::composite_subscription cs, + dest_type d, + timeout_values v, + coordinator_type c) + : timeout_values(v), + cs(std::move(cs)), + dest(std::move(d)), + coordinator(std::move(c)), + worker(coordinator.get_worker()), + index(0) {} + + rxcpp::composite_subscription cs; + dest_type dest; + coordinator_type coordinator; + rxcpp::schedulers::worker worker; + mutable std::size_t index; + }; + typedef std::shared_ptr state_type; + state_type state; + + timeout_observer(rxcpp::composite_subscription cs, + dest_type d, + timeout_values v, + coordinator_type c) + : state(std::make_shared( + timeout_subscriber_values( + std::move(cs), std::move(d), v, std::move(c)))) { + auto localState = state; + + auto disposer = [=](const rxcpp::schedulers::schedulable &) { + localState->cs.unsubscribe(); + localState->dest.unsubscribe(); + localState->worker.unsubscribe(); + }; + auto selectedDisposer = on_exception( + [&]() { return localState->coordinator.act(disposer); }, + localState->dest); + if (selectedDisposer.empty()) { + return; + } + + localState->dest.add( + [=]() { localState->worker.schedule(selectedDisposer.get()); }); + localState->cs.add( + [=]() { localState->worker.schedule(selectedDisposer.get()); }); + } + + static std::function + produce_timeout(std::size_t id, state_type state) { + auto produce = [id, state](const rxcpp::schedulers::schedulable &) { + if (id != state->index) + return; + + state->dest.on_error(std::make_exception_ptr( + rxcpp::timeout_error("timeout has occurred"))); + }; + + auto selectedProduce = on_exception( + [&]() { return state->coordinator.act(produce); }, state->dest); + if (selectedProduce.empty()) { + return std::function(); + } + + return std::function( + selectedProduce.get()); + } + + template + void on_next(Value &&v) const { + auto localState = state; + + auto selected = on_exception( + [&]() { return localState->selector(std::forward(v)); }, + localState->dest); + if (selected.empty()) { + return; + } + + auto work = [v, localState, period = std::move(selected.get())]( + const rxcpp::schedulers::schedulable &) { + auto new_id = ++localState->index; + auto produce_time = localState->worker.now() + period; + + localState->dest.on_next(v); + localState->worker.schedule(produce_time, + produce_timeout(new_id, localState)); + }; + auto selectedWork = + on_exception([&]() { return localState->coordinator.act(work); }, + localState->dest); + if (selectedWork.empty()) { + return; + } + localState->worker.schedule(selectedWork.get()); + } + + void on_error(std::exception_ptr e) const { + auto localState = state; + auto work = [e, localState](const rxcpp::schedulers::schedulable &) { + localState->dest.on_error(e); + }; + auto selectedWork = + on_exception([&]() { return localState->coordinator.act(work); }, + localState->dest); + if (selectedWork.empty()) { + return; + } + localState->worker.schedule(selectedWork.get()); + } + + void on_completed() const { + auto localState = state; + auto work = [localState](const rxcpp::schedulers::schedulable &) { + localState->dest.on_completed(); + }; + auto selectedWork = + on_exception([&]() { return localState->coordinator.act(work); }, + localState->dest); + if (selectedWork.empty()) { + return; + } + localState->worker.schedule(selectedWork.get()); + } + + static rxcpp::subscriber make(dest_type d, + timeout_values v) { + auto cs = rxcpp::composite_subscription(); + auto coordinator = v.coordination.create_coordinator(); + + return rxcpp::make_subscriber( + cs, + observer_type(this_type( + cs, std::move(d), std::move(v), std::move(coordinator)))); + } + }; + + template + auto operator()(Subscriber dest) const + -> decltype(timeout_observer::make(std::move(dest), + initial)) { + return timeout_observer::make(std::move(dest), initial); + } + }; + + template < + typename T, + typename Selector, + typename Coordination, + class ResolvedSelector = rxcpp::util::decay_t, + class Duration = decltype( + std::declval()((std::declval>()))), + class Enabled = rxcpp::util::enable_if_all_true_type_t< + rxcpp::is_coordination, + rxcpp::util::is_duration>, + class Timeout = + timeout>> + static auto makeTimeout(Selector &&s, Coordination &&cn) { + return Timeout(std::forward(s), std::forward(cn)); + }; + +} // namespace iroha + +#endif // IROHA_TIMEOUT_HPP diff --git a/shared_model/interfaces/transaction_responses/tx_response.hpp b/shared_model/interfaces/transaction_responses/tx_response.hpp index e47fcaa0ab..dd56105038 100644 --- a/shared_model/interfaces/transaction_responses/tx_response.hpp +++ b/shared_model/interfaces/transaction_responses/tx_response.hpp @@ -56,7 +56,11 @@ namespace shared_model { // ------------------------| Primitive override |------------------------- std::string toString() const override { - return boost::apply_visitor(detail::ToStringVisitor(), get()); + return detail::PrettyStringBuilder() + .init("TransactionResponse") + .append("transactionHash", transactionHash().hex()) + .append(boost::apply_visitor(detail::ToStringVisitor(), get())) + .finalize(); } bool operator==(const ModelType &rhs) const override { diff --git a/test/module/iroha-cli/client_test.cpp b/test/module/iroha-cli/client_test.cpp index 4839d65dde..90d289c247 100644 --- a/test/module/iroha-cli/client_test.cpp +++ b/test/module/iroha-cli/client_test.cpp @@ -53,7 +53,8 @@ using namespace iroha::validation; using namespace shared_model::proto; using namespace std::chrono_literals; -constexpr std::chrono::milliseconds proposal_delay = 10s; +constexpr std::chrono::milliseconds initial_timeout = 1s; +constexpr std::chrono::milliseconds nonfinal_timeout = 2 * 10s; class ClientServerTest : public testing::Test { public: @@ -100,7 +101,7 @@ class ClientServerTest : public testing::Test { //----------- Server run ---------------- runner ->append(std::make_unique( - tx_processor, storage, proposal_delay)) + tx_processor, storage, initial_timeout, nonfinal_timeout)) .append(std::make_unique(qpi)) .run() .match( diff --git a/test/module/irohad/torii/torii_service_test.cpp b/test/module/irohad/torii/torii_service_test.cpp index d791895ca8..fa7ae3ef8b 100644 --- a/test/module/irohad/torii/torii_service_test.cpp +++ b/test/module/irohad/torii/torii_service_test.cpp @@ -42,7 +42,8 @@ using namespace iroha::ametsuchi; using namespace iroha::torii; using namespace std::chrono_literals; -constexpr std::chrono::milliseconds proposal_delay = 10s; +constexpr std::chrono::milliseconds initial_timeout = 1s; +constexpr std::chrono::milliseconds nonfinal_timeout = 2 * 10s; using iroha::Commit; @@ -114,7 +115,7 @@ class ToriiServiceTest : public testing::Test { //----------- Server run ---------------- runner ->append(std::make_unique( - tx_processor, storage, proposal_delay)) + tx_processor, storage, initial_timeout, nonfinal_timeout)) .run() .match( [this](iroha::expected::Value port) { diff --git a/test/system/CMakeLists.txt b/test/system/CMakeLists.txt index 6462cd0118..a8c3393ac8 100644 --- a/test/system/CMakeLists.txt +++ b/test/system/CMakeLists.txt @@ -8,3 +8,4 @@ target_link_libraries(irohad_test ) add_dependencies(irohad_test irohad) add_definitions(-DPATHIROHAD="${PROJECT_BINARY_DIR}/bin") +add_definitions(-DPATHEXAMPLE="${PROJECT_SOURCE_DIR}/example") diff --git a/test/system/irohad_test.cpp b/test/system/irohad_test.cpp index d71dc9f74d..7f83e076dc 100644 --- a/test/system/irohad_test.cpp +++ b/test/system/irohad_test.cpp @@ -89,7 +89,7 @@ class IrohadTest : public testing::Test { void setPaths() { path_irohad_ = boost::filesystem::path(PATHIROHAD); irohad_executable = path_irohad_ / "irohad"; - path_example_ = path_irohad_.parent_path().parent_path() / "example"; + path_example_ = boost::filesystem::path(PATHEXAMPLE); path_config_ = path_example_ / "config.sample"; path_genesis_ = path_example_ / "genesis.block"; path_keypair_ = path_example_ / "node0"; From 8a1f5c2dc1adeeb64cbf4cb601e9820a66ae7600 Mon Sep 17 00:00:00 2001 From: victordrobny Date: Fri, 13 Jul 2018 12:52:09 +0300 Subject: [PATCH 43/97] Feature/soci (#1505) Pqxx replaced by SOCI. Some bugs are fixed by Andrei Signed-off-by: victordrobny --- cmake/Modules/Findpq.cmake | 5 +- cmake/Modules/Findpqxx.cmake | 46 -- cmake/Modules/Findsoci.cmake | 91 ++++ cmake/dependencies.cmake | 6 +- docker/dependencies/Dockerfile | 29 +- docker/develop/Dockerfile | 26 +- docs/source/guides/build.rst | 2 +- irohad/ametsuchi/CMakeLists.txt | 3 +- .../ametsuchi/impl/mutable_storage_impl.cpp | 27 +- .../ametsuchi/impl/mutable_storage_impl.hpp | 16 +- .../ametsuchi/impl/postgres_block_index.cpp | 79 ++-- .../ametsuchi/impl/postgres_block_index.hpp | 12 +- .../ametsuchi/impl/postgres_block_query.cpp | 155 ++++--- .../ametsuchi/impl/postgres_block_query.hpp | 20 +- ...gres_ordering_service_persistent_state.cpp | 97 ++--- ...gres_ordering_service_persistent_state.hpp | 17 +- .../ametsuchi/impl/postgres_wsv_command.cpp | 368 ++++++++-------- .../ametsuchi/impl/postgres_wsv_command.hpp | 33 +- irohad/ametsuchi/impl/postgres_wsv_common.hpp | 138 +++--- irohad/ametsuchi/impl/postgres_wsv_query.cpp | 401 +++++++++--------- irohad/ametsuchi/impl/postgres_wsv_query.hpp | 17 +- irohad/ametsuchi/impl/storage_impl.cpp | 140 +++--- irohad/ametsuchi/impl/storage_impl.hpp | 11 +- irohad/ametsuchi/impl/temporary_wsv_impl.cpp | 28 +- irohad/ametsuchi/impl/temporary_wsv_impl.hpp | 13 +- snap/snapcraft.yaml | 23 +- test/module/irohad/ametsuchi/CMakeLists.txt | 3 +- .../irohad/ametsuchi/ametsuchi_fixture.hpp | 26 +- .../irohad/ametsuchi/ametsuchi_test.cpp | 6 +- .../irohad/ametsuchi/block_query_test.cpp | 20 +- .../ametsuchi/block_query_transfer_test.cpp | 22 +- .../irohad/ametsuchi/storage_init_test.cpp | 22 +- .../ametsuchi/wsv_query_command_test.cpp | 46 +- test/system/CMakeLists.txt | 3 +- test/system/irohad_test.cpp | 16 +- 35 files changed, 959 insertions(+), 1008 deletions(-) delete mode 100644 cmake/Modules/Findpqxx.cmake create mode 100644 cmake/Modules/Findsoci.cmake diff --git a/cmake/Modules/Findpq.cmake b/cmake/Modules/Findpq.cmake index 1457ca2357..2a018af746 100644 --- a/cmake/Modules/Findpq.cmake +++ b/cmake/Modules/Findpq.cmake @@ -20,12 +20,10 @@ find_package_handle_standard_args(pq DEFAULT_MSG pg_config_EXECUTABLE ) - set(URL https://git.postgresql.org/git/postgresql.git) set(VERSION 029386ccbddd0a33d481b94e511f5219b03e6636) set_target_description(pq "C postgres client library" ${URL} ${VERSION}) - if (NOT pq_FOUND) externalproject_add(postgres_postgres GIT_REPOSITORY ${URL} @@ -52,6 +50,9 @@ endif () get_filename_component(pq_LIBRARY_DIR ${pq_LIBRARY} DIRECTORY) mark_as_advanced(pq_LIBRARY_DIR) +get_filename_component(pg_config_EXECUTABLE_DIR ${pg_config_EXECUTABLE} DIRECTORY) +mark_as_advanced(pg_config_EXECUTABLE_DIR) + set_target_properties(pq PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${pq_INCLUDE_DIR};${postgres_INCLUDE_DIR}" IMPORTED_LOCATION ${pq_LIBRARY} diff --git a/cmake/Modules/Findpqxx.cmake b/cmake/Modules/Findpqxx.cmake deleted file mode 100644 index 66d61df98b..0000000000 --- a/cmake/Modules/Findpqxx.cmake +++ /dev/null @@ -1,46 +0,0 @@ -add_library(pqxx UNKNOWN IMPORTED) - -find_path(pqxx_INCLUDE_DIR pqxx/pqxx) -mark_as_advanced(pqxx_INCLUDE_DIR) - -find_library(pqxx_LIBRARY pqxx) -mark_as_advanced(pqxx_LIBRARY) - -find_package_handle_standard_args(pqxx DEFAULT_MSG - pqxx_INCLUDE_DIR - pqxx_LIBRARY - ) - -set(URL https://github.com/jtv/libpqxx.git) -set(VERSION c4d4f4b4e6ecaf85de770a030f3a1cdc1b3a79ae) # 6.1.0 -set_target_description(pqxx "C++ bindings for postgres client library" ${URL} ${VERSION}) - -if (NOT pqxx_FOUND) - externalproject_add(jtv_libpqxx - GIT_REPOSITORY ${URL} - GIT_TAG ${VERSION} - CONFIGURE_COMMAND ${CMAKE_COMMAND} -E env CXXFLAGS=${CMAKE_CXX_FLAGS} CPPFLAGS=-I${postgres_INCLUDE_DIR} PG_CONFIG=${pg_config_EXECUTABLE} ./configure --disable-documentation --with-postgres-include=${pq_INCLUDE_DIR} --with-postgres-lib=${pq_INCLUDE_DIR} - BUILD_IN_SOURCE 1 - BUILD_COMMAND ${MAKE} - BUILD_BYPRODUCTS ${EP_PREFIX}/src/jtv_libpqxx/src/.libs/libpqxx.a - ${EP_PREFIX}/src/jtv_libpqxx/src/libpqxx.la - ${EP_PREFIX}/src/jtv_libpqxx/src/.libs/libpqxx.la - ${EP_PREFIX}/src/jtv_libpqxx/src/.libs/libpqxx.lai - INSTALL_COMMAND "" # remove install step - TEST_COMMAND "" # remove test step - UPDATE_COMMAND "" # remove update step - ) - externalproject_get_property(jtv_libpqxx source_dir) - set(pqxx_INCLUDE_DIR ${source_dir}/include) - set(pqxx_LIBRARY ${source_dir}/src/.libs/libpqxx.a) - file(MAKE_DIRECTORY ${pqxx_INCLUDE_DIR}) - - add_dependencies(jtv_libpqxx pq) - add_dependencies(pqxx jtv_libpqxx) -endif () - -set_target_properties(pqxx PROPERTIES - INTERFACE_INCLUDE_DIRECTORIES ${pqxx_INCLUDE_DIR} - IMPORTED_LOCATION ${pqxx_LIBRARY} - INTERFACE_LINK_LIBRARIES "pq" - ) diff --git a/cmake/Modules/Findsoci.cmake b/cmake/Modules/Findsoci.cmake new file mode 100644 index 0000000000..4b5c891c51 --- /dev/null +++ b/cmake/Modules/Findsoci.cmake @@ -0,0 +1,91 @@ +set(_SOCI_REQUIRED_VARS SOCI_INCLUDE_DIR SOCI_LIBRARY SOCI_postgresql_PLUGIN) + +add_library(SOCI::core UNKNOWN IMPORTED) +add_library(SOCI::postgresql UNKNOWN IMPORTED) + +find_path( + SOCI_INCLUDE_DIR soci.h + PATH_SUFFIXES "" "soci" + DOC "Soci (http://soci.sourceforge.net) include directory") +mark_as_advanced(SOCI_INCLUDE_DIR) +get_filename_component(_SOCI_INCLUDE_PARENT_DIR ${SOCI_INCLUDE_DIR} DIRECTORY) +set(SOCI_INCLUDE_DIRS ${SOCI_INCLUDE_DIR} ${_SOCI_INCLUDE_PARENT_DIR}) +mark_as_advanced(SOCI_INCLUDE_DIRS) + +find_library( + SOCI_LIBRARY + NAMES soci_core + HINTS ${SOCI_INCLUDE_DIR}/.. + PATH_SUFFIXES lib${LIB_SUFFIX}) +mark_as_advanced(SOCI_LIBRARY) + +find_library( + SOCI_postgresql_PLUGIN + NAMES soci_postgresql + HINTS ${SOCI_INCLUDE_DIR}/.. + PATH_SUFFIXES lib${LIB_SUFFIX}) +mark_as_advanced(SOCI_postgresql_PLUGIN) + +get_filename_component(SOCI_LIBRARY_DIR ${SOCI_LIBRARY} PATH) +mark_as_advanced(SOCI_LIBRARY_DIR) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(soci DEFAULT_MSG ${_SOCI_REQUIRED_VARS}) + +set(URL https://github.com/SOCI/soci) +set(VERSION 111b50af8c3876ea392367640b4bd83b4f903ab8) # 3.2.3 +set_target_description(soci "The C++ Database Access Library" ${URL} ${VERSION}) + +if (NOT soci_FOUND) + externalproject_add(soci_soci + GIT_REPOSITORY ${URL} + GIT_TAG ${VERSION} + CONFIGURE_COMMAND ${CMAKE_COMMAND} + -G${CMAKE_GENERATOR} + -H${EP_PREFIX}/src/soci_soci/src + -B${EP_PREFIX}/src/soci_soci-build + -DCMAKE_INCLUDE_PATH=${pq_INCLUDE_DIR} + -DCMAKE_LIBRARY_PATH=${pq_INCLUDE_DIR} + -DCMAKE_PROGRAM_PATH=${pg_config_EXECUTABLE_DIR} + -DCMAKE_CXX_FLAGS=-I${postgres_INCLUDE_DIR} + -DCMAKE_INSTALL_PREFIX=${EP_PREFIX} + -DWITH_BOOST=ON + -DWITH_DB2=OFF + -DWITH_FIREBIRD=OFF + -DWITH_MYSQL=OFF + -DWITH_ODBC=OFF + -DWITH_ORACLE=OFF + -DWITH_POSTGRESQL=ON + -DWITH_SQLITE3=OFF + BUILD_BYPRODUCTS ${EP_PREFIX}/src/soci_soci-build/lib/${CMAKE_STATIC_LIBRARY_PREFIX}soci_core${CMAKE_STATIC_LIBRARY_SUFFIX} + ${EP_PREFIX}/src/soci_soci-build/lib/${CMAKE_STATIC_LIBRARY_PREFIX}soci_postgresql${CMAKE_STATIC_LIBRARY_SUFFIX} + TEST_COMMAND "" # remove test step + UPDATE_COMMAND "" # remove update step + ) + externalproject_get_property(soci_soci binary_dir) + set(SOCI_INCLUDE_DIRS ${EP_PREFIX}/include ${EP_PREFIX}/include/soci) + set(SOCI_LIBRARY ${binary_dir}/lib/${CMAKE_STATIC_LIBRARY_PREFIX}soci_core${CMAKE_STATIC_LIBRARY_SUFFIX}) + set(SOCI_postgresql_PLUGIN ${binary_dir}/lib/${CMAKE_STATIC_LIBRARY_PREFIX}soci_postgresql${CMAKE_STATIC_LIBRARY_SUFFIX}) + file(MAKE_DIRECTORY ${EP_PREFIX}/include/soci) + + add_dependencies(soci_soci pq) + add_dependencies(SOCI::core soci_soci) + add_dependencies(SOCI::postgresql soci_soci) +endif () + +set_target_properties(SOCI::core PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES "${SOCI_INCLUDE_DIRS}" + IMPORTED_LOCATION "${SOCI_LIBRARY}" + INTERFACE_LINK_LIBRARIES dl + ) + +set_target_properties(SOCI::postgresql PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES "${SOCI_INCLUDE_DIRS}" + IMPORTED_LOCATION "${SOCI_postgresql_PLUGIN}" + INTERFACE_LINK_LIBRARIES pq + ) + +if(ENABLE_LIBS_PACKAGING) + add_install_step_for_lib(${SOCI_LIBRARY}) + add_install_step_for_lib(${SOCI_postgresql_PLUGIN}) +endif() diff --git a/cmake/dependencies.cmake b/cmake/dependencies.cmake index 69ac283fe9..f849b4e5ec 100644 --- a/cmake/dependencies.cmake +++ b/cmake/dependencies.cmake @@ -44,10 +44,10 @@ find_package(rapidjson) ########################## find_package(pq) -##########################a -# pqxx # ########################## -find_package(pqxx) +# SOCI # +########################## +find_package(soci) ################################ # gflags # diff --git a/docker/dependencies/Dockerfile b/docker/dependencies/Dockerfile index 346371aaac..040e8be607 100644 --- a/docker/dependencies/Dockerfile +++ b/docker/dependencies/Dockerfile @@ -187,18 +187,27 @@ RUN set -e; \ # remove rm -rf /tmp/postgresql -# install pqxx +# install soci 3.2.3 RUN set -e; \ - export PATH="/opt/dependencies/libpq/bin:$PATH"; \ - git clone https://github.com/jtv/libpqxx /tmp/libpqxx; \ - (cd /tmp/libpqxx ; git checkout 5b17abce5ac2b1a2f8278718405b7ade8bb30ae9); \ - curl -L -o /tmp/libpqxx/config/config.guess 'http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=6b2374c79506ee82a8b440f6d1ca293e2e2e2463'; \ - curl -L -o /tmp/libpqxx/config/config.sub 'http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=6b2374c79506ee82a8b440f6d1ca293e2e2e2463'; \ - (cd /tmp/libpqxx ; /tmp/libpqxx/configure --disable-documentation --with-pic --prefix=/opt/dependencies/libpqxx); \ - make -j${PARALLELISM} -C /tmp/libpqxx; \ - make -C /tmp/libpqxx install; \ + git clone https://github.com/SOCI/soci /tmp/soci; \ + (cd /tmp/soci ; git checkout 111b50af8c3876ea392367640b4bd83b4f903ab8); \ + cmake \ + -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} \ + -DWITH_BOOST=ON \ + -DWITH_DB2=OFF \ + -DWITH_FIREBIRD=OFF \ + -DWITH_MYSQL=OFF \ + -DWITH_ODBC=OFF \ + -DWITH_ORACLE=OFF \ + -DWITH_POSTGRESQL=ON \ + -DWITH_SQLITE3=OFF \ + -H/tmp/soci/src \ + -B/tmp/soci/build \ + -DCMAKE_PREFIX_PATH="/opt/dependencies/libpq" \ + -DCMAKE_INSTALL_PREFIX=/opt/dependencies/soci; \ + cmake --build /tmp/soci/build --target install; \ ldconfig; \ - rm -rf /tmp/libpqxx + rm -rf /tmp/soci # install tbb RUN set -e; \ diff --git a/docker/develop/Dockerfile b/docker/develop/Dockerfile index 1834483fed..ab85deeca7 100644 --- a/docker/develop/Dockerfile +++ b/docker/develop/Dockerfile @@ -177,17 +177,25 @@ RUN set -e; \ # remove rm -rf /tmp/postgresql -# install pqxx +# install soci 3.2.3 RUN set -e; \ - git clone https://github.com/jtv/libpqxx /tmp/libpqxx; \ - (cd /tmp/libpqxx ; git checkout 5b17abce5ac2b1a2f8278718405b7ade8bb30ae9); \ - curl -L -o /tmp/libpqxx/config/config.guess 'http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=6b2374c79506ee82a8b440f6d1ca293e2e2e2463'; \ - curl -L -o /tmp/libpqxx/config/config.sub 'http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=6b2374c79506ee82a8b440f6d1ca293e2e2e2463'; \ - (cd /tmp/libpqxx ; /tmp/libpqxx/configure --disable-documentation --with-pic); \ - make -j${PARALLELISM} -C /tmp/libpqxx; \ - make -C /tmp/libpqxx install; \ + git clone https://github.com/SOCI/soci /tmp/soci; \ + (cd /tmp/soci ; git checkout 111b50af8c3876ea392367640b4bd83b4f903ab8); \ + cmake \ + -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} \ + -DWITH_BOOST=ON \ + -DWITH_DB2=OFF \ + -DWITH_FIREBIRD=OFF \ + -DWITH_MYSQL=OFF \ + -DWITH_ODBC=OFF \ + -DWITH_ORACLE=OFF \ + -DWITH_POSTGRESQL=ON \ + -DWITH_SQLITE3=OFF \ + -H/tmp/soci/src \ + -B/tmp/soci/build; \ + cmake --build /tmp/soci/build --target install; \ ldconfig; \ - rm -rf /tmp/libpqxx + rm -rf /tmp/soci # install tbb RUN set -e; \ diff --git a/docs/source/guides/build.rst b/docs/source/guides/build.rst index 2a46387808..a49dfb50d7 100644 --- a/docs/source/guides/build.rst +++ b/docs/source/guides/build.rst @@ -116,7 +116,7 @@ to install all dependencies with Homebrew. .. code-block:: shell xcode-select --install - brew install cmake boost postgres grpc autoconf automake libtool golang libpqxx + brew install cmake boost postgres grpc autoconf automake libtool golang soci .. hint:: To install the Homebrew itself please run diff --git a/irohad/ametsuchi/CMakeLists.txt b/irohad/ametsuchi/CMakeLists.txt index fef18aa6a1..0a3be8a12d 100644 --- a/irohad/ametsuchi/CMakeLists.txt +++ b/irohad/ametsuchi/CMakeLists.txt @@ -16,11 +16,12 @@ add_library(ametsuchi target_link_libraries(ametsuchi logger rxcpp - pqxx libs_common command_execution query_execution shared_model_interfaces shared_model_proto_backend shared_model_stateless_validation + SOCI::core + SOCI::postgresql ) diff --git a/irohad/ametsuchi/impl/mutable_storage_impl.cpp b/irohad/ametsuchi/impl/mutable_storage_impl.cpp index 1898653b09..f29fa24f5e 100644 --- a/irohad/ametsuchi/impl/mutable_storage_impl.cpp +++ b/irohad/ametsuchi/impl/mutable_storage_impl.cpp @@ -29,21 +29,16 @@ namespace iroha { namespace ametsuchi { MutableStorageImpl::MutableStorageImpl( shared_model::interface::types::HashType top_hash, - std::unique_ptr connection, - std::unique_ptr transaction) + std::unique_ptr sql) : top_hash_(top_hash), - connection_(std::move(connection)), - transaction_(std::move(transaction)), - wsv_(std::make_unique(*transaction_)), - executor_(std::make_unique(*transaction_)), - block_index_(std::make_unique(*transaction_)), + sql_(std::move(sql)), + wsv_(std::make_shared(*sql_)), + executor_(std::make_shared(*sql_)), + block_index_(std::make_unique(*sql_)), + command_executor_(std::make_shared(wsv_, executor_)), committed(false), log_(logger::log("MutableStorage")) { - auto query = std::make_shared(*transaction_); - auto command = std::make_shared(*transaction_); - command_executor_ = - std::make_shared(CommandExecutor(query, command)); - transaction_->exec("BEGIN;"); + *sql_ << "BEGIN"; } bool MutableStorageImpl::apply( @@ -67,7 +62,7 @@ namespace iroha { execute_command); }; - transaction_->exec("SAVEPOINT savepoint_;"); + *sql_ << "SAVEPOINT savepoint_"; auto result = function(block, *wsv_, top_hash_) and std::all_of(block.transactions().begin(), block.transactions().end(), @@ -78,16 +73,16 @@ namespace iroha { block_index_->index(block); top_hash_ = block.hash(); - transaction_->exec("RELEASE SAVEPOINT savepoint_;"); + *sql_ << "RELEASE SAVEPOINT savepoint_"; } else { - transaction_->exec("ROLLBACK TO SAVEPOINT savepoint_;"); + *sql_ << "ROLLBACK TO SAVEPOINT savepoint_"; } return result; } MutableStorageImpl::~MutableStorageImpl() { if (not committed) { - transaction_->exec("ROLLBACK;"); + *sql_ << "ROLLBACK"; } } } // namespace ametsuchi diff --git a/irohad/ametsuchi/impl/mutable_storage_impl.hpp b/irohad/ametsuchi/impl/mutable_storage_impl.hpp index dd65aa9f43..9ca05de4eb 100644 --- a/irohad/ametsuchi/impl/mutable_storage_impl.hpp +++ b/irohad/ametsuchi/impl/mutable_storage_impl.hpp @@ -18,9 +18,8 @@ #ifndef IROHA_MUTABLE_STORAGE_IMPL_HPP #define IROHA_MUTABLE_STORAGE_IMPL_HPP +#include #include -#include -#include #include "ametsuchi/mutable_storage.hpp" #include "execution/command_executor.hpp" @@ -37,10 +36,8 @@ namespace iroha { friend class StorageImpl; public: - MutableStorageImpl( - shared_model::interface::types::HashType top_hash, - std::unique_ptr connection, - std::unique_ptr transaction); + MutableStorageImpl(shared_model::interface::types::HashType top_hash, + std::unique_ptr sql); bool apply( const shared_model::interface::Block &block, @@ -58,10 +55,9 @@ namespace iroha { std::map> block_store_; - std::unique_ptr connection_; - std::unique_ptr transaction_; - std::unique_ptr wsv_; - std::unique_ptr executor_; + std::unique_ptr sql_; + std::shared_ptr wsv_; + std::shared_ptr executor_; std::unique_ptr block_index_; std::shared_ptr command_executor_; diff --git a/irohad/ametsuchi/impl/postgres_block_index.cpp b/irohad/ametsuchi/impl/postgres_block_index.cpp index 593643b619..dd2e333800 100644 --- a/irohad/ametsuchi/impl/postgres_block_index.cpp +++ b/irohad/ametsuchi/impl/postgres_block_index.cpp @@ -31,18 +31,28 @@ namespace iroha { namespace ametsuchi { - PostgresBlockIndex::PostgresBlockIndex(pqxx::nontransaction &transaction) - : transaction_(transaction), - log_(logger::log("PostgresBlockIndex")), - execute_{makeExecuteOptional(transaction_, log_)} {} + bool execute(soci::statement &st) { + st.define_and_bind(); + try { + st.execute(true); + return true; + } catch (const std::exception &e) { + return false; + } + } + + PostgresBlockIndex::PostgresBlockIndex(soci::session &sql) + : sql_(sql), log_(logger::log("PostgresBlockIndex")) {} auto PostgresBlockIndex::indexAccountIdHeight(const std::string &account_id, const std::string &height) { - return this->execute( - "INSERT INTO height_by_account_set(account_id, height) " - "VALUES (" - + transaction_.quote(account_id) + ", " + transaction_.quote(height) - + ");"); + soci::statement st = + (sql_.prepare << "INSERT INTO height_by_account_set(account_id, " + "height) VALUES " + "(:id, :height)", + soci::use(account_id), + soci::use(height)); + return execute(st); } auto PostgresBlockIndex::indexAccountAssets( @@ -68,18 +78,20 @@ namespace iroha { auto ids = {account_id, command.srcAccountId(), command.destAccountId()}; + auto &asset_id = command.assetId(); // flat map accounts to unindexed keys boost::for_each(ids, [&](const auto &id) { - auto res = execute_( - "INSERT INTO index_by_id_height_asset(id, " - "height, asset_id, " - "index) " - "VALUES (" - + transaction_.quote(id) + ", " - + transaction_.quote(height) + ", " - + transaction_.quote(command.assetId()) + ", " - + transaction_.quote(index) + ");"); - status &= res != boost::none; + soci::statement st = + (sql_.prepare + << "INSERT INTO index_by_id_height_asset(id, " + "height, asset_id, " + "index) " + "VALUES (:id, :height, :asset_id, :index)", + soci::use(id), + soci::use(height), + soci::use(asset_id), + soci::use(index)); + status &= execute(st); }); return status; }, @@ -94,26 +106,31 @@ namespace iroha { block.transactions() | boost::adaptors::indexed(0), [&](const auto &tx) { const auto &creator_id = tx.value().creatorAccountId(); - const auto &hash = tx.value().hash().blob(); + const auto &hash = tx.value().hash().hex(); const auto &index = std::to_string(tx.index()); // tx hash -> block where hash is stored - this->execute("INSERT INTO height_by_hash(hash, height) VALUES (" - + transaction_.quote( - pqxx::binarystring(hash.data(), hash.size())) - + ", " + transaction_.quote(height) + ");"); + soci::statement st = + (sql_.prepare << "INSERT INTO height_by_hash(hash, height) " + "VALUES (:hash, " + ":height)", + soci::use(hash), + soci::use(height)); + execute(st); this->indexAccountIdHeight(creator_id, height); // to make index account_id:height -> list of tx indexes // (where tx is placed in the block) - execute_( - "INSERT INTO index_by_creator_height(creator_id, " - "height, index) " - "VALUES (" - + transaction_.quote(creator_id) + ", " - + transaction_.quote(height) + ", " + transaction_.quote(index) - + ");"); + st = + (sql_.prepare + << "INSERT INTO index_by_creator_height(creator_id, " + "height, index) " + "VALUES (:id, :height, :index)", + soci::use(creator_id), + soci::use(height), + soci::use(index)); + execute(st); this->indexAccountAssets( creator_id, height, index, tx.value().commands()); diff --git a/irohad/ametsuchi/impl/postgres_block_index.hpp b/irohad/ametsuchi/impl/postgres_block_index.hpp index 0ca4fe1fba..a2f0e20141 100644 --- a/irohad/ametsuchi/impl/postgres_block_index.hpp +++ b/irohad/ametsuchi/impl/postgres_block_index.hpp @@ -19,7 +19,6 @@ #define IROHA_POSTGRES_BLOCK_INDEX_HPP #include -#include #include "ametsuchi/impl/block_index.hpp" #include "ametsuchi/impl/postgres_wsv_common.hpp" @@ -30,7 +29,7 @@ namespace iroha { namespace ametsuchi { class PostgresBlockIndex : public BlockIndex { public: - explicit PostgresBlockIndex(pqxx::nontransaction &transaction); + explicit PostgresBlockIndex(soci::session &sql); void index(const shared_model::interface::Block &block) override; @@ -58,15 +57,8 @@ namespace iroha { const std::string &index, const shared_model::interface::Transaction::CommandsType &commands); - pqxx::nontransaction &transaction_; + soci::session &sql_; logger::Logger log_; - using ExecuteType = decltype(makeExecuteOptional(transaction_, log_)); - ExecuteType execute_; - - // TODO: refactor to return Result when it is introduced IR-775 - bool execute(const std::string &statement) noexcept { - return static_cast(execute_(statement)); - } }; } // namespace ametsuchi } // namespace iroha diff --git a/irohad/ametsuchi/impl/postgres_block_query.cpp b/irohad/ametsuchi/impl/postgres_block_query.cpp index 82e885f82b..c3214348de 100644 --- a/irohad/ametsuchi/impl/postgres_block_query.cpp +++ b/irohad/ametsuchi/impl/postgres_block_query.cpp @@ -25,23 +25,18 @@ namespace iroha { namespace ametsuchi { - PostgresBlockQuery::PostgresBlockQuery(pqxx::nontransaction &transaction, + PostgresBlockQuery::PostgresBlockQuery(soci::session &sql, KeyValueStorage &file_store) - : block_store_(file_store), - transaction_(transaction), - log_(logger::log("PostgresBlockIndex")), - execute_{makeExecuteOptional(transaction_, log_)} {} + : sql_(sql), + block_store_(file_store), + log_(logger::log("PostgresBlockIndex")) {} PostgresBlockQuery::PostgresBlockQuery( - std::unique_ptr connection, - std::unique_ptr transaction, - KeyValueStorage &file_store) - : connection_ptr_(std::move(connection)), - transaction_ptr_(std::move(transaction)), + std::unique_ptr sql_ptr, KeyValueStorage &file_store) + : sql_ptr_(std::move(sql_ptr)), + sql_(*sql_ptr_), block_store_(file_store), - transaction_(*transaction_ptr_), - log_(logger::log("PostgresBlockIndex")), - execute_{makeExecuteOptional(transaction_, log_)} {} + log_(logger::log("PostgresBlockIndex")) {} rxcpp::observable PostgresBlockQuery::getBlocks( shared_model::interface::types::HeightType height, uint32_t count) { @@ -83,43 +78,42 @@ namespace iroha { std::vector PostgresBlockQuery::getBlockIds( const shared_model::interface::types::AccountIdType &account_id) { - return execute_( - "SELECT DISTINCT height FROM height_by_account_set WHERE " - "account_id = " - + transaction_.quote(account_id) + ";") - | [&](const auto &result) - -> std::vector { - return transform( - result, [&](const auto &row) { - return row.at("height") - .template as(); - }); - }; + std::vector result; + soci::indicator ind; + std::string row; + soci::statement st = + (sql_.prepare << "SELECT DISTINCT height FROM height_by_account_set " + "WHERE account_id = :id", + soci::into(row, ind), + soci::use(account_id)); + st.execute(); + + processSoci(st, ind, row, [&result](std::string &r) { + result.push_back(std::stoull(r)); + }); + return result; } boost::optional PostgresBlockQuery::getBlockId(const shared_model::crypto::Hash &hash) { - boost::optional blockId; - return execute_("SELECT height FROM height_by_hash WHERE hash = " - + transaction_.quote(pqxx::binarystring( - hash.blob().data(), hash.blob().size())) - + ";") - | [&](const auto &result) - -> boost::optional< - shared_model::interface::types::HeightType> { - if (result.size() == 0) { - log_->info("No block with transaction {}", hash.toString()); - return boost::none; - } - return result[0] - .at("height") - .template as(); - }; + boost::optional blockId = boost::none; + boost::optional block_str; + auto hash_str = hash.hex(); + + sql_ << "SELECT height FROM height_by_hash WHERE hash = :hash", + soci::into(block_str), soci::use(hash_str); + if (block_str) { + blockId = std::stoull(block_str.get()); + } else { + log_->info("No block with transaction {}", hash.toString()); + } + return blockId; } - std::function PostgresBlockQuery::callback( + std::function &result)> + PostgresBlockQuery::callback( const rxcpp::subscriber &subscriber, uint64_t block_id) { - return [this, &subscriber, block_id](pqxx::result &result) { + return [this, &subscriber, block_id](std::vector &result) { auto block = block_store_.get(block_id) | [](const auto &bytes) { return shared_model::converters::protobuf::jsonToModel< shared_model::proto::Block>(bytesToString(bytes)); @@ -131,7 +125,10 @@ namespace iroha { boost::for_each( result | boost::adaptors::transformed([](const auto &x) { - return x.at("index").template as(); + std::istringstream iss(x); + size_t size; + iss >> size; + return size; }), [&](const auto &x) { subscriber.on_next(PostgresBlockQuery::wTransaction( @@ -152,12 +149,22 @@ namespace iroha { } for (const auto &block_id : block_ids) { - execute_( - "SELECT DISTINCT index FROM index_by_creator_height WHERE " - "creator_id = " - + transaction_.quote(account_id) - + " AND height = " + transaction_.quote(block_id) + ";") - | this->callback(subscriber, block_id); + std::vector index; + soci::indicator ind; + std::string row; + soci::statement st = + (sql_.prepare + << "SELECT DISTINCT index FROM index_by_creator_height " + "WHERE creator_id = :id AND height = :height", + soci::into(row, ind), + soci::use(account_id), + soci::use(block_id)); + st.execute(); + + processSoci(st, ind, row, [&index](std::string &r) { + index.push_back(r); + }); + this->callback(subscriber, block_id)(index); } subscriber.on_completed(); }); @@ -167,26 +174,36 @@ namespace iroha { PostgresBlockQuery::getAccountAssetTransactions( const shared_model::interface::types::AccountIdType &account_id, const shared_model::interface::types::AssetIdType &asset_id) { - return rxcpp::observable<>::create([this, - account_id, - asset_id]( - auto subscriber) { - auto block_ids = this->getBlockIds(account_id); - if (block_ids.empty()) { - subscriber.on_completed(); - return; - } + return rxcpp::observable<>::create( + [this, account_id, asset_id](auto subscriber) { + auto block_ids = this->getBlockIds(account_id); + if (block_ids.empty()) { + subscriber.on_completed(); + return; + } - for (const auto &block_id : block_ids) { - execute_( - "SELECT DISTINCT index FROM index_by_id_height_asset WHERE id = " - + transaction_.quote(account_id) - + " AND height = " + transaction_.quote(block_id) - + " AND asset_id = " + transaction_.quote(asset_id) + ";") - | this->callback(subscriber, block_id); - } - subscriber.on_completed(); - }); + for (const auto &block_id : block_ids) { + std::vector index; + soci::indicator ind; + std::string row; + soci::statement st = + (sql_.prepare + << "SELECT DISTINCT index FROM index_by_id_height_asset " + "WHERE id = :id AND height = :height AND asset_id = " + ":asset_id", + soci::into(row, ind), + soci::use(account_id), + soci::use(block_id), + soci::use(asset_id)); + st.execute(); + + processSoci(st, ind, row, [&index](std::string &r) { + index.push_back(r); + }); + this->callback(subscriber, block_id)(index); + } + subscriber.on_completed(); + }); } rxcpp::observable> diff --git a/irohad/ametsuchi/impl/postgres_block_query.hpp b/irohad/ametsuchi/impl/postgres_block_query.hpp index 5573f41a55..cf43616657 100644 --- a/irohad/ametsuchi/impl/postgres_block_query.hpp +++ b/irohad/ametsuchi/impl/postgres_block_query.hpp @@ -19,8 +19,6 @@ #define IROHA_POSTGRES_FLAT_BLOCK_QUERY_HPP #include -#include -#include #include "ametsuchi/block_query.hpp" #include "ametsuchi/impl/flat_file/flat_file.hpp" @@ -37,11 +35,10 @@ namespace iroha { */ class PostgresBlockQuery : public BlockQuery { public: - PostgresBlockQuery(pqxx::nontransaction &transaction_, - KeyValueStorage &file_store); - PostgresBlockQuery(std::unique_ptr connection, - std::unique_ptr transaction, - KeyValueStorage &file_store); + explicit PostgresBlockQuery(soci::session &sql, + KeyValueStorage &file_store); + explicit PostgresBlockQuery(std::unique_ptr sql_ptr, + KeyValueStorage &file_store); rxcpp::observable getAccountTransactions( const shared_model::interface::types::AccountIdType &account_id) @@ -96,17 +93,14 @@ namespace iroha { * @param block_id * @return */ - std::function callback( + std::function &result)> callback( const rxcpp::subscriber &s, uint64_t block_id); - std::unique_ptr connection_ptr_; - std::unique_ptr transaction_ptr_; + std::unique_ptr sql_ptr_; + soci::session &sql_; KeyValueStorage &block_store_; - pqxx::nontransaction &transaction_; logger::Logger log_; - using ExecuteType = decltype(makeExecuteOptional(transaction_, log_)); - ExecuteType execute_; }; } // namespace ametsuchi } // namespace iroha diff --git a/irohad/ametsuchi/impl/postgres_ordering_service_persistent_state.cpp b/irohad/ametsuchi/impl/postgres_ordering_service_persistent_state.cpp index e8a9caf12f..b94fbce747 100644 --- a/irohad/ametsuchi/impl/postgres_ordering_service_persistent_state.cpp +++ b/irohad/ametsuchi/impl/postgres_ordering_service_persistent_state.cpp @@ -16,108 +16,89 @@ */ #include "ametsuchi/impl/postgres_ordering_service_persistent_state.hpp" + #include #include +#include + #include "common/types.hpp" namespace iroha { namespace ametsuchi { + bool PostgresOrderingServicePersistentState::execute_(std::string query) { + try { + *sql_ << query; + } catch (std::exception &e) { + log_->error("Failed to execute query: " + query + + ". Reason: " + e.what()); + return false; + } + return true; + } + expected::Result, std::string> PostgresOrderingServicePersistentState::create( const std::string &postgres_options) { - // create connection - auto postgres_connection = - std::make_unique(postgres_options); + std::unique_ptr sql; try { - postgres_connection->activate(); - } catch (const pqxx::broken_connection &e) { + sql = + std::make_unique(soci::postgresql, postgres_options); + + } catch (std::exception &e) { return expected::makeError( (boost::format("Connection to PostgreSQL broken: %s") % e.what()) .str()); } - - // create transaction - auto postgres_transaction = std::make_unique( - *postgres_connection, "Storage"); expected::Result, std::string> storage; storage = expected::makeValue( std::make_shared( - std::move(postgres_connection), std::move(postgres_transaction))); + std::move(sql))); return storage; } PostgresOrderingServicePersistentState:: PostgresOrderingServicePersistentState( - std::unique_ptr postgres_connection, - std::unique_ptr postgres_transaction) - : postgres_connection_(std::move(postgres_connection)), - postgres_transaction_(std::move(postgres_transaction)), - log_(logger::log("PostgresOrderingServicePersistentState")), - execute_{ametsuchi::makeExecuteResult(*postgres_transaction_)} {} + std::unique_ptr sql) + : sql_(std::move(sql)), + log_(logger::log("PostgresOrderingServicePersistentState")) {} bool PostgresOrderingServicePersistentState::initStorage() { return execute_( - "CREATE TABLE IF NOT EXISTS ordering_service_state (\n" - " proposal_height bigserial\n" - ");\n" - "INSERT INTO ordering_service_state\n" - "VALUES (2); -- expected height (1 is genesis)") - .match([](expected::Value v) -> bool { return true; }, - [&](expected::Error e) -> bool { - log_->error(e.error); - return false; - }); + "CREATE TABLE IF NOT EXISTS ordering_service_state " + "(proposal_height bigserial)") + && execute_("INSERT INTO ordering_service_state VALUES (2)"); } bool PostgresOrderingServicePersistentState::dropStorgage() { log_->info("Drop storage"); - return execute_("DROP TABLE IF EXISTS ordering_service_state;") - .match([](expected::Value v) -> bool { return true; }, - [&](expected::Error e) -> bool { - log_->error(e.error); - return false; - }); + return execute_("DROP TABLE IF EXISTS ordering_service_state"); } bool PostgresOrderingServicePersistentState::saveProposalHeight( size_t height) { log_->info("Save proposal_height in ordering_service_state " + std::to_string(height)); - return execute_( - "DELETE FROM ordering_service_state;\n" - "INSERT INTO ordering_service_state " - "VALUES (" - + postgres_transaction_->quote(height) + ");") - .match([](expected::Value v) -> bool { return true; }, - [&](expected::Error e) -> bool { - log_->error(e.error); - return false; - }); + return execute_("DELETE FROM ordering_service_state") + && execute_("INSERT INTO ordering_service_state VALUES (" + + std::to_string(height) + ")"); } boost::optional PostgresOrderingServicePersistentState::loadProposalHeight() const { boost::optional height; - execute_("SELECT * FROM ordering_service_state;") - .match( - [&](expected::Value result) { - if (result.value.empty()) { - log_->error( - "There is no proposal_height in ordering_service_state. " - "Use default value 2."); - height = 2; - } else { - auto row = result.value.at(0); - height = row.at("proposal_height").as(); - log_->info("Load proposal_height in ordering_service_state " - + std::to_string(height.value())); - } - }, - [&](expected::Error e) { log_->error(e.error); }); + *sql_ << "SELECT * FROM ordering_service_state LIMIT 1", + soci::into(height); + + if (not height) { + log_->error( + "There is no proposal_height in ordering_service_state. " + "Use default value 2."); + height = 2; + } return height; } diff --git a/irohad/ametsuchi/impl/postgres_ordering_service_persistent_state.hpp b/irohad/ametsuchi/impl/postgres_ordering_service_persistent_state.hpp index a6e15f97b7..af2013d92f 100644 --- a/irohad/ametsuchi/impl/postgres_ordering_service_persistent_state.hpp +++ b/irohad/ametsuchi/impl/postgres_ordering_service_persistent_state.hpp @@ -18,7 +18,6 @@ #ifndef IROHA_POSTGRES_ORDERING_SERVICE_PERSISTENT_STATE_HPP #define IROHA_POSTGRES_ORDERING_SERVICE_PERSISTENT_STATE_HPP -#include #include "ametsuchi/impl/postgres_wsv_common.hpp" #include "ametsuchi/ordering_service_persistent_state.hpp" #include "common/result.hpp" @@ -50,8 +49,7 @@ namespace iroha { * @param postgres_transaction postgres transaction object */ explicit PostgresOrderingServicePersistentState( - std::unique_ptr postgres_connection, - std::unique_ptr postgres_transaction); + std::unique_ptr sql); /** * Initialize storage. @@ -81,20 +79,11 @@ namespace iroha { virtual bool resetState(); private: - /** - * Pg connection with direct transaction management - */ - std::unique_ptr postgres_connection_; - - /** - * Pg transaction - */ - std::unique_ptr postgres_transaction_; + std::unique_ptr sql_; logger::Logger log_; - using ExecuteType = decltype(makeExecuteResult(*postgres_transaction_)); - ExecuteType execute_; + bool execute_(std::string query); }; } // namespace ametsuchi } // namespace iroha diff --git a/irohad/ametsuchi/impl/postgres_wsv_command.cpp b/irohad/ametsuchi/impl/postgres_wsv_command.cpp index 9e89b6ff3a..525dbaca3f 100644 --- a/irohad/ametsuchi/impl/postgres_wsv_command.cpp +++ b/irohad/ametsuchi/impl/postgres_wsv_command.cpp @@ -23,79 +23,91 @@ namespace iroha { namespace ametsuchi { - PostgresWsvCommand::PostgresWsvCommand(pqxx::nontransaction &transaction) - : transaction_(transaction), - execute_{makeExecuteResult(transaction_)} {} + template + WsvCommandResult execute(soci::statement &st, Function &&error) { + st.define_and_bind(); + try { + st.execute(true); + return {}; + } catch (const std::exception &e) { + return expected::makeError(error()); + } + } + + PostgresWsvCommand::PostgresWsvCommand(soci::session &sql) : sql_(sql) {} WsvCommandResult PostgresWsvCommand::insertRole( const shared_model::interface::types::RoleIdType &role_name) { - auto result = execute_("INSERT INTO role(role_id) VALUES (" - + transaction_.quote(role_name) + ");"); - - auto message_gen = [&] { + soci::statement st = sql_.prepare + << "INSERT INTO role(role_id) VALUES (:role_id)"; + st.exchange(soci::use(role_name)); + auto msg = [&] { return (boost::format("failed to insert role: '%s'") % role_name).str(); }; - - return makeCommandResult(std::move(result), message_gen); + return execute(st, msg); } WsvCommandResult PostgresWsvCommand::insertAccountRole( const shared_model::interface::types::AccountIdType &account_id, const shared_model::interface::types::RoleIdType &role_name) { - auto result = - execute_("INSERT INTO account_has_roles(account_id, role_id) VALUES (" - + transaction_.quote(account_id) + ", " - + transaction_.quote(role_name) + ");"); + soci::statement st = sql_.prepare + << "INSERT INTO account_has_roles(account_id, role_id) VALUES " + "(:account_id, :role_id)"; + st.exchange(soci::use(account_id)); + st.exchange(soci::use(role_name)); - auto message_gen = [&] { + auto msg = [&] { return (boost::format("failed to insert account role, account: '%s', " "role name: '%s'") % account_id % role_name) .str(); }; - - return makeCommandResult(std::move(result), message_gen); + return execute(st, msg); } WsvCommandResult PostgresWsvCommand::deleteAccountRole( const shared_model::interface::types::AccountIdType &account_id, const shared_model::interface::types::RoleIdType &role_name) { - auto result = execute_("DELETE FROM account_has_roles WHERE account_id=" - + transaction_.quote(account_id) + "AND role_id=" - + transaction_.quote(role_name) + ";"); - auto message_gen = [&] { + soci::statement st = sql_.prepare + << "DELETE FROM account_has_roles WHERE account_id=:account_id " + "AND role_id=:role_id"; + st.exchange(soci::use(account_id)); + st.exchange(soci::use(role_name)); + + auto msg = [&] { return (boost::format( "failed to delete account role, account id: '%s', " "role name: '%s'") % account_id % role_name) .str(); }; - - return makeCommandResult(std::move(result), message_gen); + return execute(st, msg); } WsvCommandResult PostgresWsvCommand::insertRolePermissions( const shared_model::interface::types::RoleIdType &role_id, const shared_model::interface::RolePermissionSet &permissions) { auto perm_str = permissions.toBitstring(); - auto result = execute_( - "INSERT INTO role_has_permissions(role_id, permission) VALUES (" - + transaction_.quote(role_id) + ", " + transaction_.quote(perm_str) - + " );"); + soci::statement st = sql_.prepare + << "INSERT INTO role_has_permissions(role_id, permission) VALUES " + "(:id, :perm)"; + st.exchange(soci::use(role_id)); + st.exchange(soci::use(perm_str)); - auto message_gen = [&] { - // TODO(@l4l) 26/06/18 need to be simplified at IR-1479 + auto msg = [&] { const auto &str = shared_model::proto::permissions::toString(permissions); - const auto perm_debug_str = - std::accumulate(str.begin(), str.end(), std::string()); + std::string perm_debug_str = std::accumulate( + str.begin(), + str.end(), + std::string(), + [](auto acc, const auto &elem) { return acc + " " + elem; }); return (boost::format("failed to insert role permissions, role " "id: '%s', permissions: [%s]") % role_id % perm_debug_str) .str(); }; - - return makeCommandResult(std::move(result), message_gen); + return execute(st, msg); } WsvCommandResult PostgresWsvCommand::insertAccountGrantablePermission( @@ -106,20 +118,19 @@ namespace iroha { const auto perm_str = shared_model::interface::GrantablePermissionSet({permission}) .toBitstring(); - auto query = - (boost::format( - "INSERT INTO account_has_grantable_permissions as " - "has_perm(permittee_account_id, account_id, permission) VALUES " - "(%1%, %2%, %3%) ON CONFLICT (permittee_account_id, account_id) " - // SELECT will end up with a error, if the permission exists - "DO UPDATE SET permission=(SELECT has_perm.permission | %3% " - "WHERE (has_perm.permission & %3%) <> %3%);") - % transaction_.quote(permittee_account_id) - % transaction_.quote(account_id) % transaction_.quote(perm_str)) - .str(); - auto result = execute_(query); - - auto message_gen = [&] { + soci::statement st = sql_.prepare + << "INSERT INTO account_has_grantable_permissions as " + "has_perm(permittee_account_id, account_id, permission) VALUES " + "(:permittee_account_id, :account_id, :perm) ON CONFLICT " + "(permittee_account_id, account_id) DO UPDATE SET " + // SELECT will end up with a error, if the permission exists + "permission=(SELECT has_perm.permission | :perm WHERE " + "(has_perm.permission & :perm) <> :perm);"; + st.exchange(soci::use(permittee_account_id, "permittee_account_id")); + st.exchange(soci::use(account_id, "account_id")); + st.exchange(soci::use(perm_str, "perm")); + + auto msg = [&] { return (boost::format("failed to insert account grantable permission, " "permittee account id: '%s', " "account id: '%s', " @@ -130,8 +141,7 @@ namespace iroha { % shared_model::proto::permissions::toString(permission)) .str(); }; - - return makeCommandResult(std::move(result), message_gen); + return execute(st, msg); } WsvCommandResult PostgresWsvCommand::deleteAccountGrantablePermission( @@ -143,44 +153,43 @@ namespace iroha { .set() .unset(permission) .toBitstring(); - auto query = - (boost::format("UPDATE account_has_grantable_permissions as has_perm " - // SELECT will end up with a error, if the permission - // doesn't exists - "SET permission=(SELECT has_perm.permission & %3% " - "WHERE has_perm.permission & %3% = %3%) WHERE " - "permittee_account_id=%1% AND account_id=%2%;") - % transaction_.quote(permittee_account_id) - % transaction_.quote(account_id) % transaction_.quote(perm_str)) - .str(); - auto result = execute_(query); - - auto message_gen = [&] { + soci::statement st = sql_.prepare + << "UPDATE account_has_grantable_permissions as has_perm SET " + // SELECT will end up with a error, if the permission doesn't + // exists + "permission=(SELECT has_perm.permission & :perm WHERE " + "has_perm.permission & :perm = :perm) WHERE " + "permittee_account_id=:permittee_account_id AND " + "account_id=:account_id;"; + + st.exchange(soci::use(permittee_account_id, "permittee_account_id")); + st.exchange(soci::use(account_id, "account_id")); + st.exchange(soci::use(perm_str, "perm")); + + auto msg = [&] { return (boost::format("failed to delete account grantable permission, " "permittee account id: '%s', " "account id: '%s', " "permission id: '%s'") - % permittee_account_id - % account_id - // TODO(@l4l) 26/06/18 need to be simplified at IR-1479 + % permittee_account_id % account_id % shared_model::proto::permissions::toString(permission)) .str(); }; - - return makeCommandResult(std::move(result), message_gen); + return execute(st, msg); } WsvCommandResult PostgresWsvCommand::insertAccount( const shared_model::interface::Account &account) { - auto result = execute_( - "INSERT INTO account(account_id, domain_id, quorum, " - "data) VALUES (" - + transaction_.quote(account.accountId()) + ", " - + transaction_.quote(account.domainId()) + ", " - + transaction_.quote(account.quorum()) + ", " - + transaction_.quote(account.jsonData()) + ");"); - - auto message_gen = [&] { + soci::statement st = sql_.prepare + << "INSERT INTO account(account_id, domain_id, quorum," + "data) VALUES (:id, :domain_id, :quorum, :data)"; + uint32_t quorum = account.quorum(); + st.exchange(soci::use(account.accountId())); + st.exchange(soci::use(account.domainId())); + st.exchange(soci::use(quorum)); + st.exchange(soci::use(account.jsonData())); + + auto msg = [&] { return (boost::format("failed to insert account, " "account id: '%s', " "domain id: '%s', " @@ -190,194 +199,184 @@ namespace iroha { % account.jsonData()) .str(); }; - - return makeCommandResult(std::move(result), message_gen); + return execute(st, msg); } WsvCommandResult PostgresWsvCommand::insertAsset( const shared_model::interface::Asset &asset) { - uint32_t precision = asset.precision(); - auto result = execute_( - "INSERT INTO asset(asset_id, domain_id, \"precision\", data) " - "VALUES (" - + transaction_.quote(asset.assetId()) + ", " - + transaction_.quote(asset.domainId()) + ", " - + transaction_.quote(precision) + ", " + /*asset.data*/ "NULL" - + ");"); - - auto message_gen = [&] { + soci::statement st = sql_.prepare + << "INSERT INTO asset(asset_id, domain_id, \"precision\", data) " + "VALUES (:id, :domain_id, :precision, NULL)"; + st.exchange(soci::use(asset.assetId())); + st.exchange(soci::use(asset.domainId())); + st.exchange(soci::use(asset.precision())); + + auto msg = [&] { return (boost::format("failed to insert asset, asset id: '%s', " "domain id: '%s', precision: %d") - % asset.assetId() % asset.domainId() % precision) + % asset.assetId() % asset.domainId() % asset.precision()) .str(); }; - - return makeCommandResult(std::move(result), message_gen); + return execute(st, msg); } WsvCommandResult PostgresWsvCommand::upsertAccountAsset( const shared_model::interface::AccountAsset &asset) { - auto result = execute_( - "INSERT INTO account_has_asset(account_id, asset_id, amount) " - "VALUES (" - + transaction_.quote(asset.accountId()) + ", " - + transaction_.quote(asset.assetId()) + ", " - + transaction_.quote(asset.balance().toStringRepr()) - + ") ON CONFLICT (account_id, asset_id) DO UPDATE SET " - "amount = EXCLUDED.amount;"); - - auto message_gen = [&] { + auto balance = asset.balance().toStringRepr(); + soci::statement st = sql_.prepare + << "INSERT INTO account_has_asset(account_id, asset_id, amount) " + "VALUES (:account_id, :asset_id, :amount) ON CONFLICT " + "(account_id, asset_id) DO UPDATE SET " + "amount = EXCLUDED.amount"; + + st.exchange(soci::use(asset.accountId())); + st.exchange(soci::use(asset.assetId())); + st.exchange(soci::use(balance)); + + auto msg = [&] { return (boost::format("failed to upsert account, account id: '%s', " "asset id: '%s', balance: %s") % asset.accountId() % asset.assetId() % asset.balance().toString()) .str(); }; - - return makeCommandResult(std::move(result), message_gen); + return execute(st, msg); } WsvCommandResult PostgresWsvCommand::insertSignatory( const shared_model::interface::types::PubkeyType &signatory) { - auto result = - execute_("INSERT INTO signatory(public_key) VALUES (" - + transaction_.quote(pqxx::binarystring( - signatory.blob().data(), signatory.blob().size())) - + ") ON CONFLICT DO NOTHING;"); - auto message_gen = [&] { + soci::statement st = sql_.prepare + << "INSERT INTO signatory(public_key) VALUES (:pk) ON CONFLICT DO " + "NOTHING;"; + st.exchange(soci::use(signatory.hex())); + + auto msg = [&] { return (boost::format( "failed to insert signatory, signatory hex string: '%s'") % signatory.hex()) .str(); }; - return makeCommandResult(std::move(result), message_gen); + return execute(st, msg); } WsvCommandResult PostgresWsvCommand::insertAccountSignatory( const shared_model::interface::types::AccountIdType &account_id, const shared_model::interface::types::PubkeyType &signatory) { - auto result = execute_( - "INSERT INTO account_has_signatory(account_id, public_key) VALUES (" - + transaction_.quote(account_id) + ", " - + transaction_.quote(pqxx::binarystring(signatory.blob().data(), - signatory.blob().size())) - + ");"); - - auto message_gen = [&] { + soci::statement st = sql_.prepare + << "INSERT INTO account_has_signatory(account_id, public_key) " + "VALUES (:account_id, :pk)"; + st.exchange(soci::use(account_id)); + st.exchange(soci::use(signatory.hex())); + + auto msg = [&] { return (boost::format("failed to insert account signatory, account id: " "'%s', signatory hex string: '%s") % account_id % signatory.hex()) .str(); }; - return makeCommandResult(std::move(result), message_gen); + return execute(st, msg); } WsvCommandResult PostgresWsvCommand::deleteAccountSignatory( const shared_model::interface::types::AccountIdType &account_id, const shared_model::interface::types::PubkeyType &signatory) { - auto result = - execute_("DELETE FROM account_has_signatory WHERE account_id = " - + transaction_.quote(account_id) + " AND public_key = " - + transaction_.quote(pqxx::binarystring( - signatory.blob().data(), signatory.blob().size())) - + ";"); - - auto message_gen = [&] { + soci::statement st = sql_.prepare + << "DELETE FROM account_has_signatory WHERE account_id = " + ":account_id AND public_key = :pk"; + st.exchange(soci::use(account_id)); + st.exchange(soci::use(signatory.hex())); + + auto msg = [&] { return (boost::format("failed to delete account signatory, account id: " "'%s', signatory hex string: '%s'") % account_id % signatory.hex()) .str(); }; - return makeCommandResult(std::move(result), message_gen); + return execute(st, msg); } WsvCommandResult PostgresWsvCommand::deleteSignatory( const shared_model::interface::types::PubkeyType &signatory) { - pqxx::binarystring public_key(signatory.blob().data(), - signatory.blob().size()); - auto result = execute_("DELETE FROM signatory WHERE public_key = " - + transaction_.quote(public_key) - + " AND NOT EXISTS (SELECT 1 FROM account_has_signatory " - "WHERE public_key = " - + transaction_.quote(public_key) - + ") AND NOT EXISTS (SELECT 1 FROM peer WHERE public_key = " - + transaction_.quote(public_key) + ");"); - - auto message_gen = [&] { + soci::statement st = sql_.prepare + << "DELETE FROM signatory WHERE public_key = :pk AND NOT EXISTS " + "(SELECT 1 FROM account_has_signatory " + "WHERE public_key = :pk) AND NOT EXISTS (SELECT 1 FROM peer " + "WHERE public_key = :pk)"; + st.exchange(soci::use(signatory.hex(), "pk")); + + auto msg = [&] { return (boost::format( "failed to delete signatory, signatory hex string: '%s'") % signatory.hex()) .str(); }; - return makeCommandResult(std::move(result), message_gen); + return execute(st, msg); } WsvCommandResult PostgresWsvCommand::insertPeer( const shared_model::interface::Peer &peer) { - auto result = - execute_("INSERT INTO peer(public_key, address) VALUES (" - + transaction_.quote(pqxx::binarystring( - peer.pubkey().blob().data(), peer.pubkey().size())) - + ", " + transaction_.quote(peer.address()) + ");"); + soci::statement st = sql_.prepare + << "INSERT INTO peer(public_key, address) VALUES (:pk, :address)"; + st.exchange(soci::use(peer.pubkey().hex())); + st.exchange(soci::use(peer.address())); - auto message_gen = [&] { + auto msg = [&] { return (boost::format( "failed to insert peer, public key: '%s', address: '%s'") % peer.pubkey().hex() % peer.address()) .str(); }; - return makeCommandResult(std::move(result), message_gen); + return execute(st, msg); } WsvCommandResult PostgresWsvCommand::deletePeer( const shared_model::interface::Peer &peer) { - auto result = execute_( - "DELETE FROM peer WHERE public_key = " - + transaction_.quote(pqxx::binarystring(peer.pubkey().blob().data(), - peer.pubkey().size())) - + " AND address = " + transaction_.quote(peer.address()) + ";"); - auto message_gen = [&] { + soci::statement st = sql_.prepare + << "DELETE FROM peer WHERE public_key = :pk AND address = :address"; + st.exchange(soci::use(peer.pubkey().hex())); + st.exchange(soci::use(peer.address())); + + auto msg = [&] { return (boost::format( "failed to delete peer, public key: '%s', address: '%s'") % peer.pubkey().hex() % peer.address()) .str(); }; - return makeCommandResult(std::move(result), message_gen); + return execute(st, msg); } WsvCommandResult PostgresWsvCommand::insertDomain( const shared_model::interface::Domain &domain) { - auto result = - execute_("INSERT INTO domain(domain_id, default_role) VALUES (" - + transaction_.quote(domain.domainId()) + ", " - + transaction_.quote(domain.defaultRole()) + ");"); + soci::statement st = sql_.prepare + << "INSERT INTO domain(domain_id, default_role) VALUES (:id, " + ":role)"; + st.exchange(soci::use(domain.domainId())); + st.exchange(soci::use(domain.defaultRole())); - auto message_gen = [&] { + auto msg = [&] { return (boost::format("failed to insert domain, domain id: '%s', " "default role: '%s'") % domain.domainId() % domain.defaultRole()) .str(); }; - return makeCommandResult(std::move(result), message_gen); + return execute(st, msg); } WsvCommandResult PostgresWsvCommand::updateAccount( const shared_model::interface::Account &account) { - auto result = execute_( - "UPDATE account\n" - " SET quorum=" + - transaction_.quote(account.quorum()) + - "\n" - " WHERE account_id=" + - transaction_.quote(account.accountId()) + ";"); - - auto message_gen = [&] { + soci::statement st = sql_.prepare + << "UPDATE account SET quorum=:quorum WHERE account_id=:account_id"; + uint32_t quorum = account.quorum(); + st.exchange(soci::use(quorum)); + st.exchange(soci::use(account.accountId())); + + auto msg = [&] { return (boost::format( "failed to update account, account id: '%s', quorum: '%s'") % account.accountId() % account.quorum()) .str(); }; - return makeCommandResult(std::move(result), message_gen); + return execute(st, msg); } WsvCommandResult PostgresWsvCommand::setAccountKV( @@ -385,24 +384,31 @@ namespace iroha { const shared_model::interface::types::AccountIdType &creator_account_id, const std::string &key, const std::string &val) { - auto result = execute_( - "UPDATE account SET data = jsonb_set(CASE WHEN data ?" - + transaction_.quote(creator_account_id) - + " THEN data ELSE jsonb_set(data, " - + transaction_.quote("{" + creator_account_id + "}") + "," - + transaction_.quote("{}") + ") END," - + transaction_.quote("{" + creator_account_id + ", " + key + "}") - + "," + transaction_.quote("\"" + val + "\"") - + ") WHERE account_id=" + transaction_.quote(account_id) + ";"); - - auto message_gen = [&] { + soci::statement st = sql_.prepare + << "UPDATE account SET data = jsonb_set(" + "CASE WHEN data ?:creator_account_id THEN data ELSE " + "jsonb_set(data, :json, :empty_json) END, " + " :filled_json, :val) WHERE account_id=:account_id"; + std::string json = "{" + creator_account_id + "}"; + std::string empty_json = "{}"; + std::string filled_json = "{" + creator_account_id + ", " + key + "}"; + std::string value = "\"" + val + "\""; + st.exchange(soci::use(creator_account_id)); + st.exchange(soci::use(json)); + st.exchange(soci::use(empty_json)); + st.exchange(soci::use(filled_json)); + st.exchange(soci::use(value)); + st.exchange(soci::use(account_id)); + + auto msg = [&] { return (boost::format( "failed to set account key-value, account id: '%s', " "creator account id: '%s',\n key: '%s', value: '%s'") % account_id % creator_account_id % key % val) .str(); }; - return makeCommandResult(std::move(result), message_gen); + + return execute(st, msg); } } // namespace ametsuchi } // namespace iroha diff --git a/irohad/ametsuchi/impl/postgres_wsv_command.hpp b/irohad/ametsuchi/impl/postgres_wsv_command.hpp index 3c0c35d552..9785cb98f1 100644 --- a/irohad/ametsuchi/impl/postgres_wsv_command.hpp +++ b/irohad/ametsuchi/impl/postgres_wsv_command.hpp @@ -27,7 +27,7 @@ namespace iroha { class PostgresWsvCommand : public WsvCommand { public: - explicit PostgresWsvCommand(pqxx::nontransaction &transaction); + explicit PostgresWsvCommand(soci::session &sql); WsvCommandResult insertRole( const shared_model::interface::types::RoleIdType &role_name) override; @@ -86,36 +86,7 @@ namespace iroha { shared_model::interface::permissions::Grantable permission) override; private: - pqxx::nontransaction &transaction_; - - using ExecuteType = decltype(makeExecuteResult(transaction_)); - ExecuteType execute_; - - /** - * Transforms result which contains pqxx to WsvCommandResult, - * which will have error message generated by error_generator - * appended to error received from given result - * @param result which can be received by calling execute_ - * @param error_generator function which must generate error message - * to be used as a return error. - * Function is passed instead of string to avoid overhead of string - * construction in successful case. - * @return WsvCommandResult with combined error message - * in case of result contains error - */ - template - WsvCommandResult makeCommandResult( - expected::Result &&result, - Function &&error_generator) const noexcept { - return result.match( - [](expected::Value v) -> WsvCommandResult { - return {}; - }, - [&error_generator]( - expected::Error e) -> WsvCommandResult { - return expected::makeError(error_generator() + "\n" + e.error); - }); - } + soci::session &sql_; }; } // namespace ametsuchi } // namespace iroha diff --git a/irohad/ametsuchi/impl/postgres_wsv_common.hpp b/irohad/ametsuchi/impl/postgres_wsv_common.hpp index ddc2b2c3f5..7db8e3d59b 100644 --- a/irohad/ametsuchi/impl/postgres_wsv_common.hpp +++ b/irohad/ametsuchi/impl/postgres_wsv_common.hpp @@ -19,8 +19,11 @@ #define IROHA_POSTGRES_WSV_COMMON_HPP #include -#include -#include + +#define SOCI_USE_BOOST +#define HAVE_BOOST +#include +#include #include "builders/default_builders.hpp" #include "common/result.hpp" @@ -30,59 +33,19 @@ namespace iroha { namespace ametsuchi { /** - * Return function which can execute SQL statements on provided transaction - * @param transaction on which to apply statement. - * @param logger is used to report an error. - * @return Result with pqxx::result in value case, or exception message - * if exception was caught - */ - inline auto makeExecuteResult(pqxx::nontransaction &transaction) noexcept { - return [&](const std::string &statement) noexcept - ->expected::Result { - try { - return expected::makeValue(transaction.exec(statement)); - } catch (const std::exception &e) { - return expected::makeError(e.what()); - } - }; - } - - /** - * Return function which can execute SQL statements on provided transaction - * This function is deprecated, and will be removed as soon as wsv_query - * will be refactored to return result - * @param transaction on which to apply statement. - * @param logger is used to report an error. - * @return boost::optional with pqxx::result in successful case, or nullopt - * if exception was caught - */ - inline auto makeExecuteOptional(pqxx::nontransaction &transaction, - logger::Logger &logger) noexcept { - return [&](const std::string &statement) noexcept - ->boost::optional { - try { - return transaction.exec(statement); - } catch (const std::exception &e) { - logger->error(e.what()); - return boost::none; - } - }; - } - - /** - * Transforms pqxx::result to vector of Ts by applying transform_func + * Transforms soci::rowset to vector of Ts by applying + * transform_func * @tparam T - type to transform to * @tparam Operator - type of transformation function, must return T - * @param result - pqxx::result which contains several rows from the - * database + * @param result - soci::rowset which contains several rows from + * the database * @param transform_func - function which transforms result row to T * @return vector of target type */ template - std::vector transform(const pqxx::result &result, + std::vector transform(const soci::rowset &result, Operator &&transform_func) noexcept { std::vector values; - values.reserve(result.size()); std::transform(result.begin(), result.end(), std::back_inserter(values), @@ -106,43 +69,64 @@ namespace iroha { } } + template + void processSoci(soci::statement &st, + soci::indicator &ind, + ParamType &row, + Function f) { + while (st.fetch()) { + switch (ind) { + case soci::i_ok: + f(row); + case soci::i_null: + case soci::i_truncated: + break; + } + } + } + static inline shared_model::builder::BuilderResult< shared_model::interface::Account> - makeAccount(const pqxx::row &row) noexcept { - return tryBuild([&row] { + makeAccount(const std::string &account_id, + const std::string &domain_id, + const shared_model::interface::types::QuorumType &quorum, + const std::string &data) noexcept { + return tryBuild([&] { return shared_model::builder::DefaultAccountBuilder() - .accountId(row.at("account_id").template as()) - .domainId(row.at("domain_id").template as()) - .quorum( - row.at("quorum") - .template as()) - .jsonData(row.at("data").template as()) + .accountId(account_id) + .domainId(domain_id) + .quorum(quorum) + .jsonData(data) .build(); }); } static inline shared_model::builder::BuilderResult< shared_model::interface::Asset> - makeAsset(const pqxx::row &row) noexcept { - return tryBuild([&row] { + makeAsset(const std::string &asset_id, + const std::string &domain_id, + const int32_t precision) noexcept { + return tryBuild([&] { return shared_model::builder::DefaultAssetBuilder() - .assetId(row.at("asset_id").template as()) - .domainId(row.at("domain_id").template as()) - .precision(row.at("precision").template as()) + .assetId(asset_id) + .domainId(domain_id) + .precision(precision) .build(); }); } static inline shared_model::builder::BuilderResult< shared_model::interface::AccountAsset> - makeAccountAsset(const pqxx::row &row) noexcept { - return tryBuild([&row] { - auto balance = shared_model::builder::DefaultAmountBuilder::fromString( - row.at("amount").template as()); + makeAccountAsset(const std::string &account_id, + const std::string &asset_id, + const std::string &amount) noexcept { + return tryBuild([&] { + auto balance = + shared_model::builder::DefaultAmountBuilder::fromString(amount); return balance | [&](const auto &balance_ptr) { return shared_model::builder::DefaultAccountAssetBuilder() - .accountId(row.at("account_id").template as()) - .assetId(row.at("asset_id").template as()) + .accountId(account_id) + .assetId(asset_id) .balance(*balance_ptr) .build(); }; @@ -151,24 +135,24 @@ namespace iroha { static inline shared_model::builder::BuilderResult< shared_model::interface::Peer> - makePeer(const pqxx::row &row) noexcept { + makePeer(const soci::row &row) noexcept { return tryBuild([&row] { - pqxx::binarystring public_key_str(row.at("public_key")); - shared_model::interface::types::PubkeyType pubkey(public_key_str.str()); return shared_model::builder::DefaultPeerBuilder() - .pubkey(pubkey) - .address(row.at("address").template as()) + .pubkey(shared_model::crypto::PublicKey( + shared_model::crypto::Blob::fromHexString( + row.get(0)))) + .address(row.get(1)) .build(); }); } static inline shared_model::builder::BuilderResult< shared_model::interface::Domain> - makeDomain(const pqxx::row &row) noexcept { - return tryBuild([&row] { + makeDomain(const std::string &domain_id, const std::string &role) noexcept { + return tryBuild([&domain_id, &role] { return shared_model::builder::DefaultDomainBuilder() - .domainId(row.at("domain_id").template as()) - .defaultRole(row.at("default_role").template as()) + .domainId(domain_id) + .defaultRole(role) .build(); }); } @@ -189,9 +173,7 @@ namespace iroha { return boost::make_optional(v.value); }, [](const expected::Error> &e) - -> boost::optional> { - return boost::none; - }); + -> boost::optional> { return boost::none; }); } } // namespace ametsuchi } // namespace iroha diff --git a/irohad/ametsuchi/impl/postgres_wsv_query.cpp b/irohad/ametsuchi/impl/postgres_wsv_query.cpp index 0f559664a3..b88ed5f42c 100644 --- a/irohad/ametsuchi/impl/postgres_wsv_query.cpp +++ b/irohad/ametsuchi/impl/postgres_wsv_query.cpp @@ -36,19 +36,13 @@ namespace iroha { const std::string kAccountId = "account_id"; const std::string kDomainId = "domain_id"; - PostgresWsvQuery::PostgresWsvQuery(pqxx::nontransaction &transaction) - : transaction_(transaction), - log_(logger::log("PostgresWsvQuery")), - execute_{makeExecuteOptional(transaction_, log_)} {} - - PostgresWsvQuery::PostgresWsvQuery( - std::unique_ptr connection, - std::unique_ptr transaction) - : connection_ptr_(std::move(connection)), - transaction_ptr_(std::move(transaction)), - transaction_(*transaction_ptr_), - log_(logger::log("PostgresWsvQuery")), - execute_{makeExecuteOptional(transaction_, log_)} {} + PostgresWsvQuery::PostgresWsvQuery(soci::session &sql) + : sql_(sql), log_(logger::log("PostgresWsvQuery")) {} + + PostgresWsvQuery::PostgresWsvQuery(std::unique_ptr sql_ptr) + : sql_ptr_(std::move(sql_ptr)), + sql_(*sql_ptr_), + log_(logger::log("PostgresWsvQuery")) {} bool PostgresWsvQuery::hasAccountGrantablePermission( const AccountIdType &permitee_account_id, @@ -57,244 +51,251 @@ namespace iroha { const auto perm_str = shared_model::interface::GrantablePermissionSet({permission}) .toBitstring(); - return execute_( - "SELECT * FROM account_has_grantable_permissions WHERE " - "permittee_account_id = " - + transaction_.quote(permitee_account_id) - + " AND account_id = " + transaction_.quote(account_id) - + " AND permission & " + transaction_.quote(perm_str) + " = " - + transaction_.quote(perm_str) + ";") - | [](const auto &result) { return result.size() == 1; }; + int size; + soci::statement st = sql_.prepare + << "SELECT count(*) FROM account_has_grantable_permissions WHERE " + "permittee_account_id = :permittee_account_id AND account_id = " + ":account_id " + " AND permission & :permission = :permission "; + + st.exchange(soci::into(size)); + st.exchange(soci::use(permitee_account_id, "permittee_account_id")); + st.exchange(soci::use(account_id, "account_id")); + st.exchange(soci::use(perm_str, "permission")); + st.define_and_bind(); + st.execute(true); + + return size == 1; } boost::optional> PostgresWsvQuery::getAccountRoles( const AccountIdType &account_id) { - return execute_( - "SELECT role_id FROM account_has_roles WHERE account_id = " - + transaction_.quote(account_id) + ";") - | [&](const auto &result) { - return transform(result, [](const auto &row) { - return row.at(kRoleId).c_str(); - }); - }; + std::vector roles; + soci::indicator ind; + std::string row; + soci::statement st = + (sql_.prepare << "SELECT role_id FROM account_has_roles WHERE " + "account_id = :account_id", + soci::into(row, ind), + soci::use(account_id)); + st.execute(); + + processSoci( + st, ind, row, [&roles](std::string &row) { roles.push_back(row); }); + return roles; } boost::optional PostgresWsvQuery::getRolePermissions(const RoleIdType &role_name) { - return execute_( - "SELECT permission FROM role_has_permissions WHERE role_id = " - + transaction_.quote(role_name) + ";") - | [&](const auto &result) - -> boost::optional< - shared_model::interface::RolePermissionSet> { - // TODO(@l4l) 26/06/18 remove with IR-1480 - if (result.empty()) { - return shared_model::interface::RolePermissionSet(); - } - return shared_model::interface::RolePermissionSet( - std::string(result.at(0).at("permission").c_str())); - }; + shared_model::interface::RolePermissionSet set; + soci::indicator ind; + std::string row; + soci::statement st = + (sql_.prepare << "SELECT permission FROM role_has_permissions WHERE " + "role_id = :role_name", + soci::into(row, ind), + soci::use(role_name)); + st.execute(); + + processSoci(st, ind, row, [&set](std::string &row) { + set = shared_model::interface::RolePermissionSet(row); + }); + return set; } boost::optional> PostgresWsvQuery::getRoles() { - return execute_("SELECT role_id FROM role;") | [&](const auto &result) { - return transform( - result, [](const auto &row) { return row.at(kRoleId).c_str(); }); - }; + soci::rowset roles = + (sql_.prepare << "SELECT role_id FROM role"); + std::vector result; + for (const auto &role : roles) { + result.push_back(role); + } + return boost::make_optional(result); } boost::optional> PostgresWsvQuery::getAccount(const AccountIdType &account_id) { - return execute_("SELECT * FROM account WHERE account_id = " - + transaction_.quote(account_id) + ";") - | [&](const auto &result) - -> boost::optional< - std::shared_ptr> { - if (result.empty()) { - log_->info(kAccountNotFound, account_id); - return boost::none; - } + boost::optional domain_id, data; + boost::optional quorum; + soci::statement st = sql_.prepare + << "SELECT domain_id, quorum, data FROM account WHERE account_id = " + ":account_id"; - return fromResult(makeAccount(result.at(0))); - }; + st.exchange(soci::into(domain_id)); + st.exchange(soci::into(quorum)); + st.exchange(soci::into(data)); + st.exchange(soci::use(account_id, "account_id")); + + st.define_and_bind(); + st.execute(true); + + if (not domain_id) { + return boost::none; + } + + return fromResult( + makeAccount(account_id, domain_id.get(), quorum.get(), data.get())); } boost::optional PostgresWsvQuery::getAccountDetail( const std::string &account_id, const AccountDetailKeyType &key, const AccountIdType &writer) { - return [this, &account_id, &key, &writer]() { - if (key.empty() and writer.empty()) { - // retrieve all values for a specified account - return execute_("SELECT data#>>" + transaction_.quote("{}") - + " FROM account WHERE account_id = " - + transaction_.quote(account_id) + ";"); - } else if (not key.empty() and not writer.empty()) { - // retrieve values for the account, under the key and added by the - // writer - return execute_( - "SELECT json_build_object(" - + transaction_.quote(writer) + ", json_build_object(" - + transaction_.quote(key) + ", (" - "SELECT data #>> '{\"" + writer + "\"" - + ", \"" + key + "\"}' " - "FROM account " - "WHERE account_id = " + transaction_.quote(account_id) - + ")));"); - } else if (not writer.empty()) { - // retrieve values added by the writer under all keys - return execute_( - "SELECT json_build_object(" - + transaction_.quote(writer) + ", (" - "SELECT data -> " + transaction_.quote(writer) + " " - "FROM account " - "WHERE account_id = " + transaction_.quote(account_id) - + "));"); - } else { - // retrieve values from all writers under the key - return execute_( - "SELECT json_object_agg(key, value) AS json " - "FROM (" - "SELECT json_build_object(" - "kv.key, " - "json_build_object(" - + transaction_.quote(key) + ", " - "kv.value -> " + transaction_.quote(key) + ")) " - "FROM jsonb_each((" - "SELECT data " - "FROM account " - "WHERE account_id = " - + transaction_.quote(account_id) + ")) kv " - "WHERE kv.value ? " + transaction_.quote(key) + ") " - "AS jsons, json_each(json_build_object);"); - } - }() | [&](const auto &result) -> boost::optional { - if (result.empty()) { - // result will be empty iff account id is not found - log_->info(kAccountNotFound, account_id); - return boost::none; - } - std::string res; - auto row = result.at(0); - row.at(0) >> res; + boost::optional detail; + + if (key.empty() and writer.empty()) { + // retrieve all values for a specified account + std::string empty_json = "{}"; + sql_ << "SELECT data#>>:empty_json FROM account WHERE account_id = " + ":account_id;", + soci::into(detail), soci::use(empty_json), soci::use(account_id); + } else if (not key.empty() and not writer.empty()) { + // retrieve values for the account, under the key and added by the + // writer + std::string filled_json = "{\"" + writer + "\"" + ", \"" + key + "\"}"; + sql_ << "SELECT json_build_object(:writer::text, " + "json_build_object(:key::text, (SELECT data #>> :filled_json " + "FROM account WHERE account_id = :account_id)));", + soci::into(detail), soci::use(writer), soci::use(key), + soci::use(filled_json), soci::use(account_id); + } else if (not writer.empty()) { + // retrieve values added by the writer under all keys + sql_ << "SELECT json_build_object(:writer::text, (SELECT data -> " + ":writer FROM account WHERE account_id = :account_id));", + soci::into(detail), soci::use(writer, "writer"), + soci::use(account_id, "account_id"); + } else { + // retrieve values from all writers under the key + sql_ << "SELECT json_object_agg(key, value) AS json FROM (SELECT " + "json_build_object(kv.key, json_build_object(:key::text, " + "kv.value -> :key)) FROM jsonb_each((SELECT data FROM account " + "WHERE account_id = :account_id)) kv WHERE kv.value ? :key) AS " + "jsons, json_each(json_build_object);", + soci::into(detail), soci::use(key, "key"), + soci::use(account_id, "account_id"); + } - // if res is empty, then there is no data for this account - if (res.empty()) { - return boost::none; + return detail | [](auto &val) -> boost::optional { + // if val is empty, then there is no data for this account + if (not val.empty()) { + return val; } - return res; + return boost::none; }; } boost::optional> PostgresWsvQuery::getSignatories( const AccountIdType &account_id) { - return execute_( - "SELECT public_key FROM account_has_signatory WHERE " - "account_id = " - + transaction_.quote(account_id) + ";") - | [&](const auto &result) { - return transform(result, [&](const auto &row) { - pqxx::binarystring public_key_str(row.at(kPublicKey)); - return PubkeyType(public_key_str.str()); - }); - }; + std::vector pubkeys; + soci::indicator ind; + std::string row; + soci::statement st = + (sql_.prepare << "SELECT public_key FROM account_has_signatory WHERE " + "account_id = :account_id", + soci::into(row, ind), + soci::use(account_id)); + st.execute(); + + processSoci(st, ind, row, [&pubkeys](std::string &row) { + pubkeys.push_back(shared_model::crypto::PublicKey( + shared_model::crypto::Blob::fromHexString(row))); + }); + return boost::make_optional(pubkeys); } boost::optional> PostgresWsvQuery::getAsset(const AssetIdType &asset_id) { - pqxx::result result; - return execute_("SELECT * FROM asset WHERE asset_id = " - + transaction_.quote(asset_id) + ";") - | [&](const auto &result) - -> boost::optional< - std::shared_ptr> { - if (result.empty()) { - log_->info("Asset {} not found", asset_id); - return boost::none; - } - return fromResult(makeAsset(result.at(0))); - }; + boost::optional domain_id, data; + boost::optional precision; + soci::statement st = sql_.prepare + << "SELECT domain_id, precision FROM asset WHERE asset_id = " + ":account_id"; + st.exchange(soci::into(domain_id)); + st.exchange(soci::into(precision)); + st.exchange(soci::use(asset_id)); + + st.define_and_bind(); + st.execute(true); + + if (not domain_id) { + return boost::none; + } + + return fromResult(makeAsset(asset_id, domain_id.get(), precision.get())); } boost::optional< std::vector>> PostgresWsvQuery::getAccountAssets(const AccountIdType &account_id) { - return execute_("SELECT * FROM account_has_asset WHERE account_id = " - + transaction_.quote(account_id) + ";") - | [&](const auto &result) - -> boost::optional>> { - auto results = transform>(result, makeAccountAsset); - std::vector> - assets; - for (auto &r : results) { - r.match( - [&](expected::Value< - std::shared_ptr> &v) { - assets.push_back(v.value); - }, - [&](expected::Error> &e) { - log_->info(*e.error); - }); - } - return assets; - }; + using T = boost::tuple; + soci::rowset st = (sql_.prepare << "SELECT * FROM account_has_asset " + "WHERE account_id = :account_id", + soci::use(account_id)); + std::vector> + assets; + for (auto &t : st) { + fromResult(makeAccountAsset(account_id, t.get<1>(), t.get<2>())) | + [&assets](const auto &asset) { assets.push_back(asset); }; + } + + return boost::make_optional(assets); } + boost::optional> PostgresWsvQuery::getAccountAsset(const AccountIdType &account_id, const AssetIdType &asset_id) { - return execute_("SELECT * FROM account_has_asset WHERE account_id = " - + transaction_.quote(account_id) - + " AND asset_id = " + transaction_.quote(asset_id) + ";") - | [&](const auto &result) - -> boost::optional< - std::shared_ptr> { - if (result.empty()) { - log_->info("Account {} does not have asset {}", account_id, asset_id); - return boost::none; - } + boost::optional amount; + soci::statement st = sql_.prepare + << "SELECT amount FROM account_has_asset WHERE account_id = " + ":account_id AND asset_id = :asset_id"; + st.exchange(soci::into(amount)); + st.exchange(soci::use(account_id)); + st.exchange(soci::use(asset_id)); + st.define_and_bind(); + st.execute(true); - return fromResult(makeAccountAsset(result.at(0))); - }; + if (not amount) { + return boost::none; + } + + return fromResult(makeAccountAsset(account_id, asset_id, amount.get())); } boost::optional> PostgresWsvQuery::getDomain(const DomainIdType &domain_id) { - return execute_("SELECT * FROM domain WHERE domain_id = " - + transaction_.quote(domain_id) + ";") - | [&](const auto &result) - -> boost::optional< - std::shared_ptr> { - if (result.empty()) { - log_->info("Domain {} not found", domain_id); - return boost::none; - } - return fromResult(makeDomain(result.at(0))); - }; + boost::optional role; + soci::statement st = sql_.prepare + << "SELECT default_role FROM domain WHERE domain_id = :id LIMIT 1"; + st.exchange(soci::into(role)); + st.exchange(soci::use(domain_id)); + st.define_and_bind(); + st.execute(true); + + if (not role) { + return boost::none; + } + + return fromResult(makeDomain(domain_id, role.get())); } boost::optional>> PostgresWsvQuery::getPeers() { - pqxx::result result; - return execute_("SELECT * FROM peer;") | [&](const auto &result) - -> boost::optional>> { - auto results = transform>(result, makePeer); - std::vector> peers; - for (auto &r : results) { - r.match( - [&](expected::Value< - std::shared_ptr> &v) { - peers.push_back(v.value); - }, - [&](expected::Error> &e) { - log_->info(*e.error); - }); - } - return peers; - }; + soci::rowset rows = + (sql_.prepare << "SELECT public_key, address FROM peer"); + std::vector> peers; + + auto results = transform< + shared_model::builder::BuilderResult>( + rows, makePeer); + for (auto &r : results) { + r.match( + [&](expected::Value> + &v) { peers.push_back(v.value); }, + [&](expected::Error> &e) { + log_->info(*e.error); + }); + } + return peers; } } // namespace ametsuchi } // namespace iroha diff --git a/irohad/ametsuchi/impl/postgres_wsv_query.hpp b/irohad/ametsuchi/impl/postgres_wsv_query.hpp index 3a15c67506..3fd0c64acd 100644 --- a/irohad/ametsuchi/impl/postgres_wsv_query.hpp +++ b/irohad/ametsuchi/impl/postgres_wsv_query.hpp @@ -20,17 +20,15 @@ #include "ametsuchi/wsv_query.hpp" -#include - #include "postgres_wsv_common.hpp" namespace iroha { namespace ametsuchi { class PostgresWsvQuery : public WsvQuery { public: - explicit PostgresWsvQuery(pqxx::nontransaction &transaction); - PostgresWsvQuery(std::unique_ptr connection, - std::unique_ptr transaction); + explicit PostgresWsvQuery(soci::session &sql); + explicit PostgresWsvQuery(std::unique_ptr sql_ptr); + boost::optional> getAccountRoles(const shared_model::interface::types::AccountIdType &account_id) override; @@ -84,14 +82,9 @@ namespace iroha { shared_model::interface::permissions::Grantable permission) override; private: - std::unique_ptr connection_ptr_; - std::unique_ptr transaction_ptr_; - - pqxx::nontransaction &transaction_; + std::unique_ptr sql_ptr_; + soci::session &sql_; logger::Logger log_; - - using ExecuteType = decltype(makeExecuteOptional(transaction_, log_)); - ExecuteType execute_; }; } // namespace ametsuchi } // namespace iroha diff --git a/irohad/ametsuchi/impl/storage_impl.cpp b/irohad/ametsuchi/impl/storage_impl.cpp index 566f008d05..7acf98dd6f 100644 --- a/irohad/ametsuchi/impl/storage_impl.cpp +++ b/irohad/ametsuchi/impl/storage_impl.cpp @@ -16,7 +16,10 @@ */ #include "ametsuchi/impl/storage_impl.hpp" + #include +#include + #include "ametsuchi/impl/flat_file/flat_file.hpp" #include "ametsuchi/impl/mutable_storage_impl.hpp" #include "ametsuchi/impl/postgres_block_query.hpp" @@ -39,43 +42,27 @@ namespace iroha { StorageImpl::StorageImpl(std::string block_store_dir, PostgresOptions postgres_options, - std::unique_ptr block_store) + std::unique_ptr block_store, + std::shared_ptr connection) : block_store_dir_(std::move(block_store_dir)), postgres_options_(std::move(postgres_options)), block_store_(std::move(block_store)), + connection_(connection), log_(logger::log("StorageImpl")) {} expected::Result, std::string> StorageImpl::createTemporaryWsv() { - auto postgres_connection = std::make_unique( - postgres_options_.optionsString()); - try { - postgres_connection->activate(); - } catch (const pqxx::broken_connection &e) { - return expected::makeError( - (boost::format(kPsqlBroken) % e.what()).str()); - } - auto wsv_transaction = - std::make_unique(*postgres_connection, kTmpWsv); + auto sql = std::make_unique(*connection_); return expected::makeValue>( - std::make_unique(std::move(postgres_connection), - std::move(wsv_transaction))); + std::make_unique(std::move(sql))); } expected::Result, std::string> StorageImpl::createMutableStorage() { - auto postgres_connection = std::make_unique( - postgres_options_.optionsString()); - try { - postgres_connection->activate(); - } catch (const pqxx::broken_connection &e) { - return expected::makeError( - (boost::format(kPsqlBroken) % e.what()).str()); - } - auto wsv_transaction = - std::make_unique(*postgres_connection, kTmpWsv); + boost::optional top_hash; + auto sql = std::make_unique(*connection_); auto block_result = getBlockQuery()->getTopBlock(); return expected::makeValue>( std::make_unique( @@ -87,8 +74,7 @@ namespace iroha { [](expected::Error &) { return shared_model::interface::types::HashType(""); }), - std::move(postgres_connection), - std::move(wsv_transaction))); + std::move(sql))); } bool StorageImpl::insertBlock(const shared_model::interface::Block &block) { @@ -161,14 +147,10 @@ DROP TABLE IF EXISTS index_by_id_height_asset; // erase db log_->info("drop db"); - pqxx::connection connection(postgres_options_.optionsString()); - pqxx::work txn(connection); - txn.exec(drop); - txn.commit(); - pqxx::work init_txn(connection); - init_txn.exec(init_); - init_txn.commit(); + soci::session sql(*connection_); + sql << drop; + sql << init_; // erase blocks log_->info("drop block store"); @@ -178,20 +160,24 @@ DROP TABLE IF EXISTS index_by_id_height_asset; expected::Result StorageImpl::createDatabaseIfNotExist( const std::string &dbname, const std::string &options_str_without_dbname) { - pqxx::lazyconnection temp_connection(options_str_without_dbname); - auto transaction = - std::make_unique(temp_connection); - // check if database dbname exists try { - auto result = transaction->exec( - "SELECT datname FROM pg_catalog.pg_database WHERE datname = " - + transaction->quote(dbname)); - if (result.size() == 0) { - transaction->exec("CREATE DATABASE " + dbname); + soci::session sql(soci::postgresql, options_str_without_dbname); + + int size; + std::string name = dbname; + + sql << "SELECT count(datname) FROM pg_catalog.pg_database WHERE " + "datname = :dbname", + soci::into(size), soci::use(name); + + if (size == 0) { + std::string query = "CREATE DATABASE "; + query += dbname; + sql << query; return expected::makeValue(true); } return expected::makeValue(false); - } catch (const pqxx::failure &e) { + } catch (std::exception &e) { return expected::makeError( std::string("Connection to PostgreSQL broken: ") + e.what()); } @@ -213,6 +199,18 @@ DROP TABLE IF EXISTS index_by_id_height_asset; return expected::makeValue(ConnectionContext(std::move(*block_store))); } + expected::Result, std::string> + StorageImpl::initPostgresConnection(std::string &options_str, + size_t pool_size) { + auto pool = std::make_shared(pool_size); + + for (size_t i = 0; i != pool_size; i++) { + soci::session &session = pool->at(i); + session.open(soci::postgresql, options_str); + } + return expected::makeValue(pool); + }; + expected::Result, std::string> StorageImpl::create(std::string block_store_dir, std::string postgres_options) { @@ -234,13 +232,20 @@ DROP TABLE IF EXISTS index_by_id_height_asset; } auto ctx_result = initConnections(block_store_dir); + auto db_result = initPostgresConnection(postgres_options); expected::Result, std::string> storage; ctx_result.match( [&](expected::Value &ctx) { - storage = expected::makeValue(std::shared_ptr( - new StorageImpl(block_store_dir, - options, - std::move(ctx.value.block_store)))); + db_result.match( + [&](expected::Value> + &connection) { + storage = expected::makeValue(std::shared_ptr( + new StorageImpl(block_store_dir, + options, + std::move(ctx.value.block_store), + connection.value))); + }, + [&](expected::Error &error) { storage = error; }); }, [&](expected::Error &error) { storage = error; }); return storage; @@ -259,42 +264,21 @@ DROP TABLE IF EXISTS index_by_id_height_asset; notifier_.get_subscriber().on_next(block.second); } - storage->transaction_->exec("COMMIT;"); + *(storage->sql_) << "COMMIT"; storage->committed = true; } std::shared_ptr StorageImpl::getWsvQuery() const { - auto postgres_connection = std::make_unique( - postgres_options_.optionsString()); - try { - postgres_connection->activate(); - } catch (const pqxx::broken_connection &e) { - // TODO 29.03.2018 vdrobny IR-1184 Handle this exception - throw pqxx::broken_connection(e); - } - auto wsv_transaction = - std::make_unique(*postgres_connection); - - return std::make_shared(std::move(postgres_connection), - std::move(wsv_transaction)); + auto sql = std::make_unique(*connection_); + return std::make_shared(std::move(sql)); } std::shared_ptr StorageImpl::getBlockQuery() const { - auto postgres_connection = std::make_unique( - postgres_options_.optionsString()); - try { - postgres_connection->activate(); - } catch (const pqxx::broken_connection &e) { - // TODO 29.03.2018 vdrobny IR-1184 Handle this exception - throw pqxx::broken_connection(e); - } - auto wsv_transaction = - std::make_unique(*postgres_connection); + auto sql = std::make_unique( + soci::postgresql, postgres_options_.optionsString()); - return std::make_shared( - std::move(postgres_connection), - std::move(wsv_transaction), - *block_store_); + return std::make_shared(std::move(sql), + *block_store_); } rxcpp::observable> @@ -314,7 +298,7 @@ CREATE TABLE IF NOT EXISTS domain ( PRIMARY KEY (domain_id) ); CREATE TABLE IF NOT EXISTS signatory ( - public_key bytea NOT NULL, + public_key varchar NOT NULL, PRIMARY KEY (public_key) ); CREATE TABLE IF NOT EXISTS account ( @@ -326,11 +310,11 @@ CREATE TABLE IF NOT EXISTS account ( ); CREATE TABLE IF NOT EXISTS account_has_signatory ( account_id character varying(288) NOT NULL REFERENCES account, - public_key bytea NOT NULL REFERENCES signatory, + public_key varchar NOT NULL REFERENCES signatory, PRIMARY KEY (account_id, public_key) ); CREATE TABLE IF NOT EXISTS peer ( - public_key bytea NOT NULL, + public_key varchar NOT NULL, address character varying(261) NOT NULL UNIQUE, PRIMARY KEY (public_key) ); @@ -369,7 +353,7 @@ CREATE TABLE IF NOT EXISTS account_has_grantable_permissions ( PRIMARY KEY (permittee_account_id, account_id) ); CREATE TABLE IF NOT EXISTS height_by_hash ( - hash bytea, + hash varchar, height text ); CREATE TABLE IF NOT EXISTS height_by_account_set ( diff --git a/irohad/ametsuchi/impl/storage_impl.hpp b/irohad/ametsuchi/impl/storage_impl.hpp index 19633aa97d..32788dbbde 100644 --- a/irohad/ametsuchi/impl/storage_impl.hpp +++ b/irohad/ametsuchi/impl/storage_impl.hpp @@ -20,9 +20,9 @@ #include "ametsuchi/storage.hpp" +#include #include #include -#include #include #include "ametsuchi/impl/postgres_options.hpp" #include "ametsuchi/key_value_storage.hpp" @@ -48,6 +48,10 @@ namespace iroha { static expected::Result initConnections( std::string block_store_dir); + static expected::Result, + std::string> + initPostgresConnection(std::string &options_str, size_t pool_size = 10); + public: static expected::Result, std::string> create( std::string block_store_dir, std::string postgres_connection); @@ -89,7 +93,8 @@ namespace iroha { protected: StorageImpl(std::string block_store_dir, PostgresOptions postgres_options, - std::unique_ptr block_store); + std::unique_ptr block_store, + std::shared_ptr connection); /** * Folder with raw blocks @@ -102,6 +107,8 @@ namespace iroha { private: std::unique_ptr block_store_; + std::shared_ptr connection_; + // Allows multiple readers and a single writer std::shared_timed_mutex rw_lock_; diff --git a/irohad/ametsuchi/impl/temporary_wsv_impl.cpp b/irohad/ametsuchi/impl/temporary_wsv_impl.cpp index 310e14599a..82c9ac932e 100644 --- a/irohad/ametsuchi/impl/temporary_wsv_impl.cpp +++ b/irohad/ametsuchi/impl/temporary_wsv_impl.cpp @@ -23,19 +23,14 @@ namespace iroha { namespace ametsuchi { - TemporaryWsvImpl::TemporaryWsvImpl( - std::unique_ptr connection, - std::unique_ptr transaction) - : connection_(std::move(connection)), - transaction_(std::move(transaction)), - wsv_(std::make_unique(*transaction_)), - executor_(std::make_unique(*transaction_)), + TemporaryWsvImpl::TemporaryWsvImpl(std::unique_ptr sql) + : sql_(std::move(sql)), + wsv_(std::make_shared(*sql_)), + executor_(std::make_shared(*sql_)), + command_executor_(std::make_shared(wsv_, executor_)), + command_validator_(std::make_shared(wsv_)), log_(logger::log("TemporaryWSV")) { - auto query = std::make_shared(*transaction_); - auto command = std::make_shared(*transaction_); - command_executor_ = std::make_shared(query, command); - command_validator_ = std::make_shared(query); - transaction_->exec("BEGIN;"); + *sql_ << "BEGIN"; } expected::Result TemporaryWsvImpl::apply( @@ -56,7 +51,7 @@ namespace iroha { }; }; - transaction_->exec("SAVEPOINT savepoint_;"); + *sql_ << "SAVEPOINT savepoint_"; return apply_function(tx, *wsv_) | [this, @@ -78,18 +73,19 @@ namespace iroha { return false; }); if (not cmd_is_valid) { - transaction_->exec("ROLLBACK TO SAVEPOINT savepoint_;"); + *sql_ << "ROLLBACK TO SAVEPOINT savepoint_"; return expected::makeError(cmd_error); } } // success - transaction_->exec("RELEASE SAVEPOINT savepoint_;"); + *sql_ << "RELEASE SAVEPOINT savepoint_"; + return {}; }; } TemporaryWsvImpl::~TemporaryWsvImpl() { - transaction_->exec("ROLLBACK;"); + *sql_ << "ROLLBACK"; } } // namespace ametsuchi } // namespace iroha diff --git a/irohad/ametsuchi/impl/temporary_wsv_impl.hpp b/irohad/ametsuchi/impl/temporary_wsv_impl.hpp index 6a336ac096..f1780bff2d 100644 --- a/irohad/ametsuchi/impl/temporary_wsv_impl.hpp +++ b/irohad/ametsuchi/impl/temporary_wsv_impl.hpp @@ -18,8 +18,7 @@ #ifndef IROHA_TEMPORARY_WSV_IMPL_HPP #define IROHA_TEMPORARY_WSV_IMPL_HPP -#include -#include +#include #include "ametsuchi/temporary_wsv.hpp" #include "execution/command_executor.hpp" @@ -30,8 +29,7 @@ namespace iroha { namespace ametsuchi { class TemporaryWsvImpl : public TemporaryWsv { public: - TemporaryWsvImpl(std::unique_ptr connection, - std::unique_ptr transaction); + explicit TemporaryWsvImpl(std::unique_ptr sql); expected::Result apply( const shared_model::interface::Transaction &, @@ -42,10 +40,9 @@ namespace iroha { ~TemporaryWsvImpl() override; private: - std::unique_ptr connection_; - std::unique_ptr transaction_; - std::unique_ptr wsv_; - std::unique_ptr executor_; + std::unique_ptr sql_; + std::shared_ptr wsv_; + std::shared_ptr executor_; std::shared_ptr command_executor_; std::shared_ptr command_validator_; diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml index fc482af82d..129e10252f 100644 --- a/snap/snapcraft.yaml +++ b/snap/snapcraft.yaml @@ -42,7 +42,7 @@ parts: - rxcpp - rapidjson - libpq - - pqxx + - soci - tbb - ed25519 cmake: @@ -120,12 +120,21 @@ parts: make -C src/bin/pg_config install DESTDIR=$SNAPCRAFT_PART_INSTALL make -C src/interfaces/libpq install DESTDIR=$SNAPCRAFT_PART_INSTALL make -C src/include install DESTDIR=$SNAPCRAFT_PART_INSTALL - pqxx: - source: https://github.com/jtv/libpqxx.git - source-commit: 5b17abce5ac2b1a2f8278718405b7ade8bb30ae9 - plugin: autotools - configflags: [--disable-documentation, --with-pic] - after: [libpq] + soci: + source: https://github.com/SOCI/soci.git + source-commit: 111b50af8c3876ea392367640b4bd83b4f903ab8 + plugin: cmake + configflags: + - -DWITH_BOOST=ON + - -DWITH_DB2=OFF + - -DWITH_FIREBIRD=OFF + - -DWITH_MYSQL=OFF + - -DWITH_ODBC=OFF + - -DWITH_ORACLE=OFF + - -DWITH_POSTGRESQL=ON + - -DWITH_SQLITE3=OFF + - -DCMAKE_INSTALL_PREFIX=/ + after: [cmake, boost, libpq] tbb: source: https://github.com/01org/tbb.git source-commit: eb6336ad29450f2a64af5123ca1b9429ff6bc11d diff --git a/test/module/irohad/ametsuchi/CMakeLists.txt b/test/module/irohad/ametsuchi/CMakeLists.txt index 197464d502..2e71ca7c54 100644 --- a/test/module/irohad/ametsuchi/CMakeLists.txt +++ b/test/module/irohad/ametsuchi/CMakeLists.txt @@ -57,6 +57,7 @@ target_link_libraries(postgres_options_test add_library(ametsuchi_fixture INTERFACE) target_link_libraries(ametsuchi_fixture INTERFACE - pqxx integration_framework_config_helper + SOCI::core + SOCI::postgresql ) diff --git a/test/module/irohad/ametsuchi/ametsuchi_fixture.hpp b/test/module/irohad/ametsuchi/ametsuchi_fixture.hpp index cddf2e248f..6614f68333 100644 --- a/test/module/irohad/ametsuchi/ametsuchi_fixture.hpp +++ b/test/module/irohad/ametsuchi/ametsuchi_fixture.hpp @@ -19,10 +19,11 @@ #define IROHA_AMETSUCHI_FIXTURE_HPP #include +#include +#include #include #include #include -#include #include "ametsuchi/impl/storage_impl.hpp" #include "common/files.hpp" #include "framework/config_helper.hpp" @@ -45,15 +46,13 @@ namespace iroha { protected: virtual void clear() { - pqxx::work txn(*connection); - txn.exec(drop_); - txn.commit(); + *sql << drop_; iroha::remove_dir_contents(block_store_path); } virtual void disconnect() { - connection->disconnect(); + sql->close(); } virtual void connect() { @@ -64,12 +63,7 @@ namespace iroha { FAIL() << "StorageImpl: " << error.error; }); - connection = std::make_shared(pgopt_); - try { - connection->activate(); - } catch (const pqxx::broken_connection &e) { - FAIL() << "Connection to PostgreSQL broken: " << e.what(); - } + sql = std::make_shared(soci::postgresql, pgopt_); } void SetUp() override { @@ -82,7 +76,7 @@ namespace iroha { disconnect(); } - std::shared_ptr connection; + std::shared_ptr sql; std::shared_ptr storage; @@ -127,7 +121,7 @@ CREATE TABLE IF NOT EXISTS domain ( PRIMARY KEY (domain_id) ); CREATE TABLE IF NOT EXISTS signatory ( - public_key bytea NOT NULL, + public_key varchar NOT NULL, PRIMARY KEY (public_key) ); CREATE TABLE IF NOT EXISTS account ( @@ -139,11 +133,11 @@ CREATE TABLE IF NOT EXISTS account ( ); CREATE TABLE IF NOT EXISTS account_has_signatory ( account_id character varying(288) NOT NULL REFERENCES account, - public_key bytea NOT NULL REFERENCES signatory, + public_key varchar NOT NULL REFERENCES signatory, PRIMARY KEY (account_id, public_key) ); CREATE TABLE IF NOT EXISTS peer ( - public_key bytea NOT NULL, + public_key varchar NOT NULL, address character varying(261) NOT NULL UNIQUE, PRIMARY KEY (public_key) ); @@ -177,7 +171,7 @@ CREATE TABLE IF NOT EXISTS account_has_grantable_permissions ( PRIMARY KEY (permittee_account_id, account_id, permission_id) ); CREATE TABLE IF NOT EXISTS height_by_hash ( - hash bytea, + hash varchar, height text ); CREATE TABLE IF NOT EXISTS height_by_account_set ( diff --git a/test/module/irohad/ametsuchi/ametsuchi_test.cpp b/test/module/irohad/ametsuchi/ametsuchi_test.cpp index 0dbc69d073..b2ed612837 100644 --- a/test/module/irohad/ametsuchi/ametsuchi_test.cpp +++ b/test/module/irohad/ametsuchi/ametsuchi_test.cpp @@ -931,11 +931,7 @@ TEST_F(AmetsuchiTest, TestRestoreWSV) { EXPECT_TRUE(res); // spoil WSV - pqxx::work txn(*connection); - txn.exec(R"( -DELETE FROM domain; -)"); - txn.commit(); + *sql << "DELETE FROM domain"; // check there is no data in WSV res = storage->getWsvQuery()->getDomain("test"); diff --git a/test/module/irohad/ametsuchi/block_query_test.cpp b/test/module/irohad/ametsuchi/block_query_test.cpp index 0e38d77b7a..12464cf971 100644 --- a/test/module/irohad/ametsuchi/block_query_test.cpp +++ b/test/module/irohad/ametsuchi/block_query_test.cpp @@ -41,21 +41,14 @@ class BlockQueryTest : public AmetsuchiTest { ASSERT_TRUE(tmp); file = std::move(*tmp); mock_file = std::make_shared(); - postgres_connection = std::make_unique(pgopt_); - try { - postgres_connection->activate(); - } catch (const pqxx::broken_connection &e) { - FAIL() << "Connection to PostgreSQL broken: " << e.what(); - } - transaction = std::make_unique( - *postgres_connection, "Postgres block indexes"); + sql = std::make_unique(soci::postgresql, pgopt_); - index = std::make_shared(*transaction); - blocks = std::make_shared(*transaction, *file); + index = std::make_shared(*sql); + blocks = std::make_shared(*sql, *file); empty_blocks = - std::make_shared(*transaction, *mock_file); + std::make_shared(*sql, *mock_file); - transaction->exec(init_); + *sql << init_; // First transaction in block1 auto txn1_1 = TestTransactionBuilder().creatorAccountId(creator1).build(); @@ -98,8 +91,7 @@ class BlockQueryTest : public AmetsuchiTest { } } - std::unique_ptr postgres_connection; - std::unique_ptr transaction; + std::unique_ptr sql; std::vector tx_hashes; std::shared_ptr blocks; std::shared_ptr empty_blocks; diff --git a/test/module/irohad/ametsuchi/block_query_transfer_test.cpp b/test/module/irohad/ametsuchi/block_query_transfer_test.cpp index de91e7252f..906acddc50 100644 --- a/test/module/irohad/ametsuchi/block_query_transfer_test.cpp +++ b/test/module/irohad/ametsuchi/block_query_transfer_test.cpp @@ -37,19 +37,12 @@ namespace iroha { ASSERT_TRUE(tmp); file = std::move(*tmp); - postgres_connection = std::make_unique(pgopt_); - try { - postgres_connection->activate(); - } catch (const pqxx::broken_connection &e) { - FAIL() << "Connection to PostgreSQL broken: " << e.what(); - } - transaction = std::make_unique( - *postgres_connection, "Postgres block indexes"); - - index = std::make_shared(*transaction); - blocks = std::make_shared(*transaction, *file); - - transaction->exec(init_); + sql = std::make_unique(soci::postgresql, pgopt_); + + index = std::make_shared(*sql); + blocks = std::make_shared(*sql, *file); + + *sql << init_; } void insert(const shared_model::proto::Block &block) { @@ -59,8 +52,7 @@ namespace iroha { index->index(block); } - std::unique_ptr postgres_connection; - std::unique_ptr transaction; + std::unique_ptr sql; std::vector tx_hashes; std::shared_ptr blocks; std::shared_ptr index; diff --git a/test/module/irohad/ametsuchi/storage_init_test.cpp b/test/module/irohad/ametsuchi/storage_init_test.cpp index fe3a3e40e1..576f785fdb 100644 --- a/test/module/irohad/ametsuchi/storage_init_test.cpp +++ b/test/module/irohad/ametsuchi/storage_init_test.cpp @@ -4,6 +4,8 @@ */ #include +#include +#include #include #include #include @@ -33,10 +35,9 @@ class StorageInitTest : public ::testing::Test { std::string pgopt_; void TearDown() override { - auto temp_connection = - std::make_unique(pg_opt_without_dbname_); - auto nontx = std::make_unique(*temp_connection); - nontx->exec("DROP DATABASE IF EXISTS " + dbname_); + soci::session sql(soci::postgresql, pg_opt_without_dbname_); + std::string query = "DROP DATABASE IF EXISTS " + dbname_; + sql << query; } }; @@ -49,13 +50,12 @@ TEST_F(StorageInitTest, CreateStorageWithDatabase) { StorageImpl::create(block_store_path, pgopt_) .match([](const Value> &) { SUCCEED(); }, [](const Error &error) { FAIL() << error.error; }); - auto temp_connection = - std::make_unique(pg_opt_without_dbname_); - auto transaction = std::make_unique(*temp_connection); - auto result = transaction->exec( - "SELECT datname FROM pg_catalog.pg_database WHERE datname = " - + transaction->quote(dbname_)); - ASSERT_EQ(result.size(), 1); + soci::session sql(soci::postgresql, pg_opt_without_dbname_); + int size; + sql << "SELECT COUNT(datname) FROM pg_catalog.pg_database WHERE datname = " + ":dbname", + soci::into(size), soci::use(dbname_); + ASSERT_EQ(size, 1); } /** diff --git a/test/module/irohad/ametsuchi/wsv_query_command_test.cpp b/test/module/irohad/ametsuchi/wsv_query_command_test.cpp index 6ba63d8936..aea5b3f907 100644 --- a/test/module/irohad/ametsuchi/wsv_query_command_test.cpp +++ b/test/module/irohad/ametsuchi/wsv_query_command_test.cpp @@ -48,19 +48,12 @@ namespace iroha { void SetUp() override { AmetsuchiTest::SetUp(); - postgres_connection = std::make_unique(pgopt_); - try { - postgres_connection->activate(); - } catch (const pqxx::broken_connection &e) { - FAIL() << "Connection to PostgreSQL broken: " << e.what(); - } - wsv_transaction = - std::make_unique(*postgres_connection); - - command = std::make_unique(*wsv_transaction); - query = std::make_unique(*wsv_transaction); - - wsv_transaction->exec(init_); + sql = std::make_unique(soci::postgresql, pgopt_); + + command = std::make_unique(*sql); + query = std::make_unique(*sql); + + *sql << init_; } std::string role = "role"; @@ -69,8 +62,7 @@ namespace iroha { std::unique_ptr account; std::unique_ptr domain; - std::unique_ptr postgres_connection; - std::unique_ptr wsv_transaction; + std::unique_ptr sql; std::unique_ptr command; std::unique_ptr query; @@ -104,6 +96,11 @@ namespace iroha { ASSERT_EQ(0, roles->size()); } + TEST_F(RoleTest, InsertTwoRole) { + ASSERT_TRUE(val(command->insertRole("role"))); + ASSERT_TRUE(err(command->insertRole("role"))); + } + class RolePermissionsTest : public WsvQueryCommandTest { void SetUp() override { WsvQueryCommandTest::SetUp(); @@ -121,7 +118,7 @@ namespace iroha { auto permissions = query->getRolePermissions(role); ASSERT_TRUE(permissions); - ASSERT_EQ(role_permissions, permissions); + ASSERT_EQ(role_permissions, permissions.get()); } /** @@ -548,20 +545,15 @@ namespace iroha { // skip database setup void SetUp() override { AmetsuchiTest::SetUp(); - postgres_connection = std::make_unique(pgopt_); - try { - postgres_connection->activate(); - } catch (const pqxx::broken_connection &e) { - FAIL() << "Connection to PostgreSQL broken: " << e.what(); - } - wsv_transaction = - std::make_unique(*postgres_connection); - - command = std::make_unique(*wsv_transaction); - query = std::make_unique(*wsv_transaction); + sql = std::make_unique(soci::postgresql, pgopt_); + + command = std::make_unique(*sql); + query = std::make_unique(*sql); } }; + std::unique_ptr sql; + /** * @given not set up database * @when performing query to retrieve information from nonexisting tables diff --git a/test/system/CMakeLists.txt b/test/system/CMakeLists.txt index a8c3393ac8..d36d5eaed0 100644 --- a/test/system/CMakeLists.txt +++ b/test/system/CMakeLists.txt @@ -1,7 +1,8 @@ addtest(irohad_test irohad_test.cpp) target_link_libraries(irohad_test boost - pqxx + SOCI::core + SOCI::postgresql rapidjson integration_framework_config_helper libs_common diff --git a/test/system/irohad_test.cpp b/test/system/irohad_test.cpp index 7f83e076dc..027c881078 100644 --- a/test/system/irohad_test.cpp +++ b/test/system/irohad_test.cpp @@ -22,7 +22,8 @@ #include #include #include -#include +#include +#include #include "common/files.hpp" #include "common/types.hpp" @@ -97,13 +98,6 @@ class IrohadTest : public testing::Test { } void dropPostgres() { - auto connection = std::make_shared(pgopts_); - try { - connection->activate(); - } catch (const pqxx::broken_connection &e) { - FAIL() << "Connection to PostgreSQL broken: " << e.what(); - } - const auto drop = R"( DROP TABLE IF EXISTS account_has_signatory; DROP TABLE IF EXISTS account_has_asset; @@ -122,10 +116,8 @@ DROP TABLE IF EXISTS index_by_creator_height; DROP TABLE IF EXISTS index_by_id_height_asset; )"; - pqxx::work txn(*connection); - txn.exec(drop); - txn.commit(); - connection->disconnect(); + soci::session sql(soci::postgresql, pgopts_); + sql << drop; } public: From 89d8c636c10c91affdc1963cc601cd19013b78ac Mon Sep 17 00:00:00 2001 From: Fedor Muratov Date: Fri, 13 Jul 2018 13:01:52 +0300 Subject: [PATCH 44/97] Add concurrent fix for abstract cache (#1560) * Fix missed locks in abstract_concurrent_cahce * Improve locking model with read/write lock semantic Signed-off-by: Fedor Muratov --- libs/cache/abstract_cache.hpp | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/libs/cache/abstract_cache.hpp b/libs/cache/abstract_cache.hpp index fdab575acf..66bb14ae91 100644 --- a/libs/cache/abstract_cache.hpp +++ b/libs/cache/abstract_cache.hpp @@ -20,7 +20,7 @@ #include #include -#include +#include #include namespace iroha { @@ -41,6 +41,8 @@ namespace iroha { * @return high border of cache limit (@see AbstractCache#addItem) */ uint32_t getIndexSizeHigh() const { + // shared lock + std::shared_lock lock(access_mutex_); return constUnderlying().getIndexSizeHighImpl(); } @@ -48,6 +50,8 @@ namespace iroha { * @return low border of cache limit (@see AbstractCache#addItem) */ uint32_t getIndexSizeLow() const { + // shared lock + std::shared_lock lock(access_mutex_); return constUnderlying().getIndexSizeLowImpl(); } @@ -55,6 +59,8 @@ namespace iroha { * @return amount of items in cache */ uint32_t getCacheItemCount() const { + // shared lock + std::shared_lock lock(access_mutex_); return constUnderlying().getCacheItemCountImpl(); } @@ -69,7 +75,8 @@ namespace iroha { * @param value - value to insert */ void addItem(const KeyType &key, const ValueType &value) { - std::lock_guard lock(access_mutex_); + // exclusive lock + std::lock_guard lock(access_mutex_); underlying().addItemImpl(key, value); } @@ -79,7 +86,8 @@ namespace iroha { * @return Optional of ValueType */ boost::optional findItem(const KeyType &key) const { - std::lock_guard lock(access_mutex_); + // shared lock + std::shared_lock lock(access_mutex_); return constUnderlying().findItemImpl(key); } @@ -91,7 +99,7 @@ namespace iroha { return static_cast(*this); } - mutable std::mutex access_mutex_; + mutable std::shared_timed_mutex access_mutex_; }; } // namespace cache } // namespace iroha From 465ab52c38dfcc8568c078991d99a0cb1f15f8a4 Mon Sep 17 00:00:00 2001 From: Akvinikym Date: Fri, 13 Jul 2018 19:29:55 +0300 Subject: [PATCH 45/97] Transaction Batches Stateful Validation (#1552) Signed-off-by: Akvinikym --- irohad/ametsuchi/impl/temporary_wsv_impl.cpp | 36 ++- irohad/ametsuchi/impl/temporary_wsv_impl.hpp | 19 +- irohad/ametsuchi/temporary_wsv.hpp | 26 +- .../impl/stateful_validator_impl.cpp | 278 +++++++++++------- .../impl/stateful_validator_impl.hpp | 2 +- .../irohad/ametsuchi/ametsuchi_mocks.hpp | 27 +- test/module/irohad/validation/CMakeLists.txt | 1 + .../validation/stateful_validator_test.cpp | 194 ++++++++++++ 8 files changed, 468 insertions(+), 115 deletions(-) diff --git a/irohad/ametsuchi/impl/temporary_wsv_impl.cpp b/irohad/ametsuchi/impl/temporary_wsv_impl.cpp index 82c9ac932e..563b3aac01 100644 --- a/irohad/ametsuchi/impl/temporary_wsv_impl.cpp +++ b/irohad/ametsuchi/impl/temporary_wsv_impl.cpp @@ -51,10 +51,10 @@ namespace iroha { }; }; - *sql_ << "SAVEPOINT savepoint_"; + auto savepoint_wrapper = createSavepoint("savepoint_temp_wsv"); return apply_function(tx, *wsv_) | - [this, + [savepoint = std::move(savepoint_wrapper), &execute_command, &tx]() -> expected::Result { // check transaction's commands validity @@ -73,19 +73,45 @@ namespace iroha { return false; }); if (not cmd_is_valid) { - *sql_ << "ROLLBACK TO SAVEPOINT savepoint_"; return expected::makeError(cmd_error); } } // success - *sql_ << "RELEASE SAVEPOINT savepoint_"; - + savepoint->release(); return {}; }; } + std::unique_ptr + TemporaryWsvImpl::createSavepoint(const std::string &name) { + return std::make_unique( + SavepointWrapperImpl(*this, name)); + } + TemporaryWsvImpl::~TemporaryWsvImpl() { *sql_ << "ROLLBACK"; } + + TemporaryWsvImpl::SavepointWrapperImpl::SavepointWrapperImpl( + const iroha::ametsuchi::TemporaryWsvImpl &wsv, + std::string savepoint_name) + : sql_{wsv.sql_}, + savepoint_name_{std::move(savepoint_name)}, + is_released_{false} { + *sql_ << "SAVEPOINT " + savepoint_name_ + ";"; + }; + + void TemporaryWsvImpl::SavepointWrapperImpl::release() { + is_released_ = true; + } + + TemporaryWsvImpl::SavepointWrapperImpl::~SavepointWrapperImpl() { + if (not is_released_) { + *sql_ << "ROLLBACK TO SAVEPOINT " + savepoint_name_ + ";"; + } else { + *sql_ << "RELEASE SAVEPOINT " + savepoint_name_ + ";"; + } + } + } // namespace ametsuchi } // namespace iroha diff --git a/irohad/ametsuchi/impl/temporary_wsv_impl.hpp b/irohad/ametsuchi/impl/temporary_wsv_impl.hpp index f1780bff2d..1ed34f2c3c 100644 --- a/irohad/ametsuchi/impl/temporary_wsv_impl.hpp +++ b/irohad/ametsuchi/impl/temporary_wsv_impl.hpp @@ -29,6 +29,20 @@ namespace iroha { namespace ametsuchi { class TemporaryWsvImpl : public TemporaryWsv { public: + struct SavepointWrapperImpl : public TemporaryWsv::SavepointWrapper { + SavepointWrapperImpl(const TemporaryWsvImpl &wsv, + std::string savepoint_name); + + void release() override; + + ~SavepointWrapperImpl() override; + + private: + std::shared_ptr sql_; + std::string savepoint_name_; + bool is_released_; + }; + explicit TemporaryWsvImpl(std::unique_ptr sql); expected::Result apply( @@ -37,10 +51,13 @@ namespace iroha { const shared_model::interface::Transaction &, WsvQuery &)> function) override; + std::unique_ptr createSavepoint( + const std::string &name) override; + ~TemporaryWsvImpl() override; private: - std::unique_ptr sql_; + std::shared_ptr sql_; std::shared_ptr wsv_; std::shared_ptr executor_; std::shared_ptr command_executor_; diff --git a/irohad/ametsuchi/temporary_wsv.hpp b/irohad/ametsuchi/temporary_wsv.hpp index 92157d7bea..726a8dbf42 100644 --- a/irohad/ametsuchi/temporary_wsv.hpp +++ b/irohad/ametsuchi/temporary_wsv.hpp @@ -20,9 +20,9 @@ #include -#include "validation/stateful_validator_common.hpp" #include "ametsuchi/wsv_command.hpp" #include "ametsuchi/wsv_query.hpp" +#include "validation/stateful_validator_common.hpp" namespace shared_model { namespace interface { @@ -39,6 +39,19 @@ namespace iroha { */ class TemporaryWsv { public: + /** + * Wrapper for savepoints in wsv state; rollbacks to savepoint, if + * destroyed without explicit release, releases it otherwise + */ + struct SavepointWrapper { + /** + * Release the savepoint + */ + virtual void release() = 0; + + virtual ~SavepointWrapper() = default; + }; + /** * Applies a transaction to current state * using logic specified in function @@ -53,9 +66,18 @@ namespace iroha { const shared_model::interface::Transaction &, WsvQuery &)> function) = 0; + /** + * Create a savepoint for wsv state + * @param name of savepoint to be created + * @return RAII wrapper for savepoints + */ + virtual std::unique_ptr createSavepoint( + const std::string &name) = 0; + virtual ~TemporaryWsv() = default; + }; - } // namespace ametsuchi + } // namespace ametsuchi } // namespace iroha #endif // IROHA_TEMPORARYWSV_HPP diff --git a/irohad/validation/impl/stateful_validator_impl.cpp b/irohad/validation/impl/stateful_validator_impl.cpp index 6c9b8ad659..be45190862 100644 --- a/irohad/validation/impl/stateful_validator_impl.cpp +++ b/irohad/validation/impl/stateful_validator_impl.cpp @@ -18,9 +18,6 @@ #include "validation/impl/stateful_validator_impl.hpp" #include -#include -#include -#include #include #include "builders/protobuf/proposal.hpp" @@ -40,7 +37,7 @@ namespace iroha { static std::string formSignaturesErrorMsg( const shared_model::interface::types::SignatureRangeType &signatures, const std::vector - &signatories) { + &signatories) { std::string signatures_string, signatories_string; for (const auto &signature : signatures) { signatures_string.append(signature.publicKey().toString().append("\n")); @@ -49,14 +46,177 @@ namespace iroha { signatories_string.append(signatory.toString().append("\n")); } return (boost::format( - "stateful validator error: signatures in transaction are not " - "account signatories:\n" - "signatures' public keys: %s\n" - "signatories: %s") - % signatures_string % signatories_string) + "stateful validator error: signatures in transaction are not " + "account signatories:\n" + "signatures' public keys: %s\n" + "signatories: %s") + % signatures_string % signatories_string) .str(); } + /** + * Initially checks the transaction: its creator, signatures etc + * @param tx to be checked + * @param queries to get data from + * @return result with void, if check is succesfull, command error otherwise + */ + static expected::Result + initiallyCheckTransaction(const shared_model::interface::Transaction &tx, + ametsuchi::WsvQuery &queries) { + return expected::Result( + [&]() -> expected::Result< + std::shared_ptr, + validation::CommandError> { + // Check if tx creator has account + auto account = queries.getAccount(tx.creatorAccountId()); + if (account) { + return expected::makeValue(*account); + } + return expected::makeError( + validation::CommandError{"looking up tx creator's account", + (boost::format("could not fetch " + "account with id %s") + % tx.creatorAccountId()) + .str(), + false}); + }() | + [&](const auto &account) + -> expected::Result< + std::vector< + shared_model::interface::types::PubkeyType>, + validation::CommandError> { + // Check if account has signatories and quorum to execute + // transaction + if (boost::size(tx.signatures()) >= account->quorum()) { + auto signatories = queries.getSignatories(tx.creatorAccountId()); + if (signatories) { + return expected::makeValue(*signatories); + } + return expected::makeError(validation::CommandError{ + "looking up tx creator's signatories", + (boost::format("could not fetch " + "signatories of " + "account %s") + % tx.creatorAccountId()) + .str(), + false}); + } + return expected::makeError(validation::CommandError{ + "comparing number of tx signatures to account's quorum", + (boost::format( + "not enough " + "signatures in transaction; account's quorum %d, " + "transaction's " + "signatures amount %d") + % account->quorum() % boost::size(tx.signatures())) + .str(), + false}); + } | [&tx](const auto &signatories) + -> expected::Result { + // Check if signatures in transaction are in account + // signatory + if (signaturesSubset(tx.signatures(), signatories)) { + return {}; + } + return expected::makeError(validation::CommandError{ + "signatures are a subset of signatories", + formSignaturesErrorMsg(tx.signatures(), signatories), + false}); + }); + } + + /** + * Complements initial transaction check with command-by-command check + * @param temporary_wsv to apply commands on + * @param transactions_errors_log to write errors to + * @param tx to be checked + * @return empty result, if check is succesfull, command error otherwise + */ + static bool checkTransactions( + ametsuchi::TemporaryWsv &temporary_wsv, + validation::TransactionsErrors &transactions_errors_log, + const shared_model::interface::Transaction &tx) { + return temporary_wsv.apply(tx, initiallyCheckTransaction) + .match([](expected::Value &) { return true; }, + [&tx, &transactions_errors_log]( + expected::Error &error) { + transactions_errors_log.push_back( + std::make_pair(error.error, tx.hash())); + return false; + }); + }; + + /** + * Validate all transactions supplied; includes special rules, such as batch + * validation etc + * @param txs to be validated + * @param temporary_wsv to apply transactions on + * @param transactions_errors_log to write errors to + * @return vector of proto transactions, which passed stateful validation + */ + static std::vector validateTransactions( + const shared_model::interface::types::TransactionsCollectionType &txs, + ametsuchi::TemporaryWsv &temporary_wsv, + validation::TransactionsErrors &transactions_errors_log) { + std::vector valid_proto_txs{}; + // TODO: kamilsa IR-1010 20.02.2018 rework validation logic, so that + // casts to proto are not needed and stateful validator does not know + // about the transport + auto txs_begin = std::begin(txs); + auto txs_end = std::end(txs); + for (size_t i = 0; i < txs.size(); ++i) { + auto current_tx_it = txs_begin + i; + if (not current_tx_it->batchMeta() + or current_tx_it->batchMeta()->get()->type() + != shared_model::interface::types::BatchType::ATOMIC) { + // if transaction does not belong to atomic batch + if (checkTransactions( + temporary_wsv, transactions_errors_log, *current_tx_it)) { + // and it is valid + valid_proto_txs.push_back( + static_cast( + *current_tx_it)); + } + } else { + // find the batch end in proposal's transactions + auto batch_end_hash = + current_tx_it->batchMeta()->get()->transactionHashes().back(); + auto batch_end_it = + std::find_if(current_tx_it, txs_end, [&batch_end_hash](auto &tx) { + return tx.reducedHash() == batch_end_hash; + }); + if (batch_end_it == txs_end) { + // for peer review: adequate exception variants? + throw std::runtime_error("Batch is formed incorrectly"); + } + + // check all batch's transactions for validness + auto savepoint = temporary_wsv.createSavepoint( + "batch_" + current_tx_it->hash().hex()); + if (std::all_of(current_tx_it, batch_end_it + 1, [&temporary_wsv, &transactions_errors_log](auto &tx) { + return checkTransactions( + temporary_wsv, transactions_errors_log, tx); + })) { + // batch is successful; add it to the list of valid_txs and + // release savepoint + std::transform( + current_tx_it, + batch_end_it + 1, + std::back_inserter(valid_proto_txs), + [](const auto &tx) { + return static_cast( + tx); + }); + savepoint->release(); + } + + // move directly to transaction after batch + i += std::distance(current_tx_it, batch_end_it); + } + } + return valid_proto_txs; + } + StatefulValidatorImpl::StatefulValidatorImpl() { log_ = logger::log("SFV"); } @@ -66,105 +226,21 @@ namespace iroha { ametsuchi::TemporaryWsv &temporaryWsv) { log_->info("transactions in proposal: {}", proposal.transactions().size()); - auto checking_transaction = [](const auto &tx, auto &queries) { - return expected::Result( - [&]() -> expected::Result< - std::shared_ptr, - validation::CommandError> { - // Check if tx creator has account - auto account = queries.getAccount(tx.creatorAccountId()); - if (account) { - return expected::makeValue(*account); - } - return expected::makeError( - validation::CommandError{"looking up tx creator's account", - (boost::format("could not fetch " - "account with id %s") - % tx.creatorAccountId()) - .str(), - false}); - }() | - [&](const auto &account) - -> expected::Result< - std::vector< - shared_model::interface::types::PubkeyType>, - validation::CommandError> { - // Check if account has signatories and quorum to execute - // transaction - if (boost::size(tx.signatures()) >= account->quorum()) { - auto signatories = - queries.getSignatories(tx.creatorAccountId()); - if (signatories) { - return expected::makeValue(*signatories); - } - return expected::makeError(validation::CommandError{ - "looking up tx creator's signatories", - (boost::format("could not fetch " - "signatories of " - "account %s") - % tx.creatorAccountId()) - .str(), - false}); - } - return expected::makeError(validation::CommandError{ - "comparing number of tx signatures to account's quorum", - (boost::format( - "not enough " - "signatures in transaction; account's quorum %d, " - "transaction's " - "signatures amount %d") - % account->quorum() % boost::size(tx.signatures())) - .str(), - false}); - } | [&tx](const auto &signatories) - -> expected::Result { - // Check if signatures in transaction are in account - // signatory - if (signaturesSubset(tx.signatures(), signatories)) { - return {}; - } - return expected::makeError(validation::CommandError{ - "signatures are a subset of signatories", - formSignaturesErrorMsg(tx.signatures(), signatories), - false}); - }); - }; - // Filter only valid transactions and accumulate errors auto transactions_errors_log = validation::TransactionsErrors{}; - auto filter = [&temporaryWsv, - checking_transaction, - &transactions_errors_log](auto &tx) { - return temporaryWsv.apply(tx, checking_transaction) - .match([](expected::Value &) { return true; }, - [&transactions_errors_log, - &tx](expected::Error &error) { - transactions_errors_log.push_back( - std::make_pair(error.error, tx.hash())); - return false; - }); - }; - - // TODO: kamilsa IR-1010 20.02.2018 rework validation logic, so that this - // cast is not needed and stateful validator does not know about the - // transport - auto valid_proto_txs = - proposal.transactions() | boost::adaptors::filtered(filter) - | boost::adaptors::transformed([](auto &tx) { - return static_cast(tx); - }); - + auto valid_proto_txs = validateTransactions( + proposal.transactions(), temporaryWsv, transactions_errors_log); auto validated_proposal = shared_model::proto::ProposalBuilder() - .createdTime(proposal.createdTime()) - .height(proposal.height()) - .transactions(valid_proto_txs) - .createdTime(proposal.createdTime()) - .build(); + .createdTime(proposal.createdTime()) + .height(proposal.height()) + .transactions(valid_proto_txs) + .createdTime(proposal.createdTime()) + .build(); log_->info("transactions in verified proposal: {}", validated_proposal.transactions().size()); return std::make_pair(std::make_shared( - validated_proposal.getTransport()), + validated_proposal.getTransport()), transactions_errors_log); } diff --git a/irohad/validation/impl/stateful_validator_impl.hpp b/irohad/validation/impl/stateful_validator_impl.hpp index 8eeb643ee7..0fc9c0f4d2 100644 --- a/irohad/validation/impl/stateful_validator_impl.hpp +++ b/irohad/validation/impl/stateful_validator_impl.hpp @@ -36,8 +36,8 @@ namespace iroha { ametsuchi::TemporaryWsv &temporaryWsv) override; logger::Logger log_; - }; + } // namespace validation } // namespace iroha diff --git a/test/module/irohad/ametsuchi/ametsuchi_mocks.hpp b/test/module/irohad/ametsuchi/ametsuchi_mocks.hpp index 3f4cb18fab..f757c9499a 100644 --- a/test/module/irohad/ametsuchi/ametsuchi_mocks.hpp +++ b/test/module/irohad/ametsuchi/ametsuchi_mocks.hpp @@ -39,11 +39,10 @@ namespace iroha { MOCK_METHOD1(getAccountRoles, boost::optional>( const std::string &account_id)); - MOCK_METHOD3( - getAccountDetail, - boost::optional(const std::string &account_id, - const std::string &key, - const std::string &writer)); + MOCK_METHOD3(getAccountDetail, + boost::optional(const std::string &account_id, + const std::string &key, + const std::string &writer)); MOCK_METHOD1(getRolePermissions, boost::optional( const std::string &role_name)); @@ -190,6 +189,24 @@ namespace iroha { expected::Result, std::string>(void)); }; + class MockTemporaryWsv : public TemporaryWsv { + public: + MOCK_METHOD2( + apply, + expected::Result( + const shared_model::interface::Transaction &, + std::function( + const shared_model::interface::Transaction &, WsvQuery &)>)); + MOCK_METHOD1( + createSavepoint, + std::unique_ptr(const std::string &)); + }; + + class MockTemporaryWsvSavepointWrapper + : public TemporaryWsv::SavepointWrapper { + MOCK_METHOD0(release, void(void)); + }; + class MockMutableStorage : public MutableStorage { public: MOCK_METHOD2( diff --git a/test/module/irohad/validation/CMakeLists.txt b/test/module/irohad/validation/CMakeLists.txt index 363f33ba28..ae60cb471a 100644 --- a/test/module/irohad/validation/CMakeLists.txt +++ b/test/module/irohad/validation/CMakeLists.txt @@ -24,5 +24,6 @@ target_link_libraries(chain_validation_test addtest(stateful_validator_test stateful_validator_test.cpp) target_link_libraries(stateful_validator_test + stateful_validator shared_model_default_builders ) diff --git a/test/module/irohad/validation/stateful_validator_test.cpp b/test/module/irohad/validation/stateful_validator_test.cpp index 7fb10780a4..83177f66aa 100644 --- a/test/module/irohad/validation/stateful_validator_test.cpp +++ b/test/module/irohad/validation/stateful_validator_test.cpp @@ -4,13 +4,32 @@ */ #include +#include +#include + #include "builders/protobuf/common_objects/proto_signature_builder.hpp" +#include "common/result.hpp" #include "cryptography/crypto_provider/crypto_defaults.hpp" +#include "interfaces/iroha_internal/batch_meta.hpp" +#include "interfaces/transaction.hpp" +#include "module/irohad/ametsuchi/ametsuchi_mocks.hpp" +#include "module/shared_model/builders/protobuf/test_proposal_builder.hpp" +#include "module/shared_model/builders/protobuf/test_transaction_builder.hpp" +#include "validation/impl/stateful_validator_impl.hpp" +#include "validation/stateful_validator.hpp" #include "validation/utils.hpp" using namespace iroha::validation; using namespace shared_model::crypto; +using ::testing::_; +using ::testing::A; +using ::testing::ByMove; +using ::testing::ByRef; +using ::testing::Eq; +using ::testing::Return; +using ::testing::ReturnArg; + class SignaturesSubset : public testing::Test { public: auto makeSignature(PublicKey key, std::string sign) { @@ -78,3 +97,178 @@ TEST_F(SignaturesSubset, PublickeyUniqueness) { signatures.push_back(makeSignature(PublicKey("c"), "")); ASSERT_FALSE(signaturesSubset(signatures, keys)); } + +class Validator : public testing::Test { + public: + void SetUp() override { + sfv = std::make_shared(); + temp_wsv_mock = std::make_shared(); + } + + auto createBatch(std::vector creators, + shared_model::interface::types::BatchType batch_type) { + std::vector reduced_hashes; + std::vector txs; + + for (const auto &creator : creators) { + auto tx = TestTransactionBuilder() + .creatorAccountId(creator) + .createdTime(iroha::time::now()) + .quorum(1) + .createAsset("doge", "coin", 1) + .build(); + reduced_hashes.push_back(tx.reducedHash()); + } + + for (const auto &creator : creators) { + txs.push_back(TestTransactionBuilder() + .creatorAccountId(creator) + .createdTime(iroha::time::now()) + .quorum(1) + .createAsset("doge", "coin", 1) + .batchMeta(batch_type, reduced_hashes) + .build()); + } + return txs; + } + + std::shared_ptr sfv; + std::shared_ptr temp_wsv_mock; +}; + +/** + * @given several valid transactions + * @when statefully validating these transactions + * @then all of them will appear in verified proposal @and errors will be empty + */ +TEST_F(Validator, AllTxsValid) { + auto tx = TestTransactionBuilder() + .creatorAccountId("doge@master") + .createdTime(iroha::time::now()) + .quorum(1) + .createAsset("doge", "coin", 1) + .build(); + auto proposal = + TestProposalBuilder() + .createdTime(iroha::time::now()) + .height(3) + .transactions( + std::vector{tx, tx, tx}) + .build(); + + EXPECT_CALL(*temp_wsv_mock, apply(_, _)) + .WillRepeatedly(Return(iroha::expected::Value({}))); + + auto verified_proposal_and_errors = sfv->validate(proposal, *temp_wsv_mock); + ASSERT_EQ(verified_proposal_and_errors.first->transactions().size(), 3); + ASSERT_TRUE(verified_proposal_and_errors.second.empty()); +} + +/** + * @given several valid and a couple of invalid transactions + * @when statefully validating these transactions + * @then valid transactions will appear in verified proposal @and invalid ones + * will appear in errors + */ +TEST_F(Validator, SomeTxsFail) { + auto valid_tx = TestTransactionBuilder() + .creatorAccountId("doge@master") + .createdTime(iroha::time::now()) + .quorum(1) + .createAsset("doge", "coin", 1) + .build(); + auto invalid_tx = TestTransactionBuilder() + .creatorAccountId("doge@master") + .createdTime(iroha::time::now()) + .quorum(1) + .createAsset("cate", "coin", 1) + .build(); + auto proposal = + TestProposalBuilder() + .createdTime(iroha::time::now()) + .height(3) + .transactions(std::vector{ + valid_tx, invalid_tx, valid_tx}) + .build(); + + EXPECT_CALL(*temp_wsv_mock, apply(Eq(ByRef(invalid_tx)), _)) + .WillOnce(Return(iroha::expected::Error({}))); + EXPECT_CALL(*temp_wsv_mock, apply(Eq(ByRef(valid_tx)), _)) + .WillRepeatedly(Return(iroha::expected::Value({}))); + + auto verified_proposal_and_errors = sfv->validate(proposal, *temp_wsv_mock); + ASSERT_EQ(verified_proposal_and_errors.first->transactions().size(), 2); + ASSERT_EQ(verified_proposal_and_errors.second.size(), 1); +} + +/** + * @given two atomic batches @and one ordered @and several single transactions + * @when failing one of the atomic batched @and transaction from ordered batch + * @and transaction from single group + * @then verified proposal will contain transactions from non-failed atomic + * batch, non-failed part of ordered batch, non-failed transactions from single + * group @and errors will contain exactly one error (for failed atomic batch) + */ +TEST_F(Validator, Batches) { + auto single_tx = TestTransactionBuilder() + .creatorAccountId("doge@master") + .createdTime(iroha::time::now()) + .quorum(1) + .createAsset("doge", "coin", 1) + .build(); + auto success_atomic_batch = + createBatch(std::vector{"creator@d1", "creator@d2"}, + shared_model::interface::types::BatchType::ATOMIC); + auto failed_atomic_batch = + createBatch(std::vector{"creator@d3", "creator@d4"}, + shared_model::interface::types::BatchType::ATOMIC); + auto ordered_batch = + createBatch(std::vector{"creator@d5", "creator@d6"}, + shared_model::interface::types::BatchType::ORDERED); + + std::vector txs{single_tx, + ordered_batch[0], + ordered_batch[1], + failed_atomic_batch[0], + failed_atomic_batch[1], + success_atomic_batch[0], + success_atomic_batch[1]}; + + auto proposal = TestProposalBuilder() + .createdTime(iroha::time::now()) + .height(1) + .transactions(txs) + .build(); + + // calls to create savepoints, one per each atomic batch + EXPECT_CALL(*temp_wsv_mock, + createSavepoint("batch_" + failed_atomic_batch[0].hash().hex())) + .WillOnce(Return( + ByMove(std::make_unique< + iroha::ametsuchi::MockTemporaryWsvSavepointWrapper>()))); + EXPECT_CALL(*temp_wsv_mock, + createSavepoint("batch_" + success_atomic_batch[0].hash().hex())) + .WillOnce(Return( + ByMove(std::make_unique< + iroha::ametsuchi::MockTemporaryWsvSavepointWrapper>()))); + + // calls to validate transactions, one per each transaction except those, + // which are in failed atomic batch - there only calls before the failed + // transaction are needed + EXPECT_CALL(*temp_wsv_mock, apply(Eq(ByRef(txs[0])), _)) + .WillOnce(Return(iroha::expected::Value({}))); + EXPECT_CALL(*temp_wsv_mock, apply(Eq(ByRef(txs[1])), _)) + .WillOnce(Return(iroha::expected::Value({}))); + EXPECT_CALL(*temp_wsv_mock, apply(Eq(ByRef(txs[2])), _)) + .WillOnce(Return(iroha::expected::Value({}))); + EXPECT_CALL(*temp_wsv_mock, apply(Eq(ByRef(txs[3])), _)) + .WillOnce(Return(iroha::expected::Error({}))); + EXPECT_CALL(*temp_wsv_mock, apply(Eq(ByRef(txs[5])), _)) + .WillOnce(Return(iroha::expected::Value({}))); + EXPECT_CALL(*temp_wsv_mock, apply(Eq(ByRef(txs[6])), _)) + .WillOnce(Return(iroha::expected::Value({}))); + + auto verified_proposal_and_errors = sfv->validate(proposal, *temp_wsv_mock); + ASSERT_EQ(verified_proposal_and_errors.first->transactions().size(), 5); + ASSERT_EQ(verified_proposal_and_errors.second.size(), 1); +} From c2c0edd41af97b188f3354e1e1fea4c387956b7c Mon Sep 17 00:00:00 2001 From: Kitsu Date: Fri, 13 Jul 2018 22:05:14 +0300 Subject: [PATCH 46/97] Increase presision lifetime in insertAsset (#1562) Signed-off-by: Kitsu --- irohad/ametsuchi/impl/postgres_wsv_command.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/irohad/ametsuchi/impl/postgres_wsv_command.cpp b/irohad/ametsuchi/impl/postgres_wsv_command.cpp index 525dbaca3f..0cbdd8c4a0 100644 --- a/irohad/ametsuchi/impl/postgres_wsv_command.cpp +++ b/irohad/ametsuchi/impl/postgres_wsv_command.cpp @@ -204,12 +204,13 @@ namespace iroha { WsvCommandResult PostgresWsvCommand::insertAsset( const shared_model::interface::Asset &asset) { + auto precision = asset.precision(); soci::statement st = sql_.prepare << "INSERT INTO asset(asset_id, domain_id, \"precision\", data) " "VALUES (:id, :domain_id, :precision, NULL)"; st.exchange(soci::use(asset.assetId())); st.exchange(soci::use(asset.domainId())); - st.exchange(soci::use(asset.precision())); + st.exchange(soci::use(precision)); auto msg = [&] { return (boost::format("failed to insert asset, asset id: '%s', " From b2f60603a037128b87ec70961118847f5a663376 Mon Sep 17 00:00:00 2001 From: Igor Egorov Date: Mon, 16 Jul 2018 11:44:44 +0300 Subject: [PATCH 47/97] Add Permissions Docs Compiler (#1501) Signed-off-by: Igor Egorov --- docs/README.md | 2 + docs/permissions_compiler/compiler.py | 123 ++++ docs/permissions_compiler/consts.py | 58 ++ docs/permissions_compiler/glossary.py | 27 + docs/permissions_compiler/rst.py | 186 ++++++ docs/source/Makefile | 4 + docs/source/core_concepts/glossary.rst | 2 + docs/source/maintenance/permissions.rst | 755 +++++++++++++++++++++-- docs/source/permissions/introduction.txt | 13 + docs/source/permissions/matrix.csv | 59 ++ 10 files changed, 1174 insertions(+), 55 deletions(-) create mode 100644 docs/permissions_compiler/compiler.py create mode 100644 docs/permissions_compiler/consts.py create mode 100644 docs/permissions_compiler/glossary.py create mode 100644 docs/permissions_compiler/rst.py create mode 100644 docs/source/permissions/introduction.txt create mode 100644 docs/source/permissions/matrix.csv diff --git a/docs/README.md b/docs/README.md index a57bff90f7..566a9e0e76 100644 --- a/docs/README.md +++ b/docs/README.md @@ -5,6 +5,7 @@ The purpose of this documentation is to convey design and architecture aspects o ## Principles * modify only the contents of `image_assets` and `source` folders + * do `make permissions` in case of any change inside `source/permissions` folder * if new section/file is added — it should be placed in the list of contents * if any new principle is added — it should be discussed in GitHub issues first, as an improvement proposal * reference images or assets using GitHub download link or an external service, since our localized docs can't use relative path to image assets or other resources @@ -22,6 +23,7 @@ The purpose of this documentation is to convey design and architecture aspects o ### Build steps 1. `cd docs/source` +1. `make permissions` 1. `make html` After that, the documentation is generated in html format in `_build` folder. You can check another formats on Sphinx website or just by typing `make`. In order to get familiar with the syntax of theme used in the project please go to their [demo website](https://sphinx-rtd-theme.readthedocs.io/en/latest/demo/demo.html) diff --git a/docs/permissions_compiler/compiler.py b/docs/permissions_compiler/compiler.py new file mode 100644 index 0000000000..63cebb9510 --- /dev/null +++ b/docs/permissions_compiler/compiler.py @@ -0,0 +1,123 @@ +#!/usr/bin/env python3 + +# +# Copyright Soramitsu Co., Ltd. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +# + +import csv +import rst +import glossary +import consts +import os.path +import os + +perm_type = category = perm = "" + +result = ['.. DON\'T MODIFY THE CONTENTS MANUALLY.', + ' THIS IS AUTOGENERATED FILE.', + ' All the changes will be lost in case of manual modification.', + ' For modification change the files in docs/source/permissions.' + ' Then do "make permissions" before "make html".', ''] +result.extend(rst.header("Permissions", 0)) +glossary_links = glossary.titles_to_links(glossary.read_titles()) + +with open('permissions/introduction.txt') as intro: + intro_lines = intro.readlines() + for line in intro_lines: + result.append(line.strip()) + +result.append('') + +with open('permissions/matrix.csv', newline='') as csvfile: + reader = csv.DictReader(csvfile) + for row in reader: + grantable = False + + if row['Type'] != perm_type: + perm_type = row['Type'] + result.extend( + rst.header("{}-related permissions".format(row['Type']), 1)) + + if row['Category'] != category: + category = row['Category'] + result.extend(rst.header(category, 2)) + + if row['Permission'] != perm: + perm = row['Permission'] + result.extend(rst.header(perm, 3)) + + if row['Grantable'].strip() == 'TRUE': + grantable = True + hint = rst.hint('This is a grantable permission.') + result.extend(hint) + + descr_lines = row['Description'].split('\n') + descr_lines = list(map(lambda x: x.strip(), descr_lines)) + descr_lines.append('') + + if row['Additional Information'].strip(): + ainfo = row['Additional Information'].split('\n') + ainfo = list(map(lambda x: x.strip(), ainfo)) + ainfo.append('') + descr_lines.extend(ainfo) + + links_dict = dict(glossary_links) + descr_lines_linkified = [] + for line in descr_lines: + tokens = line.split(' ') + tokens_linkified = [] + skip = False + for token in tokens: + if skip: + tokens_linkified.append(token) + if '`' in token: + if not skip: + tokens_linkified.append(token) + if token.count('`') % 2 == 1: + skip = not skip + continue + tokens_linkified.append(rst.linkify(token, links_dict, pop=True)) + descr_lines_linkified.append(' '.join(tokens_linkified)) + + result.extend(descr_lines_linkified) + + if row['Note'].strip(): + result.extend(rst.note(row['Note'])) + + if row['Related Command'].strip(): + rc = row['Related Command'].split('\n') + rc = map(lambda x: x.strip(), rc) + rc = filter(lambda x: len(x) > 0, rc) + rc = list(rc) + links = [] + related = 'Related API method' + ('s' if len(rc) > 1 else '') + for link in rc: + try: + links.append(rst.reference(link)) + except Exception: + if (row['Related Command'].strip().lower().startswith('tbd')): + links.append('To be done') + else: + print(row['Related Command']) + raise + result.append('| {}: {}'.format(related, ', '.join(links))) + + result.extend(rst.alias(perm, grantable)) + + if row['Example'].strip(): + result.extend(rst.example(row['Example'])) + + result.extend(rst.excerpt(perm)) + result.extend(rst.header('Supplementary Sources', 1)) + commons_path = [os.path.pardir] * 2 + ['example', 'python', 'permissions', 'commons.py'] + result.extend(rst.listing(commons_path, 'commons.py')) + consts_path = [os.path.pardir, 'permissions_compiler', 'consts.py'] + result.extend(rst.listing(consts_path, 'consts.py')) + +with open('maintenance/permissions.rst', 'w') as output: + content = "\n".join(result) + output.write(content) + output.flush() + +print('done') diff --git a/docs/permissions_compiler/consts.py b/docs/permissions_compiler/consts.py new file mode 100644 index 0000000000..bbe998eb20 --- /dev/null +++ b/docs/permissions_compiler/consts.py @@ -0,0 +1,58 @@ +# +# Copyright Soramitsu Co., Ltd. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +# + +grantable = { + 'can_add_my_signatory': 'kAddMySignatory', + 'can_remove_my_signatory': 'kRemoveMySignatory', + 'can_set_my_account_detail': 'kSetMyAccountDetail', + 'can_set_my_quorum': 'kSetMyQuorum', + 'can_transfer_my_assets': 'kTransferMyAssets' +} + +role = { + 'can_add_asset_qty': 'kAddAssetQty', + 'can_add_peer': 'kAddPeer', + 'can_add_signatory': 'kAddSignatory', + 'can_append_role': 'kAppendRole', + 'can_create_account': 'kCreateAccount', + 'can_create_asset': 'kCreateAsset', + 'can_create_domain': 'kCreateDomain', + 'can_create_role': 'kCreateRole', + 'can_detach_role': 'kDetachRole', + 'can_get_all_acc_ast': 'kGetAllAccAst', + 'can_get_all_acc_ast_txs': 'kGetAllAccAstTxs', + 'can_get_all_acc_detail': 'kGetAllAccDetail', + 'can_get_all_acc_txs': 'kGetAllAccTxs', + 'can_get_all_accounts': 'kGetAllAccounts', + 'can_get_all_signatories': 'kGetAllSignatories', + 'can_get_all_txs': 'kGetAllTxs', + 'can_get_blocks': 'kGetBlocks', + 'can_get_domain_acc_ast': 'kGetDomainAccAst', + 'can_get_domain_acc_ast_txs': 'kGetDomainAccAstTxs', + 'can_get_domain_acc_detail': 'kGetDomainAccDetail', + 'can_get_domain_acc_txs': 'kGetDomainAccTxs', + 'can_get_domain_accounts': 'kGetDomainAccounts', + 'can_get_domain_signatories': 'kGetDomainSignatories', + 'can_get_my_acc_ast': 'kGetMyAccAst', + 'can_get_my_acc_ast_txs': 'kGetMyAccAstTxs', + 'can_get_my_acc_detail': 'kGetMyAccDetail', + 'can_get_my_acc_txs': 'kGetMyAccTxs', + 'can_get_my_account': 'kGetMyAccount', + 'can_get_my_signatories': 'kGetMySignatories', + 'can_get_my_txs': 'kGetMyTxs', + 'can_get_roles': 'kGetRoles', + 'can_grant_can_add_my_signatory': 'kAddMySignatory', + 'can_grant_can_remove_my_signatory': 'kRemoveMySignatory', + 'can_grant_can_set_my_account_detail': 'kSetMyAccountDetail', + 'can_grant_can_set_my_quorum': 'kSetMyQuorum', + 'can_grant_can_transfer_my_assets': 'kTransferMyAssets', + 'can_read_assets': 'kReadAssets', + 'can_receive': 'kReceive', + 'can_remove_signatory': 'kRemoveSignatory', + 'can_set_detail': 'kSetDetail', + 'can_set_quorum': 'kSetQuorum', + 'can_subtract_asset_qty': 'kSubtractAssetQty', + 'can_transfer': 'kTransfer' +} diff --git a/docs/permissions_compiler/glossary.py b/docs/permissions_compiler/glossary.py new file mode 100644 index 0000000000..7bfdce243b --- /dev/null +++ b/docs/permissions_compiler/glossary.py @@ -0,0 +1,27 @@ +# +# Copyright Soramitsu Co., Ltd. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +# + +def read_titles(level_char='='): + headings = [] + with open('core_concepts/glossary.rst') as gfile: + lines = gfile.readlines() + prevline = '' + prevlen = 0 + for line in lines: + line = line.strip() + if len(line) == prevlen and line == (level_char * prevlen) and line: + headings.append(prevline) + prevlen = len(line) + prevline = line + return headings + + +def titles_to_links(titles): + d = {} + for title in titles: + title = title.strip().lower() + anchor = '../core_concepts/glossary.html#' + title.replace(' ', '-') + d[title] = anchor + return d diff --git a/docs/permissions_compiler/rst.py b/docs/permissions_compiler/rst.py new file mode 100644 index 0000000000..ebcfaceba9 --- /dev/null +++ b/docs/permissions_compiler/rst.py @@ -0,0 +1,186 @@ +# +# Copyright Soramitsu Co., Ltd. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +# + +import consts +import os.path + +levels = ['*', '=', '-', '^', '"'] + + +def header_char(level): + level = int(level) % len(levels) + return levels[level] + + +def header_overline(level): + level = int(level) % len(levels) + return level == 0 + + +def header(title, level): + title = title.strip() + decoration = header_char(level) * len(title) + result = [] + if header_overline(level): + result.append(decoration) + result.append(title) + result.append(decoration) + result.append("") + return result + + +def hint(text): + text = text.strip() + return ['.. Hint:: {}'.format(text), ''] + + +def note(text): + text = text.strip() + return ['.. Note:: {}'.format(text), ''] + + +def reference(link): + if (not '#' in link) or (not '/api' in link): + raise Exception('Badly formed input link') + link = link.strip() + title = link.split('#')[1].replace('-', ' ').title() + path = ''.join(link.split("/api")[1:]) + path = '../api{}'.format(path) + link = '`{} <{}>`__'.format(title, path) + return link + + +def linkify(term, dictionary, pop=False): + if not term: + return term + clean = term.strip().lower() + before = after = '' + if clean[0] in ['"', "'", '(']: + before = clean[0] + clean = clean[1:] + term = term[1:] + if clean[-1] in ['.', ',', '!', '?', ':', ';', '"', "'", ')']: + after = clean[-1] + clean = clean[:-1] + term = term[:-1] + result = before + term + after + found = False + if clean in dictionary: + found = True + result = '{}`{} <{}>`__{}'.format(before, term, dictionary[clean], after) + if pop: + dictionary.pop(clean, None) + if not found and clean.endswith('s'): + clean_singular = clean[:-1] + if clean_singular in dictionary: + result = '{}`{} <{}>`__{}'.format(before, term, dictionary[clean_singular], after) + if pop: + dictionary.pop(clean_singular, None) + return result + + +def alias(permission, grantable): + """ + + :param permission: string permsssion name + :param grantable: boolean True if grantable + :return: list of strings + """ + langs = {'Python': '_', 'Java': '.'} + lines = [] + perm = permission.lower().strip() + for lang, delimiter in sorted(langs.items()): + lines.append('| Usage in {} bindings: ``{}{}{}``'.format( + lang, + 'Grantable' if grantable else 'Role', + delimiter, + consts.grantable[perm] if grantable else consts.role[perm] + )) + lines.append('|') + lines.append('') + return lines + + +def listing(compile_time_path, caption='', lines_range=None, lang='python'): + """ + Generates listing lines to include + :param compile_time_path: list of os.path primitives + :param lines_range: tuple of two ints + :return: rst lines + """ + path = os.path.join(*compile_time_path) + if not os.path.isfile(path): + print('File not found: {} (compile time path)'.format(path)) + return [] + + docs_time_path = [os.path.pardir] + list(compile_time_path) + path = os.path.join(*docs_time_path) + result = [ + '.. literalinclude:: {}'.format(path), + ' :language: {}'.format(lang), + ' :linenos:' + ] + if caption: + result.append(' :caption: {}'.format(caption)) + if lines_range: + result.append(' :lines: {}-{}'.format(lines_range[0], lines_range[1])) + result.append('') + return result + + +def excerpt_boundaries(path): + """ + + :param path: path to python example + :return: tuple with two numbers + """ + lines = [] + with open(path) as source: + lines = source.readlines() + begin = 1 + end = len(lines) + for index, line in enumerate(lines): + if begin == 1 and ('commons.user' in line or 'admin' in line): + begin = index + 1 + break + prints = spaces = False + for index, line in reversed(list(enumerate(lines))): + if index < end: + if not prints and line.startswith('print'): + prints = True + elif prints and not line.strip(): + spaces = True + elif prints and spaces and line.strip(): + end = index + 1 + break + + return (begin, end) + + +def excerpt(permission): + """ + Renders source file listing + :param permission: name of permission to list, used as a part of filename + :return: rst lines + """ + compile_time_path = [os.path.pardir, os.path.pardir, 'example', 'python', 'permissions', '{}.py'.format(permission)] + path = os.path.join(*compile_time_path) + result = [] + if not os.path.isfile(path): + print(path) + return result + window = excerpt_boundaries(path) + result.extend(listing(compile_time_path, lines_range=window)) + return result + + +def example(text): + """Renders example description contents""" + result = ['**Example**', ''] + lines = text.split('\n') + for line in lines: + result.append('| {}'.format(line)) + result.extend(['|', '']) + return result diff --git a/docs/source/Makefile b/docs/source/Makefile index e4e5e26c1f..1bc166d403 100644 --- a/docs/source/Makefile +++ b/docs/source/Makefile @@ -13,6 +13,10 @@ help: .PHONY: help Makefile +.PHONY: permissions +permissions: + python3 ../permissions_compiler/compiler.py + # Catch-all target: route all unknown targets to Sphinx using the new # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). %: Makefile diff --git a/docs/source/core_concepts/glossary.rst b/docs/source/core_concepts/glossary.rst index ded2f463b2..359ea0f9d7 100644 --- a/docs/source/core_concepts/glossary.rst +++ b/docs/source/core_concepts/glossary.rst @@ -145,6 +145,8 @@ A named rule that gives the privilege to perform a command. Permission **cannot** be granted to an `account <#account>`__ directly, instead, an account has roles, which are the collection of permissions. +`List of Iroha permissions <../maintenance/permissions.html>`_. + Grantable Permission -------------------- diff --git a/docs/source/maintenance/permissions.rst b/docs/source/maintenance/permissions.rst index c85d3b421a..cf060e794c 100644 --- a/docs/source/maintenance/permissions.rst +++ b/docs/source/maintenance/permissions.rst @@ -1,3 +1,8 @@ +.. DON'T MODIFY THE CONTENTS MANUALLY. + THIS IS AUTOGENERATED FILE. + All the changes will be lost in case of manual modification. + For modification change the files in docs/source/permissions. Then do "make permissions" before "make html". + *********** Permissions *********** @@ -13,6 +18,8 @@ This might be done at the initial step of system deployment — in genesis block or later when Iroha network is up and running, roles can be changed (if there is a role that can do that :) This section will help you to understand permissions and give you an idea of how to create roles including certain permissions. +Each permission is provided with an example written in Python that demonstrates the way of transaction or query creation, +which require specific permission. Every example uses *commons.py* module, which listing is available at `Supplementary Sources`_ section. Command-related permissions =========================== @@ -25,16 +32,44 @@ can_create_account Allows creating new `accounts <../core_concepts/glossary.html#account>`__. -Related API method: `Create Account <../api/commands.html#create-account>`__ +| Related API method: `Create Account <../api/commands.html#create-account>`__ +| Usage in Java bindings: ``Role.kCreateAccount`` +| Usage in Python bindings: ``Role_kCreateAccount`` +| + +**Example** + +| Admin creates domain "test" that contains only can_create_account permission and Alice account in that domain. Alice can create Bob account. +| + +.. literalinclude:: ../../../example/python/permissions/can_create_account.py + :language: python + :linenos: + :lines: 9-42 can_set_detail ^^^^^^^^^^^^^^ Allows setting `account <../core_concepts/glossary.html#account>`__ detail. -.. Note:: Due to a known `issue `__, permission does not take an effect. +The `permission <../core_concepts/glossary.html#permission>`__ allows setting details to other accounts. Another way to set detail without can_set_detail permission is to grant `can_set_my_account_detail`_ permission to someone. In order to grant, `transaction <../core_concepts/glossary.html#transaction>`__ creator should have `can_grant_can_set_my_account_detail`_ permission. + +.. Note:: Transaction creator can always set detail for own account even without that permission. -Related API method: `Set Account Detail <../api/commands.html#set-account-detail>`__ +| Related API method: `Set Account Detail <../api/commands.html#set-account-detail>`__ +| Usage in Java bindings: ``Role.kSetDetail`` +| Usage in Python bindings: ``Role_kSetDetail`` +| + +**Example** + +| Admin creates domain "test" that contains only can_set_detail permission and Alice account in that domain. Alice can set detail for Admin account. +| + +.. literalinclude:: ../../../example/python/permissions/can_set_detail.py + :language: python + :linenos: + :lines: 9-39 can_set_my_account_detail ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -43,9 +78,22 @@ can_set_my_account_detail `Permission <../core_concepts/glossary.html#permission>`__ that allows a specified `account <../core_concepts/glossary.html#account>`__ to set details for the another specified account. -See the example (to be done) for the usage details. +.. Note:: To grant the permission an account should already have a role with `can_grant_can_set_my_account_detail`_ permission. + +| Related API method: `Set Account Detail <../api/commands.html#set-account-detail>`__ +| Usage in Java bindings: ``Grantable.kSetMyAccountDetail`` +| Usage in Python bindings: ``Grantable_kSetMyAccountDetail`` +| -Related API method: `Set Account Detail <../api/commands.html#set-account-detail>`__ +**Example** + +| Admin creates domain "test" that contains only can_grant_can_set_my_account_detail permission and two accounts for Alice and Bob in that domain. Alice grants to Bob can_set_my_account_detail permission. Bob can set detail for Alice account. +| + +.. literalinclude:: ../../../example/python/permissions/can_set_my_account_detail.py + :language: python + :linenos: + :lines: 9-54 Asset ----- @@ -55,14 +103,40 @@ can_create_asset Allows creating new `assets <../core_concepts/glossary.html#asset>`__. -Related API method: `Create Asset <../api/commands.html#create-asset>`__ +| Related API method: `Create Asset <../api/commands.html#create-asset>`__ +| Usage in Java bindings: ``Role.kCreateAsset`` +| Usage in Python bindings: ``Role_kCreateAsset`` +| + +**Example** + +| Admin creates domain "test" that contains only can_create_asset permission and Alice account in that domain. Alice can create new assets. +| + +.. literalinclude:: ../../../example/python/permissions/can_create_asset.py + :language: python + :linenos: + :lines: 9-39 can_receive ^^^^^^^^^^^ Allows `account <../core_concepts/glossary.html#account>`__ receive `assets <../core_concepts/glossary.html#asset>`__. -Related API method: `Transfer Asset <../api/commands.html#transfer-asset>`__ +| Related API method: `Transfer Asset <../api/commands.html#transfer-asset>`__ +| Usage in Java bindings: ``Role.kReceive`` +| Usage in Python bindings: ``Role_kReceive`` +| + +**Example** + +| Admin creates domain "test" that contains can_receive and can_transfer permissions and two accounts for Alice and Bob. Admin creates "coin" asset, adds some quantity of it and transfers the asset to Alice. Alice can transfer assets to Bob (Alice has can_transfer permission and Bob has can_receive permission). +| + +.. literalinclude:: ../../../example/python/permissions/can_receive.py + :language: python + :linenos: + :lines: 9-47 can_transfer ^^^^^^^^^^^^ @@ -73,7 +147,15 @@ You can transfer an asset from one `domain <../core_concepts/glossary.html#domai .. Note:: Destination account should have `can_receive`_ permission. -Related API method: `Transfer Asset <../api/commands.html#transfer-asset>`__ +| Related API method: `Transfer Asset <../api/commands.html#transfer-asset>`__ +| Usage in Java bindings: ``Role.kTransfer`` +| Usage in Python bindings: ``Role_kTransfer`` +| + +.. literalinclude:: ../../../example/python/permissions/can_transfer.py + :language: python + :linenos: + :lines: 1-11 can_transfer_my_assets ^^^^^^^^^^^^^^^^^^^^^^ @@ -84,7 +166,20 @@ can_transfer_my_assets See the example (to be done) for the usage details. -Related API method: `Transfer Asset <../api/commands.html#transfer-asset>`__ +| Related API method: `Transfer Asset <../api/commands.html#transfer-asset>`__ +| Usage in Java bindings: ``Grantable.kTransferMyAssets`` +| Usage in Python bindings: ``Grantable_kTransferMyAssets`` +| + +**Example** + +| Admin creates domain "test" that contains can_grant_can_transfer_my_assets, can_receive, can_transfer permissions and two accounts for Alice and Bob in that domain. Admin issues some amount of "coin" asset and transfers it to Alice. Alice grants to Bob can_transfer_my_assets permission. Bob can transfer Alice's assets to any account that has can_receive permission, for example, to Admin. +| + +.. literalinclude:: ../../../example/python/permissions/can_transfer_my_assets.py + :language: python + :linenos: + :lines: 9-59 Asset Quantity -------------- @@ -96,7 +191,20 @@ Allows issuing `assets <../core_concepts/glossary.html#asset>`__. The corresponding `command <../core_concepts/glossary.html#command>`__ can be executed only for an `account <../core_concepts/glossary.html#account>`__ of `transaction <../core_concepts/glossary.html#transaction>`__ creator and only if that account has a `role <../core_concepts/glossary.html#role>`__ with the `permission <../core_concepts/glossary.html#permission>`__. -Related API method: `Add Asset Quantity <../api/commands.html#add-asset-quantity>`__ +| Related API method: `Add Asset Quantity <../api/commands.html#add-asset-quantity>`__ +| Usage in Java bindings: ``Role.kAddAssetQty`` +| Usage in Python bindings: ``Role_kAddAssetQty`` +| + +**Example** + +| Admin creates domain "test" that contains only can_add_asset_qty permission and Alice account in that domain. Admin craetes "coin" asset. Alice can add to own account any amount of any asset (e.g. "coin" asset). +| + +.. literalinclude:: ../../../example/python/permissions/can_add_asset_qty.py + :language: python + :linenos: + :lines: 9-40 can_subtract_asset_qty ^^^^^^^^^^^^^^^^^^^^^^ @@ -105,7 +213,20 @@ Allows burning `assets <../core_concepts/glossary.html#asset>`__. The corresponding `command <../core_concepts/glossary.html#command>`__ can be executed only for an `account <../core_concepts/glossary.html#account>`__ of `transaction <../core_concepts/glossary.html#transaction>`__ creator and only if that account has a `role <../core_concepts/glossary.html#role>`__ with the `permission <../core_concepts/glossary.html#permission>`__. -Related API method: `Subtract Asset Quantity <../api/commands.html#subtract-asset-quantity>`__ +| Related API method: `Subtract Asset Quantity <../api/commands.html#subtract-asset-quantity>`__ +| Usage in Java bindings: ``Role.kSubtractAssetQty`` +| Usage in Python bindings: ``Role_kSubtractAssetQty`` +| + +**Example** + +| Admin creates domain "test" that contains only can_subtract_asset_qty permission and Alice account in that domain. Admin issues some amount of "coin" asset and transfers some amount of "coin" asset to Alice. Alice can burn any amount of "coin" assets. +| + +.. literalinclude:: ../../../example/python/permissions/can_subtract_asset_qty.py + :language: python + :linenos: + :lines: 9-42 Domain ------ @@ -115,7 +236,20 @@ can_create_domain Allows creating new `domains <../core_concepts/glossary.html#domain>`__ within the system. -Related API method: `Create Domain <../api/commands.html#create-domain>`__ +| Related API method: `Create Domain <../api/commands.html#create-domain>`__ +| Usage in Java bindings: ``Role.kCreateDomain`` +| Usage in Python bindings: ``Role_kCreateDomain`` +| + +**Example** + +| Admin creates domain that contains only can_create_domain permission and Alice account in that domain. Alice can create new domains. +| + +.. literalinclude:: ../../../example/python/permissions/can_create_domain.py + :language: python + :linenos: + :lines: 9-40 Grant ----- @@ -125,35 +259,100 @@ can_grant_can_add_my_signatory Allows `role <../core_concepts/glossary.html#role>`__ owners grant `can_add_my_signatory`_ `permission <../core_concepts/glossary.html#permission>`__. -Related API method: `Grant Permission <../api/commands.html#grant-permission>`__ +| Related API method: `Grant Permission <../api/commands.html#grant-permission>`__ +| Usage in Java bindings: ``Role.kAddMySignatory`` +| Usage in Python bindings: ``Role_kAddMySignatory`` +| + +**Example** + +| Admin creates domain that contains only can_grant_can_add_my_signatory permission and two accounts for Alice and Bob in that domain. Alice can grant to Bob can_add_my_signatory permission. +| + +.. literalinclude:: ../../../example/python/permissions/can_grant_can_add_my_signatory.py + :language: python + :linenos: + :lines: 9-41 can_grant_can_remove_my_signatory ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Allows `role <../core_concepts/glossary.html#role>`__ owners grant `can_remove_my_signatory`_ `permission <../core_concepts/glossary.html#permission>`__. -Related API method: `Grant Permission <../api/commands.html#grant-permission>`__ +| Related API method: `Grant Permission <../api/commands.html#grant-permission>`__ +| Usage in Java bindings: ``Role.kRemoveMySignatory`` +| Usage in Python bindings: ``Role_kRemoveMySignatory`` +| + +**Example** + +| Admin creates domain that contains only can_grant_can_remove_my_signatory permission and two accounts for Alice and Bob in that domain. Alice can grant to Bob can_remove_my_signatory permission. +| + +.. literalinclude:: ../../../example/python/permissions/can_grant_can_remove_my_signatory.py + :language: python + :linenos: + :lines: 9-41 can_grant_can_set_my_account_detail ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Allows `role <../core_concepts/glossary.html#role>`__ owners grant `can_set_my_account_detail`_ `permission <../core_concepts/glossary.html#permission>`__. -Related API method: `Grant Permission <../api/commands.html#grant-permission>`__ +| Related API method: `Grant Permission <../api/commands.html#grant-permission>`__ +| Usage in Java bindings: ``Role.kSetMyAccountDetail`` +| Usage in Python bindings: ``Role_kSetMyAccountDetail`` +| + +**Example** + +| Admin creates domain that contains only can_grant_can_set_my_account_detail permission and two accounts for Alice and Bob in that domain. Alice can grant to Bob can_set_my_account_detail permission. +| + +.. literalinclude:: ../../../example/python/permissions/can_grant_can_set_my_account_detail.py + :language: python + :linenos: + :lines: 9-41 can_grant_can_set_my_quorum ^^^^^^^^^^^^^^^^^^^^^^^^^^^ Allows `role <../core_concepts/glossary.html#role>`__ owners grant `can_set_my_quorum`_ `permission <../core_concepts/glossary.html#permission>`__. -Related API method: `Grant Permission <../api/commands.html#grant-permission>`__ +| Related API method: `Grant Permission <../api/commands.html#grant-permission>`__ +| Usage in Java bindings: ``Role.kSetMyQuorum`` +| Usage in Python bindings: ``Role_kSetMyQuorum`` +| + +**Example** + +| Admin creates domain that contains only can_grant_can_set_my_quorum permission and two accounts for Alice and Bob in that domain. Alice can grant to Bob can_set_my_quorum permission. +| + +.. literalinclude:: ../../../example/python/permissions/can_grant_can_set_my_quorum.py + :language: python + :linenos: + :lines: 9-41 can_grant_can_transfer_my_assets ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Allows `role <../core_concepts/glossary.html#role>`__ owners grant `can_transfer_my_assets`_ `permission <../core_concepts/glossary.html#permission>`__. -Related API method: `Grant Permission <../api/commands.html#grant-permission>`__ +| Related API method: `Grant Permission <../api/commands.html#grant-permission>`__ +| Usage in Java bindings: ``Role.kTransferMyAssets`` +| Usage in Python bindings: ``Role_kTransferMyAssets`` +| + +**Example** + +| Admin creates domain that contains only can_grant_can_transfer_my_assets permission and two accounts for Alice and Bob in that domain. Alice can grant to Bob can_transfer_my_assets permission. +| + +.. literalinclude:: ../../../example/python/permissions/can_grant_can_transfer_my_assets.py + :language: python + :linenos: + :lines: 9-48 Peer ---- @@ -165,7 +364,20 @@ Allows adding `peers <../core_concepts/glossary.html#peer>`__ to the network. A new peer will be a valid participant in the next `consensus <../core_concepts/glossary.html#consensus>`__ round after an agreement on `transaction <../core_concepts/glossary.html#transaction>`__ containing "addPeer" `command <../core_concepts/glossary.html#command>`__. -Related API method: `Add Peer <../api/commands.html#add-peer>`__ +| Related API method: `Add Peer <../api/commands.html#add-peer>`__ +| Usage in Java bindings: ``Role.kAddPeer`` +| Usage in Python bindings: ``Role_kAddPeer`` +| + +**Example** + +| Admin creates domain that contains only can_add_peer permission and Alice account in that domain. Alice can add new peers. +| + +.. literalinclude:: ../../../example/python/permissions/can_add_peer.py + :language: python + :linenos: + :lines: 9-40 Role ---- @@ -175,21 +387,67 @@ can_append_role Allows appending `roles <../core_concepts/glossary.html#role>`__ to another `account <../core_concepts/glossary.html#account>`__. -Related API method: `Append Role <../api/commands.html#append-role>`__ +You can append only that role that has lesser or the same set of privileges as `transaction <../core_concepts/glossary.html#transaction>`__ creator. + +| Related API method: `Append Role <../api/commands.html#append-role>`__ +| Usage in Java bindings: ``Role.kAppendRole`` +| Usage in Python bindings: ``Role_kAppendRole`` +| + +**Example** + +| Admin creates domian that contains can_append_role and can_add_peer permissions and two accounts for Alice and Bob in that domain. Admin creates the second role that contains only can_add_peer permission. Alice can append role to Bob. +| + +.. literalinclude:: ../../../example/python/permissions/can_append_role.py + :language: python + :linenos: + :lines: 9-48 can_create_role ^^^^^^^^^^^^^^^ Allows creating a new `role <../core_concepts/glossary.html#role>`__ within a system. -Related API method: `Create Role <../api/commands.html#create-role>`__ +Possible set of `permissions <../core_concepts/glossary.html#permission>`__ for a new role is limited to those permissions that `transaction <../core_concepts/glossary.html#transaction>`__ creator has. + + +| Related API method: `Create Role <../api/commands.html#create-role>`__ +| Usage in Java bindings: ``Role.kCreateRole`` +| Usage in Python bindings: ``Role_kCreateRole`` +| + +**Example** + +| Admin creates domain that contains only can_create_role permission and Alice account in that domain. Alice can create new roles. +| + +.. literalinclude:: ../../../example/python/permissions/can_create_role.py + :language: python + :linenos: + :lines: 9-44 can_detach_role ^^^^^^^^^^^^^^^ Allows revoking a `role <../core_concepts/glossary.html#role>`__ from a user. -Related API method: `Detach Role <../api/commands.html#detach-role>`__ +.. Note:: Due to a known issue the permission allows to detach any role without limitations https://soramitsu.atlassian.net/browse/IR-1468 + +| Related API method: `Detach Role <../api/commands.html#detach-role>`__ +| Usage in Java bindings: ``Role.kDetachRole`` +| Usage in Python bindings: ``Role_kDetachRole`` +| + +**Example** + +| Admin creates domain that contains only can_detach_role permission and creates Alice account in that domain. Admin has two roles test_role and admin_role. Alice can detach test_role from Admin account. +| + +.. literalinclude:: ../../../example/python/permissions/can_detach_role.py + :language: python + :linenos: + :lines: 9-39 Signatory --------- @@ -201,7 +459,20 @@ can_add_my_signatory `Permission <../core_concepts/glossary.html#permission>`__ that allows a specified `account <../core_concepts/glossary.html#account>`__ to add an extra public key to the another specified account. -Related API method: `Add Signatory <../api/commands.html#add-signatory>`__ +| Related API method: `Add Signatory <../api/commands.html#add-signatory>`__ +| Usage in Java bindings: ``Grantable.kAddMySignatory`` +| Usage in Python bindings: ``Grantable_kAddMySignatory`` +| + +**Example** + +| Admin creates domain that contains only can_grant_can_add_my_signatory permission and two accounts for Alice and Bob in that domain. Alice can grant to Bob can_add_my_signatory permission. Bob can add an extra key to Alice account. +| + +.. literalinclude:: ../../../example/python/permissions/can_add_my_signatory.py + :language: python + :linenos: + :lines: 9-53 can_add_signatory ^^^^^^^^^^^^^^^^^ @@ -210,7 +481,20 @@ Allows linking additional public keys to `account <../core_concepts/glossary.htm The corresponding `command <../core_concepts/glossary.html#command>`__ can be executed only for an account of `transaction <../core_concepts/glossary.html#transaction>`__ creator and only if that account has a `role <../core_concepts/glossary.html#role>`__ with the `permission <../core_concepts/glossary.html#permission>`__. -Related API method: `Add Signatory <../api/commands.html#add-signatory>`__ +| Related API method: `Add Signatory <../api/commands.html#add-signatory>`__ +| Usage in Java bindings: ``Role.kAddSignatory`` +| Usage in Python bindings: ``Role_kAddSignatory`` +| + +**Example** + +| Admin creates domain that contains only can_add_signatory permission and Alice account in that domain. Alice can add to own account additional keys. +| + +.. literalinclude:: ../../../example/python/permissions/can_add_signatory.py + :language: python + :linenos: + :lines: 9-40 can_remove_my_signatory ^^^^^^^^^^^^^^^^^^^^^^^ @@ -221,7 +505,20 @@ can_remove_my_signatory See the example (to be done) for the usage details. -Related API method: `Remove Signatory <../api/commands.html#remove-signatory>`__ +| Related API method: `Remove Signatory <../api/commands.html#remove-signatory>`__ +| Usage in Java bindings: ``Grantable.kRemoveMySignatory`` +| Usage in Python bindings: ``Grantable_kRemoveMySignatory`` +| + +**Example** + +| Admin creates domain that contains can_add_signatory and can_grant_can_remove_my_signatory permissions and two accounts for Alice and Bob. Alice grants can_remove_my_signatory permission to Bob and adds additional key to own account. Bob can remove one of Alice's keys. +| + +.. literalinclude:: ../../../example/python/permissions/can_remove_my_signatory.py + :language: python + :linenos: + :lines: 9-57 can_remove_signatory ^^^^^^^^^^^^^^^^^^^^ @@ -230,7 +527,20 @@ Allows unlinking additional public keys from an `account <../core_concepts/gloss The corresponding `command <../core_concepts/glossary.html#command>`__ can be executed only for an account of `transaction <../core_concepts/glossary.html#transaction>`__ creator and only if that account has a `role <../core_concepts/glossary.html#role>`__ with the `permission <../core_concepts/glossary.html#permission>`__. -Related API method: `Remove Signatory <../api/commands.html#remove-signatory>`__ +| Related API method: `Remove Signatory <../api/commands.html#remove-signatory>`__ +| Usage in Java bindings: ``Role.kRemoveSignatory`` +| Usage in Python bindings: ``Role_kRemoveSignatory`` +| + +**Example** + +| Admin creates domian that contains can_remove_signatory permission and Alice account in that domain. Admin adds an extra key to Alice account. Alice can remove one of the keys. +| + +.. literalinclude:: ../../../example/python/permissions/can_remove_signatory.py + :language: python + :linenos: + :lines: 9-41 can_set_my_quorum ^^^^^^^^^^^^^^^^^ @@ -239,9 +549,22 @@ can_set_my_quorum `Permission <../core_concepts/glossary.html#permission>`__ that allows a specified `account <../core_concepts/glossary.html#account>`__ to set `quorum <../core_concepts/glossary.html#quorum>`__ for the another specified account. -See the example (to be done) for the usage details. +Account should have greater or equal amount of keys than quorum. + +| Related API method: `Set Account Quorum <../api/commands.html#set-account-quorum>`__ +| Usage in Java bindings: ``Grantable.kSetMyQuorum`` +| Usage in Python bindings: ``Grantable_kSetMyQuorum`` +| + +**Example** -Related API method: `Set Account Quorum <../api/commands.html#set-account-quorum>`__ +| Admin creates domain that contains can_grant_can_set_my_quorum and can_add_signatory permissions and create two accounts for Alice and Bob in that domain. Alice grants to Bob can_set_my_qourum permission and adds an extra key to account. Bob can set quorum for Alice. +| + +.. literalinclude:: ../../../example/python/permissions/can_set_my_quorum.py + :language: python + :linenos: + :lines: 9-57 can_set_quorum ^^^^^^^^^^^^^^ @@ -250,7 +573,20 @@ Allows setting `quorum <../core_concepts/glossary.html#quorum>`__. At least the same number (or more) of public keys should be already linked to an `account <../core_concepts/glossary.html#account>`__. -Related API method: `Set Account Quorum <../api/commands.html#set-account-quorum>`__ +| Related API method: `Set Account Quorum <../api/commands.html#set-account-quorum>`__ +| Usage in Java bindings: ``Role.kSetQuorum`` +| Usage in Python bindings: ``Role_kSetQuorum`` +| + +**Example** + +| Admin creates domain that contains only can_set_quorum permission and creates Alice account in that domain. Admin adds an extra key for Alice account. Alice can set quorum equals two. +| + +.. literalinclude:: ../../../example/python/permissions/can_set_quorum.py + :language: python + :linenos: + :lines: 9-42 Query-related permissions ========================= @@ -263,7 +599,20 @@ can_get_all_acc_detail Allows getting all the details set to any `account <../core_concepts/glossary.html#account>`__ within the system. -Related API method: To be done +| Related API method: To be done +| Usage in Java bindings: ``Role.kGetAllAccDetail`` +| Usage in Python bindings: ``Role_kGetAllAccDetail`` +| + +**Example** + +| Admin creates Alice account in a diffrerent domain that has only can_get_all_acc_detail permission. Alice can access details set to Admin account. +| + +.. literalinclude:: ../../../example/python/permissions/can_get_all_acc_detail.py + :language: python + :linenos: + :lines: 9-40 can_get_all_accounts ^^^^^^^^^^^^^^^^^^^^ @@ -274,14 +623,40 @@ With this `permission <../core_concepts/glossary.html#permission>`__, `query <.. All the details (set by the account owner or owners of other accounts) will be returned. -Related API method: `Get Account <../api/queries.html#get-account>`__ +| Related API method: `Get Account <../api/queries.html#get-account>`__ +| Usage in Java bindings: ``Role.kGetAllAccounts`` +| Usage in Python bindings: ``Role_kGetAllAccounts`` +| + +**Example** + +| Admin creates Alice account in a different domain that has only can_get_all_accounts permission. Alice can access account information of Admin. +| + +.. literalinclude:: ../../../example/python/permissions/can_get_all_accounts.py + :language: python + :linenos: + :lines: 9-40 can_get_domain_acc_detail ^^^^^^^^^^^^^^^^^^^^^^^^^ Allows getting all the details set to any `account <../core_concepts/glossary.html#account>`__ within the same `domain <../core_concepts/glossary.html#domain>`__ as a domain of `query <../core_concepts/glossary.html#query>`__ creator account. -Related API method: To be done +| Related API method: To be done +| Usage in Java bindings: ``Role.kGetDomainAccDetail`` +| Usage in Python bindings: ``Role_kGetDomainAccDetail`` +| + +**Example** + +| Admin creates Alice account in the same domain that has only can_get_domain_acc_detail permission. Alice can get details set to Admin account. +| + +.. literalinclude:: ../../../example/python/permissions/can_get_domain_acc_detail.py + :language: python + :linenos: + :lines: 9-39 can_get_domain_accounts ^^^^^^^^^^^^^^^^^^^^^^^ @@ -292,14 +667,40 @@ With this `permission <../core_concepts/glossary.html#permission>`__, `query <.. All the details (set by the account owner or owners of other accounts) will be returned. -Related API method: `Get Account <../api/queries.html#get-account>`__ +| Related API method: `Get Account <../api/queries.html#get-account>`__ +| Usage in Java bindings: ``Role.kGetDomainAccounts`` +| Usage in Python bindings: ``Role_kGetDomainAccounts`` +| + +**Example** + +| Admin creates Alice account in the same domain that has only can_get_domain_accounts. Alice can access account information of Admin. +| + +.. literalinclude:: ../../../example/python/permissions/can_get_domain_accounts.py + :language: python + :linenos: + :lines: 9-39 can_get_my_acc_detail ^^^^^^^^^^^^^^^^^^^^^ Allows getting all the details set to the `account <../core_concepts/glossary.html#account>`__ of `query <../core_concepts/glossary.html#query>`__ creator. -Related API method: To be done +| Related API method: To be done +| Usage in Java bindings: ``Role.kGetMyAccDetail`` +| Usage in Python bindings: ``Role_kGetMyAccDetail`` +| + +**Example** + +| Admin creates Alice account in the domain that has only can_get_my_acc_detail permission. Alice can get details set to own account. +| + +.. literalinclude:: ../../../example/python/permissions/can_get_my_acc_detail.py + :language: python + :linenos: + :lines: 9-39 can_get_my_account ^^^^^^^^^^^^^^^^^^ @@ -310,7 +711,20 @@ With this `permission <../core_concepts/glossary.html#permission>`__, `query <.. All the details (set by the account owner or owners of other accounts) will be returned. -Related API method: `Get Account <../api/queries.html#get-account>`__ +| Related API method: `Get Account <../api/queries.html#get-account>`__ +| Usage in Java bindings: ``Role.kGetMyAccount`` +| Usage in Python bindings: ``Role_kGetMyAccount`` +| + +**Example** + +| Admin creates Alice account in the domain that has only can_get_my_account permission. Alice can access own account information. +| + +.. literalinclude:: ../../../example/python/permissions/can_get_my_account.py + :language: python + :linenos: + :lines: 9-39 Account Asset ------------- @@ -318,23 +732,68 @@ Account Asset can_get_all_acc_ast ^^^^^^^^^^^^^^^^^^^ -Allows getting a balance of specified `asset <../core_concepts/glossary.html#asset>`__ on any `account <../core_concepts/glossary.html#account>`__ within the system. +Allows getting a balance of `assets <../core_concepts/glossary.html#asset>`__ on any `account <../core_concepts/glossary.html#account>`__ within the system. + +`Query <../core_concepts/glossary.html#query>`__ response will contain information about all the assets that ever been assigned to an account. + +| Related API method: `Get Account Assets <../api/queries.html#get-account-assets>`__ +| Usage in Java bindings: ``Role.kGetAllAccAst`` +| Usage in Python bindings: ``Role_kGetAllAccAst`` +| + +**Example** -Related API method: `Get Account Assets <../api/queries.html#get-account-assets>`__ +| Admin creates Alice account in a different domain that has only can_get_all_acc_ast permission. Alice can access assets balance on Admin account. +| + +.. literalinclude:: ../../../example/python/permissions/can_get_all_acc_ast.py + :language: python + :linenos: + :lines: 9-40 can_get_domain_acc_ast ^^^^^^^^^^^^^^^^^^^^^^ Allows getting a balance of specified `asset <../core_concepts/glossary.html#asset>`__ on any `account <../core_concepts/glossary.html#account>`__ within the same `domain <../core_concepts/glossary.html#domain>`__ as a domain of `query <../core_concepts/glossary.html#query>`__ creator account. -Related API method: `Get Account Assets <../api/queries.html#get-account-assets>`__ +Query response will contain information about all the assets that ever been assigned to an account. + +| Related API method: `Get Account Assets <../api/queries.html#get-account-assets>`__ +| Usage in Java bindings: ``Role.kGetDomainAccAst`` +| Usage in Python bindings: ``Role_kGetDomainAccAst`` +| + +**Example** + +| Admin creates Alice account in the same domain that has only can_get_domain_acc_ast permission. Alice can access assets balance on Admin account. +| + +.. literalinclude:: ../../../example/python/permissions/can_get_domain_acc_ast.py + :language: python + :linenos: + :lines: 9-39 can_get_my_acc_ast ^^^^^^^^^^^^^^^^^^ Allows getting a balance of specified `asset <../core_concepts/glossary.html#asset>`__ on `account <../core_concepts/glossary.html#account>`__ of `query <../core_concepts/glossary.html#query>`__ creator. -Related API method: `Get Account Assets <../api/queries.html#get-account-assets>`__ +Query response will contain information about all the assets that ever been assigned to an account. + +| Related API method: `Get Account Assets <../api/queries.html#get-account-assets>`__ +| Usage in Java bindings: ``Role.kGetMyAccAst`` +| Usage in Python bindings: ``Role_kGetMyAccAst`` +| + +**Example** + +| Admin creates Alice account in a domain that has only can_get_my_acc_ast permission. Alice can access assets balance on own account. +| + +.. literalinclude:: ../../../example/python/permissions/can_get_my_acc_ast.py + :language: python + :linenos: + :lines: 9-39 Account Asset Transaction ------------------------- @@ -344,27 +803,66 @@ can_get_all_acc_ast_txs Allows getting `transactions <../core_concepts/glossary.html#transaction>`__ associated with a specified `asset <../core_concepts/glossary.html#asset>`__ and any `account <../core_concepts/glossary.html#account>`__ within the system. -.. Note:: Incoming asset transfers will also appear in the command output. +.. Note:: Incoming asset transfers will also appear in the query response. + +| Related API method: `Get Account Asset Transactions <../api/queries.html#get-account-asset-transactions>`__ +| Usage in Java bindings: ``Role.kGetAllAccAstTxs`` +| Usage in Python bindings: ``Role_kGetAllAccAstTxs`` +| -Related API method: `Get Account Asset Transactions <../api/queries.html#get-account-asset-transactions>`__ +**Example** + +| Admin creates Alice account in a different domain that has can_get_all_acc_ast_txs, can_receive and can_transfer permissions. Admin issues some amount of coins and transfers them to Alice. Alice can query all transactions related to coins and Admin account. +| + +.. literalinclude:: ../../../example/python/permissions/can_get_all_acc_ast_txs.py + :language: python + :linenos: + :lines: 9-47 can_get_domain_acc_ast_txs ^^^^^^^^^^^^^^^^^^^^^^^^^^ Allows getting `transactions <../core_concepts/glossary.html#transaction>`__ associated with a specified `asset <../core_concepts/glossary.html#asset>`__ and an `account <../core_concepts/glossary.html#account>`__ from the same `domain <../core_concepts/glossary.html#domain>`__ as `query <../core_concepts/glossary.html#query>`__ creator. -.. Note:: Incoming asset transfers will also appear in the command output. +.. Note:: Incoming asset transfers will also appear in the query response. + +| Related API method: `Get Account Asset Transactions <../api/queries.html#get-account-asset-transactions>`__ +| Usage in Java bindings: ``Role.kGetDomainAccAstTxs`` +| Usage in Python bindings: ``Role_kGetDomainAccAstTxs`` +| + +**Example** -Related API method: `Get Account Asset Transactions <../api/queries.html#get-account-asset-transactions>`__ +| Admin creates Alice in the same domain that has only can_get_domain_acc_ast_txs permission. Admin issues some amount of coins and transfers them to Alice. Alice can query all transactions related to coins and Admin account. +| + +.. literalinclude:: ../../../example/python/permissions/can_get_domain_acc_ast_txs.py + :language: python + :linenos: + :lines: 9-42 can_get_my_acc_ast_txs ^^^^^^^^^^^^^^^^^^^^^^ Allows getting `transactions <../core_concepts/glossary.html#transaction>`__ associated with the `account <../core_concepts/glossary.html#account>`__ of `query <../core_concepts/glossary.html#query>`__ creator and specified `asset <../core_concepts/glossary.html#asset>`__. -.. Note:: Incoming asset transfers will also appear in the command output. +.. Note:: Incoming asset transfers will also appear in the query response. + +| Related API method: `Get Account Asset Transactions <../api/queries.html#get-account-asset-transactions>`__ +| Usage in Java bindings: ``Role.kGetMyAccAstTxs`` +| Usage in Python bindings: ``Role_kGetMyAccAstTxs`` +| + +**Example** -Related API method: `Get Account Asset Transactions <../api/queries.html#get-account-asset-transactions>`__ +| Admin creates Alice account in a domain that has only can_get_my_acc_ast_txs permission. Admin issues some amount of coins and transfers them to Alice. Alice can query all transactions related to coins and own account. +| + +.. literalinclude:: ../../../example/python/permissions/can_get_my_acc_ast_txs.py + :language: python + :linenos: + :lines: 9-42 Account Transaction ------------------- @@ -376,7 +874,20 @@ Allows getting all `transactions <../core_concepts/glossary.html#transaction>`__ .. Note:: Incoming asset transfer inside a transaction would NOT lead to an appearance of the transaction in the command output. -Related API method: `Get Account Asset Transactions <../api/queries.html#get-account-asset-transactions>`__ +| Related API method: `Get Account Asset Transactions <../api/queries.html#get-account-asset-transactions>`__ +| Usage in Java bindings: ``Role.kGetAllAccTxs`` +| Usage in Python bindings: ``Role_kGetAllAccTxs`` +| + +**Example** + +| Admin creates Alice account in a different domain that has only can_get_all_acc_txs permiison. Alice can request all the transactions issues by Admin. +| + +.. literalinclude:: ../../../example/python/permissions/can_get_all_acc_txs.py + :language: python + :linenos: + :lines: 9-40 can_get_domain_acc_txs ^^^^^^^^^^^^^^^^^^^^^^ @@ -385,7 +896,20 @@ Allows getting all `transactions <../core_concepts/glossary.html#transaction>`__ .. Note:: Incoming asset transfer inside a transaction would NOT lead to an appearance of the transaction in the command output. -Related API method: `Get Account Asset Transactions <../api/queries.html#get-account-asset-transactions>`__ +| Related API method: `Get Account Asset Transactions <../api/queries.html#get-account-asset-transactions>`__ +| Usage in Java bindings: ``Role.kGetDomainAccTxs`` +| Usage in Python bindings: ``Role_kGetDomainAccTxs`` +| + +**Example** + +| Admin creates Alice account in the same domain that has only can_get_domain_acc_txs permission. Alice can request all the transactions issued by Admin. +| + +.. literalinclude:: ../../../example/python/permissions/can_get_domain_acc_txs.py + :language: python + :linenos: + :lines: 9-39 can_get_my_acc_txs ^^^^^^^^^^^^^^^^^^ @@ -394,7 +918,20 @@ Allows getting all `transactions <../core_concepts/glossary.html#transaction>`__ .. Note:: Incoming asset transfer inside a transaction would NOT lead to an appearance of the transaction in the command output. -Related API method: `Get Account Asset Transactions <../api/queries.html#get-account-asset-transactions>`__ +| Related API method: `Get Account Asset Transactions <../api/queries.html#get-account-asset-transactions>`__ +| Usage in Java bindings: ``Role.kGetMyAccTxs`` +| Usage in Python bindings: ``Role_kGetMyAccTxs`` +| + +**Example** + +| Admin creates Alice account in a domain that has only can_get_my_acc_txs permission. Alice can get all transactions issued by own account. +| + +.. literalinclude:: ../../../example/python/permissions/can_get_my_acc_txs.py + :language: python + :linenos: + :lines: 9-39 Asset ----- @@ -404,7 +941,20 @@ can_read_assets Allows getting information about `asset <../core_concepts/glossary.html#asset>`__ precision. -Related API method: `Get Asset Info <../api/queries.html#get-asset-info>`__ +| Related API method: `Get Asset Info <../api/queries.html#get-asset-info>`__ +| Usage in Java bindings: ``Role.kReadAssets`` +| Usage in Python bindings: ``Role_kReadAssets`` +| + +**Example** + +| Admin creates Alice account in a domain that has can_read_assets permissions. Alice can query information about any asset. +| + +.. literalinclude:: ../../../example/python/permissions/can_read_assets.py + :language: python + :linenos: + :lines: 9-40 Block Stream ------------ @@ -412,7 +962,11 @@ Block Stream can_get_blocks ^^^^^^^^^^^^^^ -Not implemented now. Allows subscription to the stream of accepted `blocks <../core_concepts/glossary.html#block>`__. +Allows subscription to the stream of accepted `blocks <../core_concepts/glossary.html#block>`__. + +| Usage in Java bindings: ``Role.kGetBlocks`` +| Usage in Python bindings: ``Role_kGetBlocks`` +| Role ---- @@ -423,7 +977,20 @@ can_get_roles Allows getting a list of `roles <../core_concepts/glossary.html#role>`__ within the system. Allows getting a list of `permissions <../core_concepts/glossary.html#permission>`__ associated with a role. -Related API methods: `Get Roles <../api/queries.html#get-roles>`__, `Get Role Permissions <../api/queries.html#get-role-permissions>`__ +| Related API methods: `Get Roles <../api/queries.html#get-roles>`__, `Get Role Permissions <../api/queries.html#get-role-permissions>`__ +| Usage in Java bindings: ``Role.kGetRoles`` +| Usage in Python bindings: ``Role_kGetRoles`` +| + +**Example** + +| Admin creates Alice account in a domain that has can_get_roles permission. Alice can query list of all existing roles. Alice can query list of permissions contained in any role. +| + +.. literalinclude:: ../../../example/python/permissions/can_get_roles.py + :language: python + :linenos: + :lines: 9-52 Signatory --------- @@ -433,21 +1000,60 @@ can_get_all_signatories Allows getting a list of public keys linked to an `account <../core_concepts/glossary.html#account>`__ within the system. -Related API method: `Get Signatories <../api/queries.html#get-signatories>`__ +| Related API method: `Get Signatories <../api/queries.html#get-signatories>`__ +| Usage in Java bindings: ``Role.kGetAllSignatories`` +| Usage in Python bindings: ``Role_kGetAllSignatories`` +| + +**Example** + +| Admin creates Alice account in a different domain that has only can_get_all_signatories permission. Alice can query a list of public keys related to Admin account. +| + +.. literalinclude:: ../../../example/python/permissions/can_get_all_signatories.py + :language: python + :linenos: + :lines: 9-40 can_get_domain_signatories ^^^^^^^^^^^^^^^^^^^^^^^^^^ Allows getting a list of public keys of any `account <../core_concepts/glossary.html#account>`__ within the same `domain <../core_concepts/glossary.html#domain>`__ as the domain of `query <../core_concepts/glossary.html#query>`__ creator account. -Related API method: `Get Signatories <../api/queries.html#get-signatories>`__ +| Related API method: `Get Signatories <../api/queries.html#get-signatories>`__ +| Usage in Java bindings: ``Role.kGetDomainSignatories`` +| Usage in Python bindings: ``Role_kGetDomainSignatories`` +| + +**Example** + +| Admin creates Alice account in the same domain that has only can_get_domain_signatories permission. Alice can query a list of public keys related to Admin account. +| + +.. literalinclude:: ../../../example/python/permissions/can_get_domain_signatories.py + :language: python + :linenos: + :lines: 9-39 can_get_my_signatories ^^^^^^^^^^^^^^^^^^^^^^ Allows getting a list of public keys of `query <../core_concepts/glossary.html#query>`__ creator `account <../core_concepts/glossary.html#account>`__. -Related API method: `Get Signatories <../api/queries.html#get-signatories>`__ +| Related API method: `Get Signatories <../api/queries.html#get-signatories>`__ +| Usage in Java bindings: ``Role.kGetMySignatories`` +| Usage in Python bindings: ``Role_kGetMySignatories`` +| + +**Example** + +| Admin creates Alice account in a domain that has only can_get_my_signatories permission. Alice can query a list of public keys related to own account. +| + +.. literalinclude:: ../../../example/python/permissions/can_get_my_signatories.py + :language: python + :linenos: + :lines: 9-39 Transaction ----------- @@ -457,11 +1063,50 @@ can_get_all_txs Allows getting any `transaction <../core_concepts/glossary.html#transaction>`__ by hash. -Related API method: `Get Transactions <../api/queries.html#get-transactions>`__ +| Related API method: `Get Transactions <../api/queries.html#get-transactions>`__ +| Usage in Java bindings: ``Role.kGetAllTxs`` +| Usage in Python bindings: ``Role_kGetAllTxs`` +| + +**Example** + +| Admin issues several transactions and creates Alice account in a different domain that has only can_get_all_txs permission. Alice (knowing transactions hashes) can query transactions issued by Admin Account. +| + +.. literalinclude:: ../../../example/python/permissions/can_get_all_txs.py + :language: python + :linenos: + :lines: 9-72 can_get_my_txs ^^^^^^^^^^^^^^ Allows getting `transaction <../core_concepts/glossary.html#transaction>`__ (that was issued by `query <../core_concepts/glossary.html#query>`__ creator) by hash. -Related API method: `Get Transactions <../api/queries.html#get-transactions>`__ +| Related API method: `Get Transactions <../api/queries.html#get-transactions>`__ +| Usage in Java bindings: ``Role.kGetMyTxs`` +| Usage in Python bindings: ``Role_kGetMyTxs`` +| + +**Example** + +| Admin creates Alice account in a different domain. Alice (knowing transactions hashes) issues several transactions. Alice can query own transactions. +| + +.. literalinclude:: ../../../example/python/permissions/can_get_my_txs.py + :language: python + :linenos: + :lines: 9-76 + +Supplementary Sources +===================== + +.. literalinclude:: ../../../example/python/permissions/commons.py + :language: python + :linenos: + :caption: commons.py + +.. literalinclude:: ../../permissions_compiler/consts.py + :language: python + :linenos: + :caption: consts.py diff --git a/docs/source/permissions/introduction.txt b/docs/source/permissions/introduction.txt new file mode 100644 index 0000000000..a12e0c7ad7 --- /dev/null +++ b/docs/source/permissions/introduction.txt @@ -0,0 +1,13 @@ +Hyperledger Iroha uses a role-based access control system to limit actions of its users. +This system greatly helps to implement use cases involving user groups having different access levels — +ranging from the weak users, who can't even receive asset transfer to the super-users. +The beauty of our permission system is that you don't have to have a super-user +in your Iroha setup or use all the possible permissions: you can create segregated and lightweight roles. + +Maintenance of the system involves setting up roles and permissions, that are included in the roles. +This might be done at the initial step of system deployment — in genesis block, +or later when Iroha network is up and running, roles can be changed (if there is a role that can do that :) + +This section will help you to understand permissions and give you an idea of how to create roles including certain permissions. +Each permission is provided with an example written in Python that demonstrates the way of transaction or query creation, +which require specific permission. Every example uses *commons.py* module, which listing is available at `Supplementary Sources`_ section. diff --git a/docs/source/permissions/matrix.csv b/docs/source/permissions/matrix.csv new file mode 100644 index 0000000000..2d31545a89 --- /dev/null +++ b/docs/source/permissions/matrix.csv @@ -0,0 +1,59 @@ +Type,Category,Permission,Grantable,Only for tx creator,Description,Additional Information,Note,Related Command,Example +Command,Account,can_create_account,FALSE,FALSE,Allows creating new accounts.,,,http://iroha.readthedocs.io/en/latest/api/commands.html#create-account,"Admin creates domain ""test"" that contains only can_create_account permission and Alice account in that domain. Alice can create Bob account." +Command,Account,can_set_detail,FALSE,TRUE,Allows setting account detail.,"The permission allows setting details to other accounts. Another way to set detail without can_set_detail permission is to grant `can_set_my_account_detail`_ permission to someone. In order to grant, transaction creator should have `can_grant_can_set_my_account_detail`_ permission.",Transaction creator can always set detail for own account even without that permission.,http://iroha.readthedocs.io/en/latest/api/commands.html#set-account-detail,"Admin creates domain ""test"" that contains only can_set_detail permission and Alice account in that domain. Alice can set detail for Admin account." +Command,Account,can_set_my_account_detail,TRUE,,Permission that allows a specified account to set details for the another specified account., ,To grant the permission an account should already have a role with `can_grant_can_set_my_account_detail`_ permission.,http://iroha.readthedocs.io/en/latest/api/commands.html#set-account-detail,"Admin creates domain ""test"" that contains only can_grant_can_set_my_account_detail permission and two accounts for Alice and Bob in that domain. Alice grants to Bob can_set_my_account_detail permission. Bob can set detail for Alice account." +Command,Asset,can_create_asset,FALSE,FALSE,Allows creating new assets.,,,http://iroha.readthedocs.io/en/latest/api/commands.html#create-asset,"Admin creates domain ""test"" that contains only can_create_asset permission and Alice account in that domain. Alice can create new assets." +Command,Asset,can_receive,FALSE,,Allows account receive assets.,,,http://iroha.readthedocs.io/en/latest/api/commands.html#transfer-asset,"Admin creates domain ""test"" that contains can_receive and can_transfer permissions and two accounts for Alice and Bob. Admin creates ""coin"" asset, adds some quantity of it and transfers the asset to Alice. Alice can transfer assets to Bob (Alice has can_transfer permission and Bob has can_receive permission)." +Command,Asset,can_transfer,FALSE,FALSE,Allows sending assets from an account of transaction creator.,"You can transfer an asset from one domain to another, even if the other domain does not have an asset with the same name.",Destination account should have `can_receive`_ permission.,http://iroha.readthedocs.io/en/latest/api/commands.html#transfer-asset, +Command,Asset,can_transfer_my_assets,TRUE,,Permission that allows a specified account to transfer assets of another specified account.,See the example (to be done) for the usage details., ,http://iroha.readthedocs.io/en/latest/api/commands.html#transfer-asset,"Admin creates domain ""test"" that contains can_grant_can_transfer_my_assets, can_receive, can_transfer permissions and two accounts for Alice and Bob in that domain. Admin issues some amount of ""coin"" asset and transfers it to Alice. Alice grants to Bob can_transfer_my_assets permission. Bob can transfer Alice's assets to any account that has can_receive permission, for example, to Admin." +Command,Asset Quantity,can_add_asset_qty,FALSE,TRUE,Allows issuing assets.,The corresponding command can be executed only for an account of transaction creator and only if that account has a role with the permission.,,http://iroha.readthedocs.io/en/latest/api/commands.html#add-asset-quantity,"Admin creates domain ""test"" that contains only can_add_asset_qty permission and Alice account in that domain. Admin craetes ""coin"" asset. Alice can add to own account any amount of any asset (e.g. ""coin"" asset)." +Command,Asset Quantity,can_subtract_asset_qty,FALSE,TRUE,Allows burning assets.,The corresponding command can be executed only for an account of transaction creator and only if that account has a role with the permission.,,http://iroha.readthedocs.io/en/latest/api/commands.html#subtract-asset-quantity,"Admin creates domain ""test"" that contains only can_subtract_asset_qty permission and Alice account in that domain. Admin issues some amount of ""coin"" asset and transfers some amount of ""coin"" asset to Alice. Alice can burn any amount of ""coin"" assets." +Command,Domain,can_create_domain,FALSE,,Allows creating new domains within the system.,,,http://iroha.readthedocs.io/en/latest/api/commands.html#create-domain,Admin creates domain that contains only can_create_domain permission and Alice account in that domain. Alice can create new domains. +Command,Grant,can_grant_can_add_my_signatory,FALSE,,Allows role owners grant `can_add_my_signatory`_ permission.,,,http://iroha.readthedocs.io/en/latest/api/commands.html#grant-permission,Admin creates domain that contains only can_grant_can_add_my_signatory permission and two accounts for Alice and Bob in that domain. Alice can grant to Bob can_add_my_signatory permission. +Command,Grant,can_grant_can_remove_my_signatory,FALSE,,Allows role owners grant `can_remove_my_signatory`_ permission.,,,http://iroha.readthedocs.io/en/latest/api/commands.html#grant-permission,Admin creates domain that contains only can_grant_can_remove_my_signatory permission and two accounts for Alice and Bob in that domain. Alice can grant to Bob can_remove_my_signatory permission. +Command,Grant,can_grant_can_set_my_account_detail,FALSE,,Allows role owners grant `can_set_my_account_detail`_ permission.,,,http://iroha.readthedocs.io/en/latest/api/commands.html#grant-permission,Admin creates domain that contains only can_grant_can_set_my_account_detail permission and two accounts for Alice and Bob in that domain. Alice can grant to Bob can_set_my_account_detail permission. +Command,Grant,can_grant_can_set_my_quorum,FALSE,,Allows role owners grant `can_set_my_quorum`_ permission.,,,http://iroha.readthedocs.io/en/latest/api/commands.html#grant-permission,Admin creates domain that contains only can_grant_can_set_my_quorum permission and two accounts for Alice and Bob in that domain. Alice can grant to Bob can_set_my_quorum permission. +Command,Grant,can_grant_can_transfer_my_assets,FALSE,,Allows role owners grant `can_transfer_my_assets`_ permission.,,,http://iroha.readthedocs.io/en/latest/api/commands.html#grant-permission,Admin creates domain that contains only can_grant_can_transfer_my_assets permission and two accounts for Alice and Bob in that domain. Alice can grant to Bob can_transfer_my_assets permission. +Command,Peer,can_add_peer,FALSE,,Allows adding peers to the network.,"A new peer will be a valid participant in the next consensus round after an agreement on transaction containing ""addPeer"" command.",,http://iroha.readthedocs.io/en/latest/api/commands.html#add-peer,Admin creates domain that contains only can_add_peer permission and Alice account in that domain. Alice can add new peers. +Command,Role,can_append_role,FALSE,,Allows appending roles to another account.,You can append only that role that has lesser or the same set of privileges as transaction creator.,,http://iroha.readthedocs.io/en/latest/api/commands.html#append-role,Admin creates domian that contains can_append_role and can_add_peer permissions and two accounts for Alice and Bob in that domain. Admin creates the second role that contains only can_add_peer permission. Alice can append role to Bob. +Command,Role,can_create_role,FALSE,,Allows creating a new role within a system.,"Possible set of permissions for a new role is limited to those permissions that transaction creator has. +",,http://iroha.readthedocs.io/en/latest/api/commands.html#create-role,Admin creates domain that contains only can_create_role permission and Alice account in that domain. Alice can create new roles. +Command,Role,can_detach_role,FALSE,,Allows revoking a role from a user.,,Due to a known issue the permission allows to detach any role without limitations https://soramitsu.atlassian.net/browse/IR-1468,http://iroha.readthedocs.io/en/latest/api/commands.html#detach-role,Admin creates domain that contains only can_detach_role permission and creates Alice account in that domain. Admin has two roles test_role and admin_role. Alice can detach test_role from Admin account. +Command,Signatory,can_add_my_signatory,TRUE,,Permission that allows a specified account to add an extra public key to the another specified account.,,,http://iroha.readthedocs.io/en/latest/api/commands.html#add-signatory,Admin creates domain that contains only can_grant_can_add_my_signatory permission and two accounts for Alice and Bob in that domain. Alice can grant to Bob can_add_my_signatory permission. Bob can add an extra key to Alice account. +Command,Signatory,can_add_signatory,FALSE,FALSE,Allows linking additional public keys to account.,The corresponding command can be executed only for an account of transaction creator and only if that account has a role with the permission.,,http://iroha.readthedocs.io/en/latest/api/commands.html#add-signatory,Admin creates domain that contains only can_add_signatory permission and Alice account in that domain. Alice can add to own account additional keys. +Command,Signatory,can_remove_my_signatory,TRUE,,Permission that allows a specified account remove public key from the another specified account.,See the example (to be done) for the usage details.,,http://iroha.readthedocs.io/en/latest/api/commands.html#remove-signatory,Admin creates domain that contains can_add_signatory and can_grant_can_remove_my_signatory permissions and two accounts for Alice and Bob. Alice grants can_remove_my_signatory permission to Bob and adds additional key to own account. Bob can remove one of Alice's keys. +Command,Signatory,can_remove_signatory,FALSE,FALSE,Allows unlinking additional public keys from an account.,The corresponding command can be executed only for an account of transaction creator and only if that account has a role with the permission.,,http://iroha.readthedocs.io/en/latest/api/commands.html#remove-signatory,Admin creates domian that contains can_remove_signatory permission and Alice account in that domain. Admin adds an extra key to Alice account. Alice can remove one of the keys. +Command,Signatory,can_set_my_quorum,TRUE,,Permission that allows a specified account to set quorum for the another specified account.,Account should have greater or equal amount of keys than quorum. ,,http://iroha.readthedocs.io/en/latest/api/commands.html#set-account-quorum,Admin creates domain that contains can_grant_can_set_my_quorum and can_add_signatory permissions and create two accounts for Alice and Bob in that domain. Alice grants to Bob can_set_my_qourum permission and adds an extra key to account. Bob can set quorum for Alice. +Command,Signatory,can_set_quorum,FALSE,FALSE,Allows setting quorum.,At least the same number (or more) of public keys should be already linked to an account.,,http://iroha.readthedocs.io/en/latest/api/commands.html#set-account-quorum,Admin creates domain that contains only can_set_quorum permission and creates Alice account in that domain. Admin adds an extra key for Alice account. Alice can set quorum equals two. +Query,Account,can_get_all_acc_detail,FALSE,,Allows getting all the details set to any account within the system.,,,tbd GetAccountDetail,Admin creates Alice account in a diffrerent domain that has only can_get_all_acc_detail permission. Alice can access details set to Admin account. +Query,Account,can_get_all_accounts,FALSE,,"Allows getting account information: quorum and all the details related to the account. + +With this permission, query creator can get information about any account within a system.",All the details (set by the account owner or owners of other accounts) will be returned.,,http://iroha.readthedocs.io/en/latest/api/queries.html#get-account,Admin creates Alice account in a different domain that has only can_get_all_accounts permission. Alice can access account information of Admin. +Query,Account,can_get_domain_acc_detail,FALSE,,Allows getting all the details set to any account within the same domain as a domain of query creator account.,,,tbd GetAccountDetail,Admin creates Alice account in the same domain that has only can_get_domain_acc_detail permission. Alice can get details set to Admin account. +Query,Account,can_get_domain_accounts,FALSE,,"Allows getting account information: quorum and all the details related to the account. + +With this permission, query creator can get information only about accounts from the same domain.",All the details (set by the account owner or owners of other accounts) will be returned.,,http://iroha.readthedocs.io/en/latest/api/queries.html#get-account,Admin creates Alice account in the same domain that has only can_get_domain_accounts. Alice can access account information of Admin. +Query,Account,can_get_my_acc_detail,FALSE,,Allows getting all the details set to the account of query creator.,,,tbd GetAccountDetail,Admin creates Alice account in the domain that has only can_get_my_acc_detail permission. Alice can get details set to own account. +Query,Account,can_get_my_account,FALSE,,"Allows getting account information: quorum and all the details related to the account. + +With this permission, query creator can get information only about own account.",All the details (set by the account owner or owners of other accounts) will be returned.,,http://iroha.readthedocs.io/en/latest/api/queries.html#get-account,Admin creates Alice account in the domain that has only can_get_my_account permission. Alice can access own account information. +Query,Account Asset,can_get_all_acc_ast,FALSE,,Allows getting a balance of assets on any account within the system.,Query response will contain information about all the assets that ever been assigned to an account.,,http://iroha.readthedocs.io/en/latest/api/queries.html#get-account-assets,Admin creates Alice account in a different domain that has only can_get_all_acc_ast permission. Alice can access assets balance on Admin account. +Query,Account Asset,can_get_domain_acc_ast,FALSE,,Allows getting a balance of specified asset on any account within the same domain as a domain of query creator account.,Query response will contain information about all the assets that ever been assigned to an account.,,http://iroha.readthedocs.io/en/latest/api/queries.html#get-account-assets,Admin creates Alice account in the same domain that has only can_get_domain_acc_ast permission. Alice can access assets balance on Admin account. +Query,Account Asset,can_get_my_acc_ast,FALSE,,Allows getting a balance of specified asset on account of query creator.,Query response will contain information about all the assets that ever been assigned to an account.,,http://iroha.readthedocs.io/en/latest/api/queries.html#get-account-assets,Admin creates Alice account in a domain that has only can_get_my_acc_ast permission. Alice can access assets balance on own account. +Query,Account Asset Transaction,can_get_all_acc_ast_txs,FALSE,,Allows getting transactions associated with a specified asset and any account within the system.,,Incoming asset transfers will also appear in the query response.,http://iroha.readthedocs.io/en/latest/api/queries.html#get-account-asset-transactions,"Admin creates Alice account in a different domain that has can_get_all_acc_ast_txs, can_receive and can_transfer permissions. Admin issues some amount of coins and transfers them to Alice. Alice can query all transactions related to coins and Admin account." +Query,Account Asset Transaction,can_get_domain_acc_ast_txs,FALSE,,Allows getting transactions associated with a specified asset and an account from the same domain as query creator.,,Incoming asset transfers will also appear in the query response.,http://iroha.readthedocs.io/en/latest/api/queries.html#get-account-asset-transactions,Admin creates Alice in the same domain that has only can_get_domain_acc_ast_txs permission. Admin issues some amount of coins and transfers them to Alice. Alice can query all transactions related to coins and Admin account. +Query,Account Asset Transaction,can_get_my_acc_ast_txs,FALSE,,Allows getting transactions associated with the account of query creator and specified asset.,,Incoming asset transfers will also appear in the query response.,http://iroha.readthedocs.io/en/latest/api/queries.html#get-account-asset-transactions,Admin creates Alice account in a domain that has only can_get_my_acc_ast_txs permission. Admin issues some amount of coins and transfers them to Alice. Alice can query all transactions related to coins and own account. +Query,Account Transaction,can_get_all_acc_txs,FALSE,,Allows getting all transactions issued by any account within the system.,,Incoming asset transfer inside a transaction would NOT lead to an appearance of the transaction in the command output.,http://iroha.readthedocs.io/en/latest/api/queries.html#get-account-asset-transactions,Admin creates Alice account in a different domain that has only can_get_all_acc_txs permiison. Alice can request all the transactions issues by Admin. +Query,Account Transaction,can_get_domain_acc_txs,FALSE,,Allows getting all transactions issued by any account from the same domain as query creator.,,Incoming asset transfer inside a transaction would NOT lead to an appearance of the transaction in the command output.,http://iroha.readthedocs.io/en/latest/api/queries.html#get-account-asset-transactions,Admin creates Alice account in the same domain that has only can_get_domain_acc_txs permission. Alice can request all the transactions issued by Admin. +Query,Account Transaction,can_get_my_acc_txs,FALSE,,Allows getting all transactions issued by an account of query creator.,,Incoming asset transfer inside a transaction would NOT lead to an appearance of the transaction in the command output.,http://iroha.readthedocs.io/en/latest/api/queries.html#get-account-asset-transactions,Admin creates Alice account in a domain that has only can_get_my_acc_txs permission. Alice can get all transactions issued by own account. +Query,Asset,can_read_assets,FALSE,,Allows getting information about asset precision.,,,http://iroha.readthedocs.io/en/latest/api/queries.html#get-asset-info,Admin creates Alice account in a domain that has can_read_assets permissions. Alice can query information about any asset. +Query,Block Stream,can_get_blocks,FALSE,,Allows subscription to the stream of accepted blocks.,,,, +Query,Role,can_get_roles,FALSE,,"Allows getting a list of roles within the system. +Allows getting a list of permissions associated with a role.",,,"http://iroha.readthedocs.io/en/latest/api/queries.html#get-roles + +http://iroha.readthedocs.io/en/latest/api/queries.html#get-role-permissions",Admin creates Alice account in a domain that has can_get_roles permission. Alice can query list of all existing roles. Alice can query list of permissions contained in any role. +Query,Signatory,can_get_all_signatories,FALSE,,Allows getting a list of public keys linked to an account within the system.,,,http://iroha.readthedocs.io/en/latest/api/queries.html#get-signatories,Admin creates Alice account in a different domain that has only can_get_all_signatories permission. Alice can query a list of public keys related to Admin account. +Query,Signatory,can_get_domain_signatories,FALSE,,Allows getting a list of public keys of any account within the same domain as the domain of query creator account.,,,http://iroha.readthedocs.io/en/latest/api/queries.html#get-signatories,Admin creates Alice account in the same domain that has only can_get_domain_signatories permission. Alice can query a list of public keys related to Admin account. +Query,Signatory,can_get_my_signatories,FALSE,,Allows getting a list of public keys of query creator account.,,,http://iroha.readthedocs.io/en/latest/api/queries.html#get-signatories,Admin creates Alice account in a domain that has only can_get_my_signatories permission. Alice can query a list of public keys related to own account. +Query,Transaction,can_get_all_txs,FALSE,,Allows getting any transaction by hash.,,,http://iroha.readthedocs.io/en/latest/api/queries.html#get-transactions,Admin issues several transactions and creates Alice account in a different domain that has only can_get_all_txs permission. Alice (knowing transactions hashes) can query transactions issued by Admin Account. +Query,Transaction,can_get_my_txs,FALSE,,Allows getting transaction (that was issued by query creator) by hash.,,,http://iroha.readthedocs.io/en/latest/api/queries.html#get-transactions,Admin creates Alice account in a different domain. Alice (knowing transactions hashes) issues several transactions. Alice can query own transactions. From 37ddee1c7ac74980ded065814854ec54b13c451e Mon Sep 17 00:00:00 2001 From: Kitsu Date: Mon, 16 Jul 2018 12:11:09 +0300 Subject: [PATCH 48/97] Mark builder classes as deprecated (#1564) Signed-off-by: Kitsu --- .../builders/common_objects/account_asset_builder.hpp | 2 +- .../builders/common_objects/account_builder.hpp | 7 ++++--- .../builders/common_objects/amount_builder.hpp | 7 ++++--- .../builders/common_objects/asset_builder.hpp | 2 +- shared_model/builders/common_objects/common.hpp | 3 ++- .../builders/common_objects/domain_builder.hpp | 7 ++++--- shared_model/builders/common_objects/peer_builder.hpp | 2 +- .../builders/common_objects/signature_builder.hpp | 7 ++++--- .../protobuf/block_variant_transport_builder.hpp | 2 +- .../protobuf/builder_templates/block_template.hpp | 2 +- .../builder_templates/blocks_query_template.hpp | 11 ++++++----- .../builder_templates/empty_block_template.hpp | 2 +- .../protobuf/builder_templates/proposal_template.hpp | 2 +- .../builder_templates/query_response_template.hpp | 2 +- .../protobuf/builder_templates/query_template.hpp | 9 +++++---- .../builder_templates/transaction_template.hpp | 2 +- .../common_objects/proto_account_asset_builder.hpp | 2 +- .../protobuf/common_objects/proto_account_builder.hpp | 2 +- .../protobuf/common_objects/proto_amount_builder.hpp | 2 +- .../protobuf/common_objects/proto_asset_builder.hpp | 2 +- .../protobuf/common_objects/proto_domain_builder.hpp | 2 +- .../protobuf/common_objects/proto_peer_builder.hpp | 2 +- .../common_objects/proto_signature_builder.hpp | 2 +- .../proto_block_query_response_builder.hpp | 2 +- .../proto_transaction_status_builder.hpp | 2 +- .../protobuf/transaction_sequence_builder.hpp | 2 +- shared_model/builders/protobuf/transport_builder.hpp | 2 +- shared_model/builders/protobuf/unsigned_proto.hpp | 2 +- .../query_responses/block_query_response_builder.hpp | 2 +- .../transaction_status_builder.hpp | 2 +- shared_model/cryptography/blob.hpp | 1 - shared_model/interfaces/common_objects/types.hpp | 1 + 32 files changed, 53 insertions(+), 46 deletions(-) diff --git a/shared_model/builders/common_objects/account_asset_builder.hpp b/shared_model/builders/common_objects/account_asset_builder.hpp index 5cb1b1f9b1..cf3915b38f 100644 --- a/shared_model/builders/common_objects/account_asset_builder.hpp +++ b/shared_model/builders/common_objects/account_asset_builder.hpp @@ -35,7 +35,7 @@ namespace shared_model { * to perform stateless validation on model fields */ template - class AccountAssetBuilder + class DEPRECATED AccountAssetBuilder : public CommonObjectBuilder { diff --git a/shared_model/builders/common_objects/account_builder.hpp b/shared_model/builders/common_objects/account_builder.hpp index cac6611e6f..33465f7e99 100644 --- a/shared_model/builders/common_objects/account_builder.hpp +++ b/shared_model/builders/common_objects/account_builder.hpp @@ -35,9 +35,10 @@ namespace shared_model { * to perform stateless validation on model fields */ template - class AccountBuilder : public CommonObjectBuilder { + class DEPRECATED AccountBuilder + : public CommonObjectBuilder { public: AccountBuilder accountId( const interface::types::AccountIdType &account_id) { diff --git a/shared_model/builders/common_objects/amount_builder.hpp b/shared_model/builders/common_objects/amount_builder.hpp index e243d5140b..4184d39d95 100644 --- a/shared_model/builders/common_objects/amount_builder.hpp +++ b/shared_model/builders/common_objects/amount_builder.hpp @@ -37,9 +37,10 @@ namespace shared_model { * to perform stateless validation on model fields */ template - class AmountBuilder : public CommonObjectBuilder { + class DEPRECATED AmountBuilder + : public CommonObjectBuilder { public: AmountBuilder intValue(const boost::multiprecision::uint256_t &value) { AmountBuilder copy(*this); diff --git a/shared_model/builders/common_objects/asset_builder.hpp b/shared_model/builders/common_objects/asset_builder.hpp index 79a2a812f4..d8a4e1e0ed 100644 --- a/shared_model/builders/common_objects/asset_builder.hpp +++ b/shared_model/builders/common_objects/asset_builder.hpp @@ -36,7 +36,7 @@ namespace shared_model { * to perform stateless validation on model fields */ template - class AssetBuilder + class DEPRECATED AssetBuilder : public CommonObjectBuilder { public: AssetBuilder assetId(const interface::types::AccountIdType &asset_id) { diff --git a/shared_model/builders/common_objects/common.hpp b/shared_model/builders/common_objects/common.hpp index 32c51b533d..ec0f771f47 100644 --- a/shared_model/builders/common_objects/common.hpp +++ b/shared_model/builders/common_objects/common.hpp @@ -19,6 +19,7 @@ #define IROHA_BUILDERS_COMMON_HPP #include "common/result.hpp" +#include "utils/swig_keyword_hider.hpp" #include "validators/answer.hpp" // TODO: 16.02.2018 nickaleks: Add validators for common_objects IR-986 @@ -43,7 +44,7 @@ namespace shared_model { * @tparam Validator - validation object */ template - class CommonObjectBuilder { + class DEPRECATED CommonObjectBuilder { public: /** * build() constructs specified object and performs stateless validation diff --git a/shared_model/builders/common_objects/domain_builder.hpp b/shared_model/builders/common_objects/domain_builder.hpp index a7a47c3e2e..c6a3e0527d 100644 --- a/shared_model/builders/common_objects/domain_builder.hpp +++ b/shared_model/builders/common_objects/domain_builder.hpp @@ -25,9 +25,10 @@ namespace shared_model { namespace builder { template - class DomainBuilder : public CommonObjectBuilder { + class DEPRECATED DomainBuilder + : public CommonObjectBuilder { public: DomainBuilder defaultRole( const interface::types::RoleIdType &default_role) { diff --git a/shared_model/builders/common_objects/peer_builder.hpp b/shared_model/builders/common_objects/peer_builder.hpp index 1f1a71b995..1da7229e30 100644 --- a/shared_model/builders/common_objects/peer_builder.hpp +++ b/shared_model/builders/common_objects/peer_builder.hpp @@ -35,7 +35,7 @@ namespace shared_model { * to perform stateless validation on model fields */ template - class PeerBuilder + class DEPRECATED PeerBuilder : public CommonObjectBuilder { public: PeerBuilder address(const interface::types::AddressType &address) { diff --git a/shared_model/builders/common_objects/signature_builder.hpp b/shared_model/builders/common_objects/signature_builder.hpp index 50bd9bcf6d..dadccdc5a4 100644 --- a/shared_model/builders/common_objects/signature_builder.hpp +++ b/shared_model/builders/common_objects/signature_builder.hpp @@ -36,9 +36,10 @@ namespace shared_model { * to perform stateless validation on model fields */ template - class SignatureBuilder : public CommonObjectBuilder { + class DEPRECATED SignatureBuilder + : public CommonObjectBuilder { public: SignatureBuilder publicKey( const shared_model::interface::types::PubkeyType &key) { diff --git a/shared_model/builders/protobuf/block_variant_transport_builder.hpp b/shared_model/builders/protobuf/block_variant_transport_builder.hpp index 6d6f3cc8b5..20e3495198 100644 --- a/shared_model/builders/protobuf/block_variant_transport_builder.hpp +++ b/shared_model/builders/protobuf/block_variant_transport_builder.hpp @@ -19,7 +19,7 @@ namespace shared_model { * @tparam SV Stateless validator type */ template - class TransportBuilder { + class DEPRECATED TransportBuilder { private: /** * Create container type (i.e. Block or EmptyBlock) diff --git a/shared_model/builders/protobuf/builder_templates/block_template.hpp b/shared_model/builders/protobuf/builder_templates/block_template.hpp index b8c805e1e7..e8126847c7 100644 --- a/shared_model/builders/protobuf/builder_templates/block_template.hpp +++ b/shared_model/builders/protobuf/builder_templates/block_template.hpp @@ -42,7 +42,7 @@ namespace shared_model { template > - class TemplateBlockBuilder { + class DEPRECATED TemplateBlockBuilder { private: template friend class TemplateBlockBuilder; diff --git a/shared_model/builders/protobuf/builder_templates/blocks_query_template.hpp b/shared_model/builders/protobuf/builder_templates/blocks_query_template.hpp index 191b0df7af..5da3b909bb 100644 --- a/shared_model/builders/protobuf/builder_templates/blocks_query_template.hpp +++ b/shared_model/builders/protobuf/builder_templates/blocks_query_template.hpp @@ -8,8 +8,8 @@ #include "backend/protobuf/queries/proto_blocks_query.hpp" #include "builders/protobuf/unsigned_proto.hpp" -#include "interfaces/queries/blocks_query.hpp" #include "interfaces/common_objects/types.hpp" +#include "interfaces/queries/blocks_query.hpp" #include "interfaces/transaction.hpp" #include "queries.pb.h" #include "validators/default_validator.hpp" @@ -26,9 +26,9 @@ namespace shared_model { * @tparam BT -- build type of built object returned by build method */ template > - class TemplateBlocksQueryBuilder { + typename SV = validation::DefaultBlocksQueryValidator, + typename BT = UnsignedWrapper> + class DEPRECATED TemplateBlocksQueryBuilder { private: template friend class TemplateBlocksQueryBuilder; @@ -46,7 +46,8 @@ namespace shared_model { using ProtoBlocksQuery = iroha::protocol::BlocksQuery; template - TemplateBlocksQueryBuilder(const TemplateBlocksQueryBuilder &o) + TemplateBlocksQueryBuilder( + const TemplateBlocksQueryBuilder &o) : query_(o.query_), stateless_validator_(o.stateless_validator_) {} /** diff --git a/shared_model/builders/protobuf/builder_templates/empty_block_template.hpp b/shared_model/builders/protobuf/builder_templates/empty_block_template.hpp index 4f1727de57..a16a4bdd2c 100644 --- a/shared_model/builders/protobuf/builder_templates/empty_block_template.hpp +++ b/shared_model/builders/protobuf/builder_templates/empty_block_template.hpp @@ -26,7 +26,7 @@ namespace shared_model { template > - class TemplateEmptyBlockBuilder { + class DEPRECATED TemplateEmptyBlockBuilder { private: template friend class TemplateEmptyBlockBuilder; diff --git a/shared_model/builders/protobuf/builder_templates/proposal_template.hpp b/shared_model/builders/protobuf/builder_templates/proposal_template.hpp index 431d04bf71..8720d663c7 100644 --- a/shared_model/builders/protobuf/builder_templates/proposal_template.hpp +++ b/shared_model/builders/protobuf/builder_templates/proposal_template.hpp @@ -34,7 +34,7 @@ namespace shared_model { * @tparam SV -- stateless validator called when build method is invoked */ template - class TemplateProposalBuilder { + class DEPRECATED TemplateProposalBuilder { private: template friend class TemplateProposalBuilder; diff --git a/shared_model/builders/protobuf/builder_templates/query_response_template.hpp b/shared_model/builders/protobuf/builder_templates/query_response_template.hpp index 3b7bee9815..ef10af79f1 100644 --- a/shared_model/builders/protobuf/builder_templates/query_response_template.hpp +++ b/shared_model/builders/protobuf/builder_templates/query_response_template.hpp @@ -28,7 +28,7 @@ namespace shared_model { * set */ template - class TemplateQueryResponseBuilder { + class DEPRECATED TemplateQueryResponseBuilder { public: template TemplateQueryResponseBuilder(TemplateQueryResponseBuilder &&o) diff --git a/shared_model/builders/protobuf/builder_templates/query_template.hpp b/shared_model/builders/protobuf/builder_templates/query_template.hpp index f44c92e6b6..7831d5800f 100644 --- a/shared_model/builders/protobuf/builder_templates/query_template.hpp +++ b/shared_model/builders/protobuf/builder_templates/query_template.hpp @@ -29,7 +29,7 @@ namespace shared_model { template > - class TemplateQueryBuilder { + class DEPRECATED TemplateQueryBuilder { private: template friend class TemplateQueryBuilder; @@ -143,9 +143,10 @@ namespace shared_model { }); } - auto getAccountDetail(const interface::types::AccountIdType &account_id = "", - const interface::types::AccountDetailKeyType &key = "", - const interface::types::AccountIdType &writer = "") { + auto getAccountDetail( + const interface::types::AccountIdType &account_id = "", + const interface::types::AccountDetailKeyType &key = "", + const interface::types::AccountIdType &writer = "") { return queryField([&](auto proto_query) { auto query = proto_query->mutable_get_account_detail(); if (not account_id.empty()) { diff --git a/shared_model/builders/protobuf/builder_templates/transaction_template.hpp b/shared_model/builders/protobuf/builder_templates/transaction_template.hpp index c916543f99..6ba054b7c7 100644 --- a/shared_model/builders/protobuf/builder_templates/transaction_template.hpp +++ b/shared_model/builders/protobuf/builder_templates/transaction_template.hpp @@ -47,7 +47,7 @@ namespace shared_model { template > - class TemplateTransactionBuilder { + class DEPRECATED TemplateTransactionBuilder { private: template friend class TemplateTransactionBuilder; diff --git a/shared_model/builders/protobuf/common_objects/proto_account_asset_builder.hpp b/shared_model/builders/protobuf/common_objects/proto_account_asset_builder.hpp index 7c88bed648..93ac802ac6 100644 --- a/shared_model/builders/protobuf/common_objects/proto_account_asset_builder.hpp +++ b/shared_model/builders/protobuf/common_objects/proto_account_asset_builder.hpp @@ -27,7 +27,7 @@ namespace shared_model { * AccountAssetBuilder is used to construct AccountAsset proto objects with * initialized protobuf implementation */ - class AccountAssetBuilder { + class DEPRECATED AccountAssetBuilder { public: shared_model::proto::AccountAsset build() { return shared_model::proto::AccountAsset( diff --git a/shared_model/builders/protobuf/common_objects/proto_account_builder.hpp b/shared_model/builders/protobuf/common_objects/proto_account_builder.hpp index 0e64308342..487dfbad4d 100644 --- a/shared_model/builders/protobuf/common_objects/proto_account_builder.hpp +++ b/shared_model/builders/protobuf/common_objects/proto_account_builder.hpp @@ -28,7 +28,7 @@ namespace shared_model { * AccountBuilder is used to construct Account proto objects with * initialized protobuf implementation */ - class AccountBuilder { + class DEPRECATED AccountBuilder { public: shared_model::proto::Account build() { return shared_model::proto::Account(iroha::protocol::Account(account_)); diff --git a/shared_model/builders/protobuf/common_objects/proto_amount_builder.hpp b/shared_model/builders/protobuf/common_objects/proto_amount_builder.hpp index 2b8a16537e..980d9b0037 100644 --- a/shared_model/builders/protobuf/common_objects/proto_amount_builder.hpp +++ b/shared_model/builders/protobuf/common_objects/proto_amount_builder.hpp @@ -28,7 +28,7 @@ namespace shared_model { * AmountBuilder is used to construct Amount proto objects with initialized * protobuf implementation */ - class AmountBuilder { + class DEPRECATED AmountBuilder { public: shared_model::proto::Amount build() { return shared_model::proto::Amount(iroha::protocol::Amount(amount_)); diff --git a/shared_model/builders/protobuf/common_objects/proto_asset_builder.hpp b/shared_model/builders/protobuf/common_objects/proto_asset_builder.hpp index c0123aeb8b..a412725e46 100644 --- a/shared_model/builders/protobuf/common_objects/proto_asset_builder.hpp +++ b/shared_model/builders/protobuf/common_objects/proto_asset_builder.hpp @@ -28,7 +28,7 @@ namespace shared_model { * AssetBuilder is used to construct Asset proto objects with initialized * protobuf implementation */ - class AssetBuilder { + class DEPRECATED AssetBuilder { public: shared_model::proto::Asset build() { return shared_model::proto::Asset(iroha::protocol::Asset(asset_)); diff --git a/shared_model/builders/protobuf/common_objects/proto_domain_builder.hpp b/shared_model/builders/protobuf/common_objects/proto_domain_builder.hpp index 3eab6c5878..3dad699e59 100644 --- a/shared_model/builders/protobuf/common_objects/proto_domain_builder.hpp +++ b/shared_model/builders/protobuf/common_objects/proto_domain_builder.hpp @@ -23,7 +23,7 @@ namespace shared_model { namespace proto { - class DomainBuilder { + class DEPRECATED DomainBuilder { public: shared_model::proto::Domain build() { return shared_model::proto::Domain(iroha::protocol::Domain(domain_)); diff --git a/shared_model/builders/protobuf/common_objects/proto_peer_builder.hpp b/shared_model/builders/protobuf/common_objects/proto_peer_builder.hpp index 78334efb74..a6d20de1b3 100644 --- a/shared_model/builders/protobuf/common_objects/proto_peer_builder.hpp +++ b/shared_model/builders/protobuf/common_objects/proto_peer_builder.hpp @@ -28,7 +28,7 @@ namespace shared_model { * PeerBuilder is used to construct Peer proto objects with initialized * protobuf implementation */ - class PeerBuilder { + class DEPRECATED PeerBuilder { public: shared_model::proto::Peer build() { return shared_model::proto::Peer(iroha::protocol::Peer(peer_)); diff --git a/shared_model/builders/protobuf/common_objects/proto_signature_builder.hpp b/shared_model/builders/protobuf/common_objects/proto_signature_builder.hpp index e920d6d47d..501edd720f 100644 --- a/shared_model/builders/protobuf/common_objects/proto_signature_builder.hpp +++ b/shared_model/builders/protobuf/common_objects/proto_signature_builder.hpp @@ -29,7 +29,7 @@ namespace shared_model { * SignatureBuilder is used to construct Signature proto objects with * initialized protobuf implementation */ - class SignatureBuilder { + class DEPRECATED SignatureBuilder { public: shared_model::proto::Signature build() { return shared_model::proto::Signature( diff --git a/shared_model/builders/protobuf/query_responses/proto_block_query_response_builder.hpp b/shared_model/builders/protobuf/query_responses/proto_block_query_response_builder.hpp index c9d0a39b74..79e3701bb1 100644 --- a/shared_model/builders/protobuf/query_responses/proto_block_query_response_builder.hpp +++ b/shared_model/builders/protobuf/query_responses/proto_block_query_response_builder.hpp @@ -10,7 +10,7 @@ namespace shared_model { namespace proto { - class BlockQueryResponseBuilder { + class DEPRECATED BlockQueryResponseBuilder { public: shared_model::proto::BlockQueryResponse build() &&; diff --git a/shared_model/builders/protobuf/transaction_responses/proto_transaction_status_builder.hpp b/shared_model/builders/protobuf/transaction_responses/proto_transaction_status_builder.hpp index 4c4765bb49..f98500fe82 100644 --- a/shared_model/builders/protobuf/transaction_responses/proto_transaction_status_builder.hpp +++ b/shared_model/builders/protobuf/transaction_responses/proto_transaction_status_builder.hpp @@ -22,7 +22,7 @@ namespace shared_model { namespace proto { - class TransactionStatusBuilder { + class DEPRECATED TransactionStatusBuilder { public: shared_model::proto::TransactionResponse build() &&; diff --git a/shared_model/builders/protobuf/transaction_sequence_builder.hpp b/shared_model/builders/protobuf/transaction_sequence_builder.hpp index fe002a246e..7a3a70a037 100644 --- a/shared_model/builders/protobuf/transaction_sequence_builder.hpp +++ b/shared_model/builders/protobuf/transaction_sequence_builder.hpp @@ -17,7 +17,7 @@ namespace shared_model { * @tparam SV Stateless validator type */ template - class TransportBuilder { + class DEPRECATED TransportBuilder { public: TransportBuilder( SV stateless_validator = SV()) diff --git a/shared_model/builders/protobuf/transport_builder.hpp b/shared_model/builders/protobuf/transport_builder.hpp index 6623f69d7c..2d912d01b2 100644 --- a/shared_model/builders/protobuf/transport_builder.hpp +++ b/shared_model/builders/protobuf/transport_builder.hpp @@ -30,7 +30,7 @@ namespace shared_model { * @tparam SV Stateless validator type */ template - class TransportBuilder { + class DEPRECATED TransportBuilder { public: TransportBuilder(const SV &validator = SV()) : stateless_validator_(validator) {} diff --git a/shared_model/builders/protobuf/unsigned_proto.hpp b/shared_model/builders/protobuf/unsigned_proto.hpp index fde59bcc43..d87d464bc3 100644 --- a/shared_model/builders/protobuf/unsigned_proto.hpp +++ b/shared_model/builders/protobuf/unsigned_proto.hpp @@ -33,7 +33,7 @@ namespace shared_model { * finish() throws an exception */ template - class UnsignedWrapper { + class DEPRECATED UnsignedWrapper { public: using ModelType = T; diff --git a/shared_model/builders/query_responses/block_query_response_builder.hpp b/shared_model/builders/query_responses/block_query_response_builder.hpp index a2b63eec28..7cc708b591 100644 --- a/shared_model/builders/query_responses/block_query_response_builder.hpp +++ b/shared_model/builders/query_responses/block_query_response_builder.hpp @@ -16,7 +16,7 @@ namespace shared_model { * @tparam BuilderImpl */ template - class BlockQueryResponseBuilder { + class DEPRECATED BlockQueryResponseBuilder { public: std::shared_ptr build() { return clone(builder_.build()); diff --git a/shared_model/builders/transaction_responses/transaction_status_builder.hpp b/shared_model/builders/transaction_responses/transaction_status_builder.hpp index 58f8592965..1e4d030eae 100644 --- a/shared_model/builders/transaction_responses/transaction_status_builder.hpp +++ b/shared_model/builders/transaction_responses/transaction_status_builder.hpp @@ -31,7 +31,7 @@ namespace shared_model { * @tparam BuilderImpl */ template - class TransactionStatusBuilder { + class DEPRECATED TransactionStatusBuilder { public: std::shared_ptr build() { return clone(builder_.build()); diff --git a/shared_model/cryptography/blob.hpp b/shared_model/cryptography/blob.hpp index b1a9d30e7c..9639d6d7a0 100644 --- a/shared_model/cryptography/blob.hpp +++ b/shared_model/cryptography/blob.hpp @@ -23,7 +23,6 @@ #include "interfaces/base/model_primitive.hpp" #include "utils/lazy_initializer.hpp" -#include "utils/swig_keyword_hider.hpp" namespace shared_model { namespace crypto { diff --git a/shared_model/interfaces/common_objects/types.hpp b/shared_model/interfaces/common_objects/types.hpp index a873ae1bca..54b15eb034 100644 --- a/shared_model/interfaces/common_objects/types.hpp +++ b/shared_model/interfaces/common_objects/types.hpp @@ -26,6 +26,7 @@ #include #include "cryptography/hash.hpp" #include "cryptography/public_key.hpp" +#include "utils/swig_keyword_hider.hpp" namespace shared_model { From 72338044110750a80c85b8ba74a43e8761beed6e Mon Sep 17 00:00:00 2001 From: Akvinikym Date: Mon, 16 Jul 2018 16:57:27 +0300 Subject: [PATCH 49/97] Fixed Java bindings (#1569) Signed-off-by: Akvinikym --- test/module/shared_model/bindings/QueryTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/module/shared_model/bindings/QueryTest.java b/test/module/shared_model/bindings/QueryTest.java index 51003c2335..f4b08e74b7 100644 --- a/test/module/shared_model/bindings/QueryTest.java +++ b/test/module/shared_model/bindings/QueryTest.java @@ -548,7 +548,7 @@ void getAccountDetailWithInvalidDomain() { @Test void getAccountDetailWithNoArgs() { - UnsignedQuery query = base().getAccountDetail(); + UnsignedQuery query = base().getAccountDetail().build(); assertTrue(checkProtoQuery(proto(query))); } } From c4293313e68401f2e6eea5c7c161b9cdfa63fa43 Mon Sep 17 00:00:00 2001 From: Kitsu Date: Mon, 16 Jul 2018 19:37:35 +0300 Subject: [PATCH 50/97] Use amount as a string in schema (#1555) Signed-off-by: Kitsu --- example/python/prepare.sh | 2 +- iroha-cli/impl/query_response_handler.cpp | 4 +- .../impl/interactive_transaction_cli.cpp | 32 +-- irohad/ametsuchi/impl/postgres_wsv_common.hpp | 14 +- irohad/ametsuchi/impl/temporary_wsv_impl.cpp | 1 - irohad/execution/command_executor.hpp | 1 - irohad/execution/impl/command_executor.cpp | 15 +- irohad/model/CMakeLists.txt | 1 - irohad/model/account_asset.hpp | 5 +- irohad/model/commands/add_asset_quantity.hpp | 5 +- .../commands/subtract_asset_quantity.hpp | 6 +- irohad/model/commands/transfer_asset.hpp | 5 +- .../converters/impl/json_command_factory.cpp | 67 +----- .../converters/impl/pb_command_factory.cpp | 18 +- irohad/model/converters/impl/pb_common.cpp | 24 +- .../impl/pb_query_response_factory.cpp | 24 +- irohad/model/converters/pb_common.hpp | 5 - irohad/model/generators/command_generator.hpp | 10 +- .../generators/impl/command_generator.cpp | 6 +- libs/CMakeLists.txt | 6 +- libs/amount/CMakeLists.txt | 24 -- libs/amount/amount.cpp | 217 ------------------ libs/amount/amount.hpp | 180 --------------- shared_model/backend/protobuf/CMakeLists.txt | 1 - .../impl/proto_add_asset_quantity.cpp | 5 +- .../impl/proto_subtract_asset_quantity.cpp | 2 +- .../commands/impl/proto_transfer_asset.cpp | 3 +- .../commands/proto_add_asset_quantity.hpp | 4 +- .../proto_subtract_asset_quantity.hpp | 4 +- .../commands/proto_transfer_asset.hpp | 4 +- .../protobuf/common_objects/account_asset.hpp | 4 +- .../protobuf/common_objects/amount.hpp | 108 --------- shared_model/bindings/CMakeLists.txt | 1 - .../common_objects/amount_builder.hpp | 115 ---------- shared_model/builders/default_builders.hpp | 10 - .../query_response_template.hpp | 1 - .../transaction_template.hpp | 8 +- .../proto_account_asset_builder.hpp | 4 +- .../common_objects/proto_amount_builder.hpp | 55 ----- shared_model/builders/protobuf/helpers.hpp | 46 ---- shared_model/interfaces/CMakeLists.txt | 1 + .../interfaces/common_objects/amount.hpp | 75 +++--- .../interfaces/common_objects/impl/amount.cpp | 74 ++++++ shared_model/packages/ios/ios-build.sh | 1 - shared_model/schema/commands.proto | 6 +- shared_model/schema/primitive.proto | 15 +- shared_model/schema/qry_responses.proto | 2 +- shared_model/utils/amount_utils.cpp | 54 ++--- shared_model/utils/amount_utils.hpp | 3 +- test/integration/transport/CMakeLists.txt | 1 - .../irohad/ametsuchi/ametsuchi_test.cpp | 29 +-- .../command_validate_execute_test.cpp | 43 +--- .../irohad/execution/query_execution_test.cpp | 18 +- .../model/converters/json_commands_test.cpp | 6 +- .../model/converters/pb_commands_test.cpp | 6 +- .../converters/pb_query_responses_test.cpp | 3 +- .../model/operators/model_operators_test.cpp | 21 +- test/module/irohad/ordering/CMakeLists.txt | 2 - .../irohad/torii/torii_queries_test.cpp | 6 +- test/module/libs/CMakeLists.txt | 1 - test/module/libs/amount/CMakeLists.txt | 24 -- test/module/libs/amount/amount_test.cpp | 95 -------- .../module/shared_model/amount_utils_test.cpp | 140 ++++------- .../shared_model/backend_proto/CMakeLists.txt | 8 - .../shared_proto_amount_test.cpp | 62 ----- .../shared_proto_transaction_test.cpp | 3 +- .../builders/common_objects/CMakeLists.txt | 9 - .../account_asset_builder_test.cpp | 7 +- .../common_objects/amount_builder_test.cpp | 81 ------- .../query_response_builder_test.cpp | 13 +- .../builders/protobuf/CMakeLists.txt | 8 - .../proto_account_asset_builder_test.cpp | 7 +- .../proto_amount_builder_test.cpp | 62 ----- .../shared_model/cryptography/CMakeLists.txt | 1 - .../validators/field_validator_test.cpp | 18 +- .../validators/validators_fixture.hpp | 9 +- 76 files changed, 308 insertions(+), 1653 deletions(-) delete mode 100644 libs/amount/CMakeLists.txt delete mode 100644 libs/amount/amount.cpp delete mode 100644 libs/amount/amount.hpp delete mode 100644 shared_model/backend/protobuf/common_objects/amount.hpp delete mode 100644 shared_model/builders/common_objects/amount_builder.hpp delete mode 100644 shared_model/builders/protobuf/common_objects/proto_amount_builder.hpp delete mode 100644 shared_model/builders/protobuf/helpers.hpp create mode 100644 shared_model/interfaces/common_objects/impl/amount.cpp delete mode 100644 test/module/libs/amount/CMakeLists.txt delete mode 100644 test/module/libs/amount/amount_test.cpp delete mode 100644 test/module/shared_model/backend_proto/common_objects/shared_proto_amount_test.cpp delete mode 100644 test/module/shared_model/builders/common_objects/amount_builder_test.cpp delete mode 100644 test/module/shared_model/builders/protobuf/common_objects/proto_amount_builder_test.cpp diff --git a/example/python/prepare.sh b/example/python/prepare.sh index 35244e8dde..cd6bb3bea3 100755 --- a/example/python/prepare.sh +++ b/example/python/prepare.sh @@ -9,5 +9,5 @@ cmake -H$IROHA_HOME -Bbuild -DSWIG_PYTHON=ON -DSUPPORT_PYTHON2=ON; cmake --build build/ --target irohapy -- -j"$(getconf _NPROCESSORS_ONLN)" # generate proto files in current dir -protoc --proto_path=../../shared_model/schema --python_out=. block.proto primitive.proto commands.proto queries.proto qry_responses.proto endpoint.proto +protoc --proto_path=../../shared_model/schema --python_out=. block.proto transaction.proto primitive.proto commands.proto queries.proto qry_responses.proto endpoint.proto python -m grpc_tools.protoc --proto_path=../../schema --proto_path=../../shared_model/schema --python_out=. --grpc_python_out=. endpoint.proto yac.proto ordering.proto loader.proto diff --git a/iroha-cli/impl/query_response_handler.cpp b/iroha-cli/impl/query_response_handler.cpp index d360756d7b..751d434f97 100644 --- a/iroha-cli/impl/query_response_handler.cpp +++ b/iroha-cli/impl/query_response_handler.cpp @@ -153,9 +153,7 @@ namespace iroha_cli { for (auto &acc_asset : acc_assets) { log_->info(prefix.at(kAccountId), acc_asset.account_id()); log_->info(prefix.at(kAssetId), acc_asset.asset_id()); - auto balance = - iroha::model::converters::deserializeAmount(acc_asset.balance()); - log_->info(prefix.at(kAmount), balance.to_string()); + log_->info(prefix.at(kAmount), acc_asset.balance()); } } diff --git a/iroha-cli/interactive/impl/interactive_transaction_cli.cpp b/iroha-cli/interactive/impl/interactive_transaction_cli.cpp index c485634949..8ba6be8104 100644 --- a/iroha-cli/interactive/impl/interactive_transaction_cli.cpp +++ b/iroha-cli/interactive/impl/interactive_transaction_cli.cpp @@ -68,8 +68,7 @@ namespace iroha_cli { const auto acc_id = "Account Id"; const auto ast_id = "Asset Id"; const auto dom_id = "Domain Id"; - const auto amount_a = "Amount to add (integer part)"; - const auto amount_b = "Amount to add (precision)"; + const auto amount_str = "Amount to to add, e.g 123.456"; const auto peer_id = "Full address of a peer"; const auto pub_key = "Public Key"; const auto acc_name = "Account Name"; @@ -87,8 +86,7 @@ namespace iroha_cli { const auto can_roles = "Can create/append roles"; command_params_map_ = { - {ADD_ASSET_QTY, - makeParamsDescription({acc_id, ast_id, amount_a, amount_b})}, + {ADD_ASSET_QTY, makeParamsDescription({acc_id, ast_id, amount_str})}, {ADD_PEER, makeParamsDescription({peer_id, pub_key})}, {ADD_SIGN, makeParamsDescription({acc_id, pub_key})}, {CREATE_ACC, makeParamsDescription({acc_name, dom_id, pub_key})}, @@ -103,8 +101,7 @@ namespace iroha_cli { makeParamsDescription({std::string("Src") + acc_id, std::string("Dest") + acc_id, ast_id, - amount_a, - amount_b})}, + amount_str})}, {CREATE_ROLE, makeParamsDescription({role, can_read_self, @@ -330,19 +327,7 @@ namespace iroha_cli { InteractiveTransactionCli::parseAddAssetQuantity( std::vector params) { auto asset_id = params[0]; - auto val_int = - parser::parseValue(params[2]); - auto precision = parser::parseValue(params[3]); - if (not val_int or not precision) { - std::cout << "Wrong format for amount" << std::endl; - return nullptr; - } - if (precision.value() > 255) { - std::cout << "Too big precision (should be less than 256)" << std::endl; - return nullptr; - } - std::cout << val_int.value() << " " << precision.value() << std::endl; - iroha::Amount amount(val_int.value(), precision.value()); + auto amount = params[1]; return generator_.generateAddAssetQuantity(asset_id, amount); } @@ -432,14 +417,7 @@ namespace iroha_cli { auto src_account_id = params[0]; auto dest_account_id = params[1]; auto asset_id = params[2]; - auto val_int = - parser::parseValue(params[3]); - auto precision = parser::parseValue(params[4]); - if (not val_int or not precision) { - std::cout << "Wrong format for amount" << std::endl; - return nullptr; - } - iroha::Amount amount(val_int.value(), precision.value()); + auto amount = params[3]; return generator_.generateTransferAsset( src_account_id, dest_account_id, asset_id, amount); } diff --git a/irohad/ametsuchi/impl/postgres_wsv_common.hpp b/irohad/ametsuchi/impl/postgres_wsv_common.hpp index 7db8e3d59b..acb4080105 100644 --- a/irohad/ametsuchi/impl/postgres_wsv_common.hpp +++ b/irohad/ametsuchi/impl/postgres_wsv_common.hpp @@ -121,15 +121,11 @@ namespace iroha { const std::string &asset_id, const std::string &amount) noexcept { return tryBuild([&] { - auto balance = - shared_model::builder::DefaultAmountBuilder::fromString(amount); - return balance | [&](const auto &balance_ptr) { - return shared_model::builder::DefaultAccountAssetBuilder() - .accountId(account_id) - .assetId(asset_id) - .balance(*balance_ptr) - .build(); - }; + return shared_model::builder::DefaultAccountAssetBuilder() + .accountId(account_id) + .assetId(asset_id) + .balance(shared_model::interface::Amount(amount)) + .build(); }); } diff --git a/irohad/ametsuchi/impl/temporary_wsv_impl.cpp b/irohad/ametsuchi/impl/temporary_wsv_impl.cpp index 563b3aac01..d8cf25b059 100644 --- a/irohad/ametsuchi/impl/temporary_wsv_impl.cpp +++ b/irohad/ametsuchi/impl/temporary_wsv_impl.cpp @@ -19,7 +19,6 @@ #include "ametsuchi/impl/postgres_wsv_command.hpp" #include "ametsuchi/impl/postgres_wsv_query.hpp" -#include "amount/amount.hpp" namespace iroha { namespace ametsuchi { diff --git a/irohad/execution/command_executor.hpp b/irohad/execution/command_executor.hpp index 08e39351df..a8f70dbdf5 100644 --- a/irohad/execution/command_executor.hpp +++ b/irohad/execution/command_executor.hpp @@ -130,7 +130,6 @@ namespace iroha { std::shared_ptr commands; shared_model::interface::types::AccountIdType creator_account_id; - shared_model::builder::AmountBuilderWithoutValidator amount_builder_; shared_model::builder::DefaultAccountAssetBuilder account_asset_builder_; shared_model::builder::DefaultAccountBuilder account_builder_; shared_model::builder::DefaultAssetBuilder asset_builder_; diff --git a/irohad/execution/impl/command_executor.cpp b/irohad/execution/impl/command_executor.cpp index 3d395c8b6e..d78ba028f7 100644 --- a/irohad/execution/impl/command_executor.cpp +++ b/irohad/execution/impl/command_executor.cpp @@ -89,15 +89,10 @@ namespace iroha { auto account_asset = queries->getAccountAsset(creator_account_id, command.assetId()); - auto new_balance = command_amount | [this](const auto &amount) { - return amount_builder_.precision(amount->precision()) - .intValue(amount->intValue()) - .build(); - }; using AccountAssetResult = expected::Result, iroha::CommandError>; - auto account_asset_new = new_balance.match( + auto account_asset_new = command_amount.match( [this, &account_asset, &command_name, &command]( const expected::Value< std::shared_ptr> @@ -438,17 +433,13 @@ namespace iroha { }; auto dest_account_asset_new = command_amount | [&](const auto &amount) { - const auto kZero = boost::get< - expected::Value>>( - amount_builder_.precision(asset.value()->precision()) - .intValue(0) - .build()); + static const shared_model::interface::Amount kZero("0"); auto new_amount = (dest_account_asset | [](const auto &ast) -> boost::optional { return {ast->balance()}; }) - .get_value_or(*kZero.value) + .get_value_or(kZero) + *amount; return new_amount | [this, &command](const auto &new_dest_balance) { return account_asset_builder_.assetId(command.assetId()) diff --git a/irohad/model/CMakeLists.txt b/irohad/model/CMakeLists.txt index 77c02c7715..5992b853fe 100644 --- a/irohad/model/CMakeLists.txt +++ b/irohad/model/CMakeLists.txt @@ -36,7 +36,6 @@ target_link_libraries(model sha3_hash rxcpp logger - iroha_amount common_execution schema ed25519_crypto diff --git a/irohad/model/account_asset.hpp b/irohad/model/account_asset.hpp index f01a4295ba..cbb7b5396e 100644 --- a/irohad/model/account_asset.hpp +++ b/irohad/model/account_asset.hpp @@ -19,7 +19,6 @@ #define IROHA_ACCOUNT_ASSET_HPP #include -#include "amount/amount.hpp" namespace iroha { namespace model { @@ -31,7 +30,7 @@ namespace iroha { AccountAsset(const std::string &asset_id, const std::string &account_id, - const Amount &balance) + const std::string &balance) : asset_id(asset_id), account_id(account_id), balance(balance) {} /** @@ -47,7 +46,7 @@ namespace iroha { /** * Current balance */ - Amount balance; + std::string balance; }; } // namespace model } // namespace iroha diff --git a/irohad/model/commands/add_asset_quantity.hpp b/irohad/model/commands/add_asset_quantity.hpp index 43c87c784e..578aeec104 100644 --- a/irohad/model/commands/add_asset_quantity.hpp +++ b/irohad/model/commands/add_asset_quantity.hpp @@ -19,7 +19,6 @@ #define IROHA_ADD_ASSET_QUANTITY_HPP #include -#include "amount/amount.hpp" #include "model/command.hpp" namespace iroha { @@ -38,13 +37,13 @@ namespace iroha { /** * Amount to add to account asset */ - Amount amount; + std::string amount; bool operator==(const Command &command) const override; AddAssetQuantity() {} - AddAssetQuantity(const std::string &asset_id, Amount amount) + AddAssetQuantity(const std::string &asset_id, const std::string &amount) : asset_id(asset_id), amount(amount) {} }; } // namespace model diff --git a/irohad/model/commands/subtract_asset_quantity.hpp b/irohad/model/commands/subtract_asset_quantity.hpp index c7cde162c1..8aec619e00 100644 --- a/irohad/model/commands/subtract_asset_quantity.hpp +++ b/irohad/model/commands/subtract_asset_quantity.hpp @@ -18,7 +18,6 @@ #define IROHA_SUBTRACT_ASSET_QUANTITY_HPP #include -#include "amount/amount.hpp" #include "model/command.hpp" namespace iroha { @@ -37,13 +36,14 @@ namespace iroha { /** * Amount to add to account asset */ - Amount amount; + std::string amount; bool operator==(const Command &command) const override; SubtractAssetQuantity() {} - SubtractAssetQuantity(const std::string &asset_id, Amount amount) + SubtractAssetQuantity(const std::string &asset_id, + const std::string &amount) : asset_id(asset_id), amount(amount) {} }; } // namespace model diff --git a/irohad/model/commands/transfer_asset.hpp b/irohad/model/commands/transfer_asset.hpp index 2d06653c3d..38683b9377 100644 --- a/irohad/model/commands/transfer_asset.hpp +++ b/irohad/model/commands/transfer_asset.hpp @@ -21,7 +21,6 @@ #include #include "common/types.hpp" #include "model/command.hpp" -#include "amount/amount.hpp" namespace iroha { namespace model { @@ -53,7 +52,7 @@ namespace iroha { /** * Amount of transferred asset */ - Amount amount; + std::string amount; bool operator==(const Command &command) const override; @@ -62,7 +61,7 @@ namespace iroha { TransferAsset(const std::string &src_account_id, const std::string &dest_account_id, const std::string &asset_id, - const Amount &amount) + const std::string &amount) : src_account_id(src_account_id), dest_account_id(dest_account_id), asset_id(asset_id), diff --git a/irohad/model/converters/impl/json_command_factory.cpp b/irohad/model/converters/impl/json_command_factory.cpp index 7b41f81bbc..70fd48aadb 100644 --- a/irohad/model/converters/impl/json_command_factory.cpp +++ b/irohad/model/converters/impl/json_command_factory.cpp @@ -37,38 +37,10 @@ #include "model/commands/transfer_asset.hpp" using namespace rapidjson; -using namespace boost::multiprecision; namespace iroha { namespace model { namespace converters { - template <> - struct Convert { - template - boost::optional operator()(T &&x) { - auto des = makeFieldDeserializer(x); - auto str_int_value = des.String("value"); - - if (not str_int_value) { - return boost::none; - } - - // check if value is actually number - std::regex e("\\d+"); - if (not std::regex_match(str_int_value.value(), e)) { - return boost::none; - } - - uint256_t value(str_int_value.value()); - uint8_t precision; - rapidjson::Document dd; - precision = des.document["precision"].GetUint(); - - return boost::make_optional( - Amount(value, static_cast(precision))); - } - }; - template <> struct Convert { template @@ -152,15 +124,7 @@ namespace iroha { document.SetObject(); document.AddMember("command_type", "AddAssetQuantity", allocator); document.AddMember("asset_id", add_asset_quantity->asset_id, allocator); - - Value amount; - amount.SetObject(); - amount.AddMember( - "value", add_asset_quantity->amount.getIntValue().str(), allocator); - amount.AddMember( - "precision", add_asset_quantity->amount.getPrecision(), allocator); - - document.AddMember("amount", amount, allocator); + document.AddMember("amount", add_asset_quantity->amount, allocator); return document; } @@ -170,7 +134,7 @@ namespace iroha { auto des = makeFieldDeserializer(document); return make_optional_ptr() | des.String(&AddAssetQuantity::asset_id, "asset_id") - | des.Object(&AddAssetQuantity::amount, "amount") | toCommand; + | des.String(&AddAssetQuantity::amount, "amount") | toCommand; } // AddPeer @@ -400,15 +364,7 @@ namespace iroha { document.AddMember("asset_id", transfer_asset->asset_id, allocator); document.AddMember( "description", transfer_asset->description, allocator); - - Value amount; - amount.SetObject(); - amount.AddMember( - "value", transfer_asset->amount.getIntValue().str(), allocator); - amount.AddMember( - "precision", transfer_asset->amount.getPrecision(), allocator); - - document.AddMember("amount", amount, allocator); + document.AddMember("amount", transfer_asset->amount, allocator); return document; } @@ -421,7 +377,7 @@ namespace iroha { | des.String(&TransferAsset::dest_account_id, "dest_account_id") | des.String(&TransferAsset::asset_id, "asset_id") | des.String(&TransferAsset::description, "description") - | des.Object(&TransferAsset::amount, "amount") | toCommand; + | des.String(&TransferAsset::amount, "amount") | toCommand; } rapidjson::Document JsonCommandFactory::serializeAppendRole( @@ -564,17 +520,8 @@ namespace iroha { document.AddMember("command_type", "SubtractAssetQuantity", allocator); document.AddMember( "asset_id", subtract_asset_quantity->asset_id, allocator); - - Value amount; - amount.SetObject(); - amount.AddMember("value", - subtract_asset_quantity->amount.getIntValue().str(), - allocator); - amount.AddMember("precision", - subtract_asset_quantity->amount.getPrecision(), - allocator); - - document.AddMember("amount", amount, allocator); + document.AddMember( + "amount", subtract_asset_quantity->amount, allocator); return document; } @@ -585,7 +532,7 @@ namespace iroha { auto des = makeFieldDeserializer(document); return make_optional_ptr() | des.String(&SubtractAssetQuantity::asset_id, "asset_id") - | des.Object(&SubtractAssetQuantity::amount, "amount") | toCommand; + | des.String(&SubtractAssetQuantity::amount, "amount") | toCommand; } // Abstract diff --git a/irohad/model/converters/impl/pb_command_factory.cpp b/irohad/model/converters/impl/pb_command_factory.cpp index 2b74db1e30..0bb769941d 100644 --- a/irohad/model/converters/impl/pb_command_factory.cpp +++ b/irohad/model/converters/impl/pb_command_factory.cpp @@ -160,8 +160,7 @@ namespace iroha { const model::AddAssetQuantity &add_asset_quantity) { protocol::AddAssetQuantity pb_add_asset_quantity; pb_add_asset_quantity.set_asset_id(add_asset_quantity.asset_id); - auto amount = pb_add_asset_quantity.mutable_amount(); - amount->CopyFrom(serializeAmount(add_asset_quantity.amount)); + pb_add_asset_quantity.set_amount(add_asset_quantity.amount); return pb_add_asset_quantity; } @@ -169,8 +168,7 @@ namespace iroha { const protocol::AddAssetQuantity &pb_add_asset_quantity) { model::AddAssetQuantity add_asset_quantity; add_asset_quantity.asset_id = pb_add_asset_quantity.asset_id(); - add_asset_quantity.amount = - deserializeAmount(pb_add_asset_quantity.amount()); + add_asset_quantity.amount = pb_add_asset_quantity.amount(); return add_asset_quantity; } @@ -182,8 +180,7 @@ namespace iroha { protocol::SubtractAssetQuantity pb_subtract_asset_quantity; pb_subtract_asset_quantity.set_asset_id( subtract_asset_quantity.asset_id); - auto amount = pb_subtract_asset_quantity.mutable_amount(); - amount->CopyFrom(serializeAmount(subtract_asset_quantity.amount)); + pb_subtract_asset_quantity.set_amount(subtract_asset_quantity.amount); return pb_subtract_asset_quantity; } @@ -193,8 +190,7 @@ namespace iroha { model::SubtractAssetQuantity subtract_asset_quantity; subtract_asset_quantity.asset_id = pb_subtract_asset_quantity.asset_id(); - subtract_asset_quantity.amount = - deserializeAmount(pb_subtract_asset_quantity.amount()); + subtract_asset_quantity.amount = pb_subtract_asset_quantity.amount(); return subtract_asset_quantity; } @@ -337,8 +333,7 @@ namespace iroha { pb_transfer_asset.set_dest_account_id(transfer_asset.dest_account_id); pb_transfer_asset.set_asset_id(transfer_asset.asset_id); pb_transfer_asset.set_description(transfer_asset.description); - auto amount = pb_transfer_asset.mutable_amount(); - amount->CopyFrom(serializeAmount(transfer_asset.amount)); + pb_transfer_asset.set_amount(transfer_asset.amount); return pb_transfer_asset; } @@ -351,8 +346,7 @@ namespace iroha { pb_subtract_asset_quantity.dest_account_id(); transfer_asset.asset_id = pb_subtract_asset_quantity.asset_id(); transfer_asset.description = pb_subtract_asset_quantity.description(); - transfer_asset.amount = - deserializeAmount(pb_subtract_asset_quantity.amount()); + transfer_asset.amount = pb_subtract_asset_quantity.amount(); return transfer_asset; } diff --git a/irohad/model/converters/impl/pb_common.cpp b/irohad/model/converters/impl/pb_common.cpp index 80e7bef4ad..ca61c85720 100644 --- a/irohad/model/converters/impl/pb_common.cpp +++ b/irohad/model/converters/impl/pb_common.cpp @@ -25,27 +25,6 @@ namespace iroha { namespace model { namespace converters { - protocol::Amount serializeAmount(iroha::Amount iroha_amount) { - protocol::Amount res; - res.set_precision(iroha_amount.getPrecision()); - auto value = res.mutable_value(); - auto vectorUint64s = iroha_amount.to_uint64s(); - value->set_first(vectorUint64s.at(0)); - value->set_second(vectorUint64s.at(1)); - value->set_third(vectorUint64s.at(2)); - value->set_fourth(vectorUint64s.at(3)); - return res; - } - - iroha::Amount deserializeAmount(protocol::Amount pb_amount) { - auto value = pb_amount.value(); - return {value.first(), - value.second(), - value.third(), - value.fourth(), - static_cast(pb_amount.precision())}; - } - protocol::Peer serializePeer(iroha::model::Peer iroha_peer) { protocol::Peer res; res.set_address(iroha_peer.address); @@ -85,8 +64,7 @@ namespace iroha { iroha::protocol::AccountAsset pb_account_asset{}; pb_account_asset.set_account_id(account_asset.account_id); pb_account_asset.set_asset_id(account_asset.asset_id); - pb_account_asset.set_allocated_balance( - new protocol::Amount(serializeAmount(account_asset.balance))); + pb_account_asset.set_balance(account_asset.balance); return pb_account_asset; } diff --git a/irohad/model/converters/impl/pb_query_response_factory.cpp b/irohad/model/converters/impl/pb_query_response_factory.cpp index e096cfd748..e79428ef46 100644 --- a/irohad/model/converters/impl/pb_query_response_factory.cpp +++ b/irohad/model/converters/impl/pb_query_response_factory.cpp @@ -168,8 +168,7 @@ namespace iroha { protocol::AccountAsset pb_account_asset; pb_account_asset.set_account_id(account_asset.account_id); pb_account_asset.set_asset_id(account_asset.asset_id); - auto pb_balance = pb_account_asset.mutable_balance(); - pb_balance->CopyFrom(serializeAmount(account_asset.balance)); + pb_account_asset.set_balance(account_asset.balance); return pb_account_asset; } @@ -177,7 +176,7 @@ namespace iroha { const protocol::AccountAsset &account_asset) const { model::AccountAsset res; res.account_id = account_asset.account_id(); - res.balance = deserializeAmount(account_asset.balance()); + res.balance = account_asset.balance(); res.asset_id = account_asset.asset_id(); return res; } @@ -187,15 +186,11 @@ namespace iroha { const model::AccountAssetResponse &accountAssetResponse) const { protocol::AccountAssetResponse pb_response; auto pb_account_asset = pb_response.mutable_account_assets(); - for (auto &asset: accountAssetResponse.acct_assets) { + for (auto &asset : accountAssetResponse.acct_assets) { auto pb_asset = new iroha::protocol::AccountAsset(); - pb_asset->set_asset_id( - asset.asset_id); - pb_asset->set_account_id( - asset.account_id); - auto pb_amount = pb_asset->mutable_balance(); - pb_amount->CopyFrom( - serializeAmount(asset.balance)); + pb_asset->set_asset_id(asset.asset_id); + pb_asset->set_account_id(asset.account_id); + pb_asset->set_balance(asset.balance); pb_account_asset->AddAllocated(pb_asset); } return pb_response; @@ -205,10 +200,11 @@ namespace iroha { PbQueryResponseFactory::deserializeAccountAssetResponse( const protocol::AccountAssetResponse &account_asset_response) const { model::AccountAssetResponse res; - for (int i = 0; i < account_asset_response.account_assets().size(); i++) { + for (int i = 0; i < account_asset_response.account_assets().size(); + i++) { auto model_asset = iroha::model::AccountAsset(); - model_asset.balance = deserializeAmount( - account_asset_response.account_assets(i).balance()); + model_asset.balance = + account_asset_response.account_assets(i).balance(); model_asset.account_id = account_asset_response.account_assets(i).account_id(); model_asset.asset_id = diff --git a/irohad/model/converters/pb_common.hpp b/irohad/model/converters/pb_common.hpp index 2c6ac31f3a..3d1e06646e 100644 --- a/irohad/model/converters/pb_common.hpp +++ b/irohad/model/converters/pb_common.hpp @@ -18,7 +18,6 @@ #ifndef IROHA_PB_COMMON_HPP #define IROHA_PB_COMMON_HPP -#include "amount/amount.hpp" #include "commands.pb.h" #include "common/types.hpp" #include "cryptography/ed25519_sha3_impl/internal/sha3_hash.hpp" @@ -33,10 +32,6 @@ namespace iroha { namespace model { namespace converters { - // amount - protocol::Amount serializeAmount(iroha::Amount iroha_amount); - iroha::Amount deserializeAmount(protocol::Amount pb_amount); - // peer protocol::Peer serializePeer(iroha::model::Peer iroha_peer); iroha::model::Peer deserializePeer(protocol::Peer pb_peer); diff --git a/irohad/model/generators/command_generator.hpp b/irohad/model/generators/command_generator.hpp index c00b0935cb..23f5f50dbe 100644 --- a/irohad/model/generators/command_generator.hpp +++ b/irohad/model/generators/command_generator.hpp @@ -19,13 +19,9 @@ #define IROHA_COMMAND_GENERATOR_HPP #include -#include "amount/amount.hpp" #include "generator/generator.hpp" namespace iroha { - - class Amount; - namespace model { struct Peer; @@ -73,10 +69,10 @@ namespace iroha { const std::string &account_id, uint32_t quorum); std::shared_ptr generateAddAssetQuantity( - const std::string &asset_id, const Amount &amount); + const std::string &asset_id, const std::string &amount); std::shared_ptr generateSubtractAssetQuantity( - const std::string &asset_id, const Amount &amount); + const std::string &asset_id, const std::string &amount); /** * Generate transfer assets from source account_id to target account_id * @param src_account_id - source account identifier @@ -89,7 +85,7 @@ namespace iroha { const std::string &src_account_id, const std::string &target_account_id, const std::string &asset_id, - const Amount &amount); + const std::string &amount); std::shared_ptr generateAppendRole( const std::string &account_id, const std::string &role_name); diff --git a/irohad/model/generators/impl/command_generator.cpp b/irohad/model/generators/impl/command_generator.cpp index 70a7220fc6..9600550856 100644 --- a/irohad/model/generators/impl/command_generator.cpp +++ b/irohad/model/generators/impl/command_generator.cpp @@ -101,12 +101,12 @@ namespace iroha { } std::shared_ptr CommandGenerator::generateAddAssetQuantity( - const std::string &asset_id, const Amount &amount) { + const std::string &asset_id, const std::string &amount) { return generateCommand(asset_id, amount); } std::shared_ptr CommandGenerator::generateSubtractAssetQuantity( - const std::string &asset_id, const Amount &amount) { + const std::string &asset_id, const std::string &amount) { return generateCommand(asset_id, amount); } @@ -119,7 +119,7 @@ namespace iroha { const std::string &src_account, const std::string &dest_account, const std::string &asset_id, - const Amount &amount) { + const std::string &amount) { return generateCommand( src_account, dest_account, asset_id, amount); } diff --git a/libs/CMakeLists.txt b/libs/CMakeLists.txt index e82607000a..bb73fbdb4d 100644 --- a/libs/CMakeLists.txt +++ b/libs/CMakeLists.txt @@ -1,4 +1,8 @@ -add_subdirectory(amount) +# +# Copyright Soramitsu Co., Ltd. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +# + add_subdirectory(common) add_subdirectory(crypto) add_subdirectory(logger) diff --git a/libs/amount/CMakeLists.txt b/libs/amount/CMakeLists.txt deleted file mode 100644 index 1abbd107a9..0000000000 --- a/libs/amount/CMakeLists.txt +++ /dev/null @@ -1,24 +0,0 @@ -# -# Copyright Soramitsu Co., Ltd. 2017 All Rights Reserved. -# http://soramitsu.co.jp -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -add_library(iroha_amount - amount.cpp - ) - -target_link_libraries(iroha_amount - boost - ) diff --git a/libs/amount/amount.cpp b/libs/amount/amount.cpp deleted file mode 100644 index e27644a26d..0000000000 --- a/libs/amount/amount.cpp +++ /dev/null @@ -1,217 +0,0 @@ -/** - * Copyright Soramitsu Co., Ltd. 2017 All Rights Reserved. - * http://soramitsu.co.jp - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "amount/amount.hpp" - -#include -#include - -using namespace boost::multiprecision; - -namespace iroha { - - // to raise to power integer values - static int ipow(int base, int exp) { - int result = 1; - while (exp != 0) { - if (exp & 1) { - result *= base; - } - exp >>= 1; - base *= base; - } - - return result; - } - - static uint256_t getJointUint256(uint64_t first, - uint64_t second, - uint64_t third, - uint64_t fourth) { - // join 4 uint64_t into single uint256_t by means of logic or operator - uint256_t res(0); - res |= first; - res <<= 64; - res |= second; - res <<= 64; - res |= third; - res <<= 64; - res |= fourth; - return res; - } - - Amount::Amount() {} - - Amount::Amount(uint256_t value) : value_(value) {} - - Amount::Amount(uint256_t amount, uint8_t precision) - : value_(amount), precision_(precision) {} - - Amount::Amount(uint64_t first, - uint64_t second, - uint64_t third, - uint64_t fourth) - : Amount(first, second, third, fourth, 0) {} - - Amount::Amount(uint64_t first, - uint64_t second, - uint64_t third, - uint64_t fourth, - uint8_t precision) - : precision_(precision) { - value_ = getJointUint256(first, second, third, fourth); - } - - Amount::Amount(const Amount &am) - : value_(am.value_), precision_(am.precision_) {} - - Amount &Amount::operator=(const Amount &other) { - // check for self-assignment - if (&other == this) - return *this; - value_ = other.value_; - precision_ = other.precision_; - return *this; - } - - Amount::Amount(Amount &&am) : value_(am.value_), precision_(am.precision_) {} - - Amount &Amount::operator=(Amount &&other) { - std::swap(value_, other.value_); - std::swap(precision_, other.precision_); - return *this; - } - - boost::optional Amount::createFromString(std::string str_amount) { - // check if valid number - std::regex e("([0-9]*\\.[0-9]+|[0-9]+)"); - if (!std::regex_match(str_amount, e)) { - return boost::none; - } - - // get precision - auto dot_place = str_amount.find('.'); - size_t precision; - if (dot_place > str_amount.size()) { - precision = 0; - } else { - precision = str_amount.size() - dot_place - 1; - // erase dot from the string - str_amount.erase(std::remove(str_amount.begin(), str_amount.end(), '.'), - str_amount.end()); - } - - auto begin = str_amount.find_first_not_of('0'); - - // create uint256 value from obtained string - uint256_t value = 0; - if (begin <= str_amount.size()) { - value = uint256_t(str_amount.substr(begin)); - } - return Amount(value, precision); - } - - uint256_t Amount::getIntValue() { - return value_; - } - - uint8_t Amount::getPrecision() { - return precision_; - } - - std::vector Amount::to_uint64s() { - std::vector array(4); - for (int i = 0; i < 4; i++) { - constexpr boost::multiprecision::uint256_t mask_bits = - std::numeric_limits::max(); - uint64_t res = ((value_ >> (i * 64)) & mask_bits).convert_to(); - array[3 - i] = res; - } - return array; - } - - Amount Amount::percentage(uint256_t percents) const { - uint256_t new_val = value_ * percents / 100; - return {new_val, precision_}; - } - - Amount Amount::percentage(const Amount &am) const { - // multiply two amount values - uint256_t new_value = value_ * am.value_; - - // new value should be decreased by the scale of am to move floating point - // to the left, as it is done when we multiply manually - new_value /= uint256_t(pow(10, am.precision_)); - // to take percentage value we need divide by 100 - new_value /= 100; - return {new_value, precision_}; - } - - Amount Amount::add(const Amount &other) const { - auto new_val = value_ + other.value_; - return {new_val, precision_}; - } - - Amount Amount::subtract(const Amount &other) const { - auto new_val = value_ - other.value_; - return {new_val, precision_}; - } - - int Amount::compareTo(const Amount &other) const { - if (precision_ == other.precision_) { - return (value_ < other.value_) ? -1 : (value_ > other.value_) ? 1 : 0; - } - // when different precisions transform to have the same scale - auto max_precision = std::max(precision_, other.precision_); - auto val1 = value_ * ipow(10, max_precision - precision_); - auto val2 = other.value_ * ipow(10, max_precision - other.precision_); - return (val1 < val2) ? -1 : (val1 > val2) ? 1 : 0; - } - - bool Amount::operator==(const Amount &other) const { - return compareTo(other) == 0; - } - - bool Amount::operator!=(const Amount &other) const { - return compareTo(other) != 0; - } - - bool Amount::operator<(const Amount &other) const { - return compareTo(other) < 0; - } - - bool Amount::operator>(const Amount &other) const { - return compareTo(other) > 0; - } - - bool Amount::operator<=(const Amount &other) const { - return compareTo(other) <= 0; - } - - bool Amount::operator>=(const Amount &other) const { - return compareTo(other) >= 0; - } - - std::string Amount::to_string() const { - if (precision_ > 0) { - cpp_dec_float_50 float50(value_); - float50 /= pow(10, precision_); - return float50.str(precision_, std::ios_base::fixed); - } - return value_.str(0, std::ios_base::fixed); - } -} // namespace iroha diff --git a/libs/amount/amount.hpp b/libs/amount/amount.hpp deleted file mode 100644 index 3a472d1d9f..0000000000 --- a/libs/amount/amount.hpp +++ /dev/null @@ -1,180 +0,0 @@ -/** - * Copyright Soramitsu Co., Ltd. 2017 All Rights Reserved. - * http://soramitsu.co.jp - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#ifndef IROHA_AMOUNT_H -#define IROHA_AMOUNT_H - -#include -#include -#include -#include -#include -#include - -namespace iroha { - - /** - * Keeps integer and scale values allowing performing math - * operations on them - */ - class Amount { - public: - /** - * Creates Amount with integer = 0 and scale = 0 - */ - Amount(); - - /** - * Amount with integer = amount and scale = 0 - * @param amount integer part - */ - Amount(boost::multiprecision::uint256_t amount); - - /** - * Amount with provided integer and scale part - * @param amount integer part - * @param precision scale part - */ - Amount(boost::multiprecision::uint256_t amount, uint8_t precision); - - Amount(uint64_t first, uint64_t second, uint64_t third, uint64_t fourth); - - Amount(uint64_t first, - uint64_t second, - uint64_t third, - uint64_t fourth, - uint8_t precision); - - std::vector to_uint64s(); - - /** - * Copy constructor - */ - Amount(const Amount &); - Amount &operator=(const Amount &); - - /** - * Move constructor - */ - Amount(Amount &&); - Amount &operator=(Amount &&); - - boost::multiprecision::uint256_t getIntValue(); - uint8_t getPrecision(); - - static boost::optional createFromString(std::string str_amount); - - /** - * Takes percentage from current amount - * @param percents - * @return - */ - Amount percentage(boost::multiprecision::uint256_t percents) const; - - /** - * Takes percentage represented as amount value - * The current scale and scale of percents may differ - * @param percents - * @return - */ - Amount percentage(const Amount &percents) const; - - /** - * Sums up two optionals of the amounts. - * Requires to have the same scale. - * Otherwise nullopt is returned - * @param a left term - * @param b right term - * @param optional result - */ - friend boost::optional operator+(boost::optional a, - boost::optional b) { - // check precisions - if (a->precision_ != b->precision_) { - return boost::none; - } - auto res = a->add(*b); - // check overflow - if (res.value_ < a->value_ or res.value_ < b->value_) { - return boost::none; - } - return res; - } - - /** - * Subtracts right term from the left term - * Requires to have the same scale. - * Otherwise nullopt is returned - * @param a left term - * @param b right term - * @param optional result - */ - friend boost::optional operator-(boost::optional a, - boost::optional b) { - // check precisions - if (a->precision_ != b->precision_) { - return boost::none; - } - // check if a greater than b - if (a->value_ < b->value_) { - return boost::none; - } - return a->subtract(*b); - } - - /** - * Comparisons are possible between amounts with different precisions. - * - * @return - */ - bool operator==(const Amount &) const; - bool operator!=(const Amount &) const; - bool operator<(const Amount &) const; - bool operator>(const Amount &) const; - bool operator<=(const Amount &) const; - bool operator>=(const Amount &) const; - - std::string to_string() const; - ~Amount() = default; - - private: - /** - * Support function for comparison operators. - * Returns 0 when equal, -1 when current Amount smaller, and 1 when it is - * greater - * @param other - * @return - */ - int compareTo(const Amount &other) const; - - /** - * Sums two amounts. - * @return - */ - Amount add(const Amount &) const; - /** - * Subtracts one amount from another. - * Requires to have the same scale between both amounts. - * Otherwise nullopt is returned - * @return - */ - Amount subtract(const Amount &) const; - - boost::multiprecision::uint256_t value_{0}; - uint8_t precision_{0}; - }; -} // namespace iroha -#endif // IROHA_AMOUNT_H diff --git a/shared_model/backend/protobuf/CMakeLists.txt b/shared_model/backend/protobuf/CMakeLists.txt index 5dee8f6609..ffafd6243f 100644 --- a/shared_model/backend/protobuf/CMakeLists.txt +++ b/shared_model/backend/protobuf/CMakeLists.txt @@ -60,5 +60,4 @@ endif () target_link_libraries(shared_model_proto_backend schema shared_model_interfaces - iroha_amount ) diff --git a/shared_model/backend/protobuf/commands/impl/proto_add_asset_quantity.cpp b/shared_model/backend/protobuf/commands/impl/proto_add_asset_quantity.cpp index f8fa221292..e3ec072c42 100644 --- a/shared_model/backend/protobuf/commands/impl/proto_add_asset_quantity.cpp +++ b/shared_model/backend/protobuf/commands/impl/proto_add_asset_quantity.cpp @@ -12,8 +12,9 @@ namespace shared_model { AddAssetQuantity::AddAssetQuantity(CommandType &&command) : CopyableProto(std::forward(command)), add_asset_quantity_{proto_->add_asset_quantity()}, - amount_{ - [this] { return proto::Amount(add_asset_quantity_.amount()); }} {} + amount_{[this] { + return interface::Amount(add_asset_quantity_.amount()); + }} {} // TODO 30/05/2018 andrei Reduce boilerplate code in variant classes template AddAssetQuantity::AddAssetQuantity( diff --git a/shared_model/backend/protobuf/commands/impl/proto_subtract_asset_quantity.cpp b/shared_model/backend/protobuf/commands/impl/proto_subtract_asset_quantity.cpp index 23906c79b6..c94264b36b 100644 --- a/shared_model/backend/protobuf/commands/impl/proto_subtract_asset_quantity.cpp +++ b/shared_model/backend/protobuf/commands/impl/proto_subtract_asset_quantity.cpp @@ -13,7 +13,7 @@ namespace shared_model { : CopyableProto(std::forward(command)), subtract_asset_quantity_{proto_->subtract_asset_quantity()}, amount_{[this] { - return proto::Amount(subtract_asset_quantity_.amount()); + return interface::Amount(subtract_asset_quantity_.amount()); }} {} template SubtractAssetQuantity::SubtractAssetQuantity( diff --git a/shared_model/backend/protobuf/commands/impl/proto_transfer_asset.cpp b/shared_model/backend/protobuf/commands/impl/proto_transfer_asset.cpp index 308a824c2a..c4c45cb7f9 100644 --- a/shared_model/backend/protobuf/commands/impl/proto_transfer_asset.cpp +++ b/shared_model/backend/protobuf/commands/impl/proto_transfer_asset.cpp @@ -12,7 +12,8 @@ namespace shared_model { TransferAsset::TransferAsset(CommandType &&command) : CopyableProto(std::forward(command)), transfer_asset_{proto_->transfer_asset()}, - amount_{[this] { return proto::Amount(transfer_asset_.amount()); }} {} + amount_{ + [this] { return interface::Amount(transfer_asset_.amount()); }} {} template TransferAsset::TransferAsset(TransferAsset::TransportType &); template TransferAsset::TransferAsset(const TransferAsset::TransportType &); diff --git a/shared_model/backend/protobuf/commands/proto_add_asset_quantity.hpp b/shared_model/backend/protobuf/commands/proto_add_asset_quantity.hpp index 88b4733388..fceb3996a5 100644 --- a/shared_model/backend/protobuf/commands/proto_add_asset_quantity.hpp +++ b/shared_model/backend/protobuf/commands/proto_add_asset_quantity.hpp @@ -20,9 +20,9 @@ #include "interfaces/commands/add_asset_quantity.hpp" -#include "backend/protobuf/common_objects/amount.hpp" #include "backend/protobuf/common_objects/trivial_proto.hpp" #include "commands.pb.h" +#include "interfaces/common_objects/amount.hpp" #include "utils/lazy_initializer.hpp" namespace shared_model { @@ -50,7 +50,7 @@ namespace shared_model { const iroha::protocol::AddAssetQuantity &add_asset_quantity_; - const Lazy amount_; + const Lazy amount_; }; } // namespace proto diff --git a/shared_model/backend/protobuf/commands/proto_subtract_asset_quantity.hpp b/shared_model/backend/protobuf/commands/proto_subtract_asset_quantity.hpp index eee21947b0..1ab529e42f 100644 --- a/shared_model/backend/protobuf/commands/proto_subtract_asset_quantity.hpp +++ b/shared_model/backend/protobuf/commands/proto_subtract_asset_quantity.hpp @@ -20,9 +20,9 @@ #include "interfaces/commands/subtract_asset_quantity.hpp" -#include "backend/protobuf/common_objects/amount.hpp" #include "backend/protobuf/common_objects/trivial_proto.hpp" #include "commands.pb.h" +#include "interfaces/common_objects/amount.hpp" #include "utils/lazy_initializer.hpp" namespace shared_model { @@ -50,7 +50,7 @@ namespace shared_model { const iroha::protocol::SubtractAssetQuantity &subtract_asset_quantity_; - const Lazy amount_; + const Lazy amount_; }; } // namespace proto diff --git a/shared_model/backend/protobuf/commands/proto_transfer_asset.hpp b/shared_model/backend/protobuf/commands/proto_transfer_asset.hpp index 1274e8727a..da5b7bda2f 100644 --- a/shared_model/backend/protobuf/commands/proto_transfer_asset.hpp +++ b/shared_model/backend/protobuf/commands/proto_transfer_asset.hpp @@ -20,8 +20,8 @@ #include "backend/protobuf/common_objects/trivial_proto.hpp" #include "commands.pb.h" -#include "backend/protobuf/common_objects/amount.hpp" #include "interfaces/commands/transfer_asset.hpp" +#include "interfaces/common_objects/amount.hpp" namespace shared_model { namespace proto { @@ -54,7 +54,7 @@ namespace shared_model { const iroha::protocol::TransferAsset &transfer_asset_; - const Lazy amount_; + const Lazy amount_; }; } // namespace proto diff --git a/shared_model/backend/protobuf/common_objects/account_asset.hpp b/shared_model/backend/protobuf/common_objects/account_asset.hpp index ac23d93c80..5537fc6d93 100644 --- a/shared_model/backend/protobuf/common_objects/account_asset.hpp +++ b/shared_model/backend/protobuf/common_objects/account_asset.hpp @@ -18,7 +18,6 @@ #ifndef IROHA_PROTO_ACCOUNT_ASSET_HPP #define IROHA_PROTO_ACCOUNT_ASSET_HPP -#include "backend/protobuf/common_objects/amount.hpp" #include "backend/protobuf/common_objects/trivial_proto.hpp" #include "backend/protobuf/util.hpp" #include "interfaces/common_objects/account_asset.hpp" @@ -57,7 +56,8 @@ namespace shared_model { template using Lazy = detail::LazyInitializer; - const Lazy balance_{[this] { return Amount(proto_->balance()); }}; + const Lazy balance_{ + [this] { return interface::Amount(proto_->balance()); }}; }; } // namespace proto } // namespace shared_model diff --git a/shared_model/backend/protobuf/common_objects/amount.hpp b/shared_model/backend/protobuf/common_objects/amount.hpp deleted file mode 100644 index 0817a2000c..0000000000 --- a/shared_model/backend/protobuf/common_objects/amount.hpp +++ /dev/null @@ -1,108 +0,0 @@ -/** - * Copyright Soramitsu Co., Ltd. 2017 All Rights Reserved. - * http://soramitsu.co.jp - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef IROHA_PROTO_AMOUNT_HPP -#define IROHA_PROTO_AMOUNT_HPP - -#include "interfaces/common_objects/amount.hpp" - -#include - -#include "backend/protobuf/common_objects/trivial_proto.hpp" -#include "backend/protobuf/util.hpp" -#include "primitive.pb.h" -#include "utils/lazy_initializer.hpp" - -namespace shared_model { - namespace proto { - - /** - * converts protobuf amount to uint256_t, assuming big-endian order. - * @param amount - protobuf object which is assumed to have 4 values - * @return uint256_t representation of proto object - */ - template - boost::multiprecision::uint256_t convertToUInt256( - const AmountType &amount) noexcept { - using boost::multiprecision::uint256_t; - constexpr auto offset = 64u; - uint256_t result; - result |= uint256_t{amount.first()} << offset * 3; - result |= uint256_t{amount.second()} << offset * 2; - result |= uint256_t{amount.third()} << offset; - result |= uint256_t{amount.fourth()}; - return result; - } - - /** - * Sets protobuf value to specified uint256_t. - * @param value - protobuf value, which will be changed - * @param amount - integer value - */ - template - void convertToProtoAmount( - ValueType &value, - const boost::multiprecision::uint256_t &amount) noexcept { - constexpr auto offset = 64u; - constexpr boost::multiprecision::uint256_t mask_bits = - std::numeric_limits::max(); - auto convert = [&](auto i) { - // Select two middle bits from 011011 and offset = 2 - // 011011 >> (2 * 1) = 000110 - // Have to mask 2 high bits to prevent any overflows - // 000110 & 000011 = 000010 - return ((amount >> (offset * i)) & mask_bits) - .template convert_to(); - }; - value.set_first(convert(3)); - value.set_second(convert(2)); - value.set_third(convert(1)); - value.set_fourth(convert(0)); - } - - class Amount final : public CopyableProto { - public: - template - explicit Amount(AmountType &&amount) - : CopyableProto(std::forward(amount)), - multiprecision_repr_( - [this] { return convertToUInt256(proto_->value()); }) {} - - Amount(const Amount &o) : Amount(o.proto_) {} - - Amount(Amount &&o) noexcept : Amount(std::move(o.proto_)) {} - - const boost::multiprecision::uint256_t &intValue() const override { - return *multiprecision_repr_; - } - - interface::types::PrecisionType precision() const override { - return proto_->precision(); - } - - private: - // lazy - template - using Lazy = detail::LazyInitializer; - - const Lazy multiprecision_repr_; - }; - } // namespace proto -} // namespace shared_model -#endif // IROHA_PROTO_AMOUNT_HPP diff --git a/shared_model/bindings/CMakeLists.txt b/shared_model/bindings/CMakeLists.txt index ef5fc7ffbe..98e7fedb10 100644 --- a/shared_model/bindings/CMakeLists.txt +++ b/shared_model/bindings/CMakeLists.txt @@ -25,7 +25,6 @@ target_link_libraries(bindings shared_model_cryptography generator shared_model_stateless_validation - iroha_amount ) diff --git a/shared_model/builders/common_objects/amount_builder.hpp b/shared_model/builders/common_objects/amount_builder.hpp deleted file mode 100644 index 4184d39d95..0000000000 --- a/shared_model/builders/common_objects/amount_builder.hpp +++ /dev/null @@ -1,115 +0,0 @@ -/** - * Copyright Soramitsu Co., Ltd. 2018 All Rights Reserved. - * http://soramitsu.co.jp - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef IROHA_AMOUNT_BUILDER_HPP -#define IROHA_AMOUNT_BUILDER_HPP - -#include - -#include "builders/common_objects/common.hpp" -#include "interfaces/common_objects/amount.hpp" - -// TODO: 14.02.2018 nickaleks Add check for uninitialized fields IR-972 - -namespace shared_model { - namespace builder { - - /** - * AmountBuilder is a class, used for construction of Amount objects - * @tparam BuilderImpl is a type, which defines builder for implementation - * of shared_model. Since we return abstract classes, it is necessary for - * them to be instantiated with some concrete implementation - * @tparam Validator is a type, whose responsibility is - * to perform stateless validation on model fields - */ - template - class DEPRECATED AmountBuilder - : public CommonObjectBuilder { - public: - AmountBuilder intValue(const boost::multiprecision::uint256_t &value) { - AmountBuilder copy(*this); - copy.builder_ = this->builder_.intValue(value); - return copy; - } - - AmountBuilder precision( - const interface::types::PrecisionType &precision) { - AmountBuilder copy(*this); - copy.builder_ = this->builder_.precision(precision); - return copy; - } - - /** - * Constructs Amount object from given string - * @param str_amount string in format "100.00", - * where there are exactly precision numbers after dot - * @return Amount constructed from string - */ - static BuilderResult fromString( - std::string str_amount) { - // taken from iroha::model::Amount - // check if valid number - std::regex e("([0-9]*\\.[0-9]+|[0-9]+)"); - if (!std::regex_match(str_amount, e)) { - return iroha::expected::makeError( - std::make_shared("number string is invalid")); - } - - // get precision - auto dot_place = str_amount.find('.'); - size_t precision; - if (dot_place > str_amount.size()) { - precision = 0; - } else { - precision = str_amount.size() - dot_place - 1; - // erase dot from the string - str_amount.erase( - std::remove(str_amount.begin(), str_amount.end(), '.'), - str_amount.end()); - } - - auto begin = str_amount.find_first_not_of('0'); - - // create uint256 value from obtained string - boost::multiprecision::uint256_t value = 0; - if (begin <= str_amount.size()) { - value = boost::multiprecision::uint256_t(str_amount.substr(begin)); - } - - AmountBuilder builder; - return builder.intValue(value).precision(precision).build(); - } - - protected: - virtual std::string builderName() const override { - return "Amount Builder"; - } - - virtual validation::ReasonsGroupType validate( - const interface::Amount &object) override { - validation::ReasonsGroupType reasons; - this->validator_.validateAmount(reasons, object); - - return reasons; - } - }; - } // namespace builder -} // namespace shared_model - -#endif // IROHA_AMOUNT_BUILDER_HPP diff --git a/shared_model/builders/default_builders.hpp b/shared_model/builders/default_builders.hpp index e19898c0a0..a61dc39433 100644 --- a/shared_model/builders/default_builders.hpp +++ b/shared_model/builders/default_builders.hpp @@ -20,14 +20,12 @@ #include "builders/common_objects/account_asset_builder.hpp" #include "builders/common_objects/account_builder.hpp" -#include "builders/common_objects/amount_builder.hpp" #include "builders/common_objects/asset_builder.hpp" #include "builders/common_objects/domain_builder.hpp" #include "builders/common_objects/peer_builder.hpp" #include "builders/common_objects/signature_builder.hpp" #include "builders/protobuf/common_objects/proto_account_asset_builder.hpp" #include "builders/protobuf/common_objects/proto_account_builder.hpp" -#include "builders/protobuf/common_objects/proto_amount_builder.hpp" #include "builders/protobuf/common_objects/proto_asset_builder.hpp" #include "builders/protobuf/common_objects/proto_domain_builder.hpp" #include "builders/protobuf/common_objects/proto_peer_builder.hpp" @@ -58,10 +56,6 @@ namespace shared_model { shared_model::proto::PeerBuilder, shared_model::validation::FieldValidator>; - using DefaultAmountBuilder = shared_model::builder::AmountBuilder< - shared_model::proto::AmountBuilder, - shared_model::validation::FieldValidator>; - using DefaultDomainBuilder = shared_model::builder::DomainBuilder< shared_model::proto::DomainBuilder, shared_model::validation::FieldValidator>; @@ -74,10 +68,6 @@ namespace shared_model { shared_model::proto::SignatureBuilder, shared_model::validation::FieldValidator>; - using AmountBuilderWithoutValidator = shared_model::builder::AmountBuilder< - shared_model::proto::AmountBuilder, - shared_model::validation::AmountTrueValidator>; - using DefaultBlockQueryResponseBuilder = shared_model::builder::BlockQueryResponseBuilder< shared_model::proto::BlockQueryResponseBuilder>; diff --git a/shared_model/builders/protobuf/builder_templates/query_response_template.hpp b/shared_model/builders/protobuf/builder_templates/query_response_template.hpp index ef10af79f1..7a90ff11c3 100644 --- a/shared_model/builders/protobuf/builder_templates/query_response_template.hpp +++ b/shared_model/builders/protobuf/builder_templates/query_response_template.hpp @@ -8,7 +8,6 @@ #include "backend/protobuf/permissions.hpp" #include "backend/protobuf/query_responses/proto_query_response.hpp" -#include "builders/protobuf/helpers.hpp" #include "common/visitor.hpp" #include "interfaces/common_objects/types.hpp" #include "interfaces/permissions.hpp" diff --git a/shared_model/builders/protobuf/builder_templates/transaction_template.hpp b/shared_model/builders/protobuf/builder_templates/transaction_template.hpp index 6ba054b7c7..815e393c36 100644 --- a/shared_model/builders/protobuf/builder_templates/transaction_template.hpp +++ b/shared_model/builders/protobuf/builder_templates/transaction_template.hpp @@ -26,9 +26,7 @@ #include "primitive.pb.h" #include "transaction.pb.h" -#include "amount/amount.hpp" #include "backend/protobuf/permissions.hpp" -#include "builders/protobuf/helpers.hpp" #include "builders/protobuf/unsigned_proto.hpp" #include "interfaces/common_objects/types.hpp" #include "interfaces/permissions.hpp" @@ -145,7 +143,7 @@ namespace shared_model { return addCommand([&](auto proto_command) { auto command = proto_command->mutable_add_asset_quantity(); command->set_asset_id(asset_id); - initializeProtobufAmount(command->mutable_amount(), amount); + command->set_amount(amount); }); } @@ -288,7 +286,7 @@ namespace shared_model { return addCommand([&](auto proto_command) { auto command = proto_command->mutable_subtract_asset_quantity(); command->set_asset_id(asset_id); - initializeProtobufAmount(command->mutable_amount(), amount); + command->set_amount(amount); }); } @@ -303,7 +301,7 @@ namespace shared_model { command->set_dest_account_id(dest_account_id); command->set_asset_id(asset_id); command->set_description(description); - initializeProtobufAmount(command->mutable_amount(), amount); + command->set_amount(amount); }); } diff --git a/shared_model/builders/protobuf/common_objects/proto_account_asset_builder.hpp b/shared_model/builders/protobuf/common_objects/proto_account_asset_builder.hpp index 93ac802ac6..9e2a07a482 100644 --- a/shared_model/builders/protobuf/common_objects/proto_account_asset_builder.hpp +++ b/shared_model/builders/protobuf/common_objects/proto_account_asset_builder.hpp @@ -50,9 +50,7 @@ namespace shared_model { AccountAssetBuilder balance(const interface::Amount &amount) { AccountAssetBuilder copy(*this); - auto *amount_proto = copy.account_asset_.mutable_balance(); - convertToProtoAmount(*amount_proto->mutable_value(), amount.intValue()); - amount_proto->set_precision(amount.precision()); + *copy.account_asset_.mutable_balance() = amount.toStringRepr(); return copy; } diff --git a/shared_model/builders/protobuf/common_objects/proto_amount_builder.hpp b/shared_model/builders/protobuf/common_objects/proto_amount_builder.hpp deleted file mode 100644 index 980d9b0037..0000000000 --- a/shared_model/builders/protobuf/common_objects/proto_amount_builder.hpp +++ /dev/null @@ -1,55 +0,0 @@ -/** - * Copyright Soramitsu Co., Ltd. 2018 All Rights Reserved. - * http://soramitsu.co.jp - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef IROHA_PROTO_AMOUNT_BUILDER_HPP -#define IROHA_PROTO_AMOUNT_BUILDER_HPP - -#include "backend/protobuf/common_objects/amount.hpp" -#include "primitive.pb.h" - -namespace shared_model { - namespace proto { - - /** - * AmountBuilder is used to construct Amount proto objects with initialized - * protobuf implementation - */ - class DEPRECATED AmountBuilder { - public: - shared_model::proto::Amount build() { - return shared_model::proto::Amount(iroha::protocol::Amount(amount_)); - } - - AmountBuilder intValue(const boost::multiprecision::uint256_t &value) { - AmountBuilder copy(*this); - convertToProtoAmount(*copy.amount_.mutable_value(), value); - return copy; - } - - AmountBuilder precision( - const interface::types::PrecisionType &precision) { - AmountBuilder copy(*this); - copy.amount_.set_precision(precision); - return copy; - } - - private: - iroha::protocol::Amount amount_; - }; - } // namespace proto -} // namespace shared_model -#endif // IROHA_PROTO_AMOUNT_BUILDER_HPP diff --git a/shared_model/builders/protobuf/helpers.hpp b/shared_model/builders/protobuf/helpers.hpp deleted file mode 100644 index 5f6d3dc31e..0000000000 --- a/shared_model/builders/protobuf/helpers.hpp +++ /dev/null @@ -1,46 +0,0 @@ -/** - * Copyright Soramitsu Co., Ltd. 2017 All Rights Reserved. - * http://soramitsu.co.jp - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef IROHA_PROTO_BUILDER_HELPER_HPP -#define IROHA_PROTO_BUILDER_HELPER_HPP - -#include "amount/amount.hpp" -#include "primitive.pb.h" - -namespace shared_model { - namespace proto { - /** - * Initialize protobuf Amout by text number representation - * @param proto is Amount that is being set - * @param str_amount is string representation of Amount - */ - static inline void initializeProtobufAmount(iroha::protocol::Amount *proto, - const std::string &str_amount) { - iroha::Amount::createFromString(str_amount) | [proto](auto &&amount) { - auto proto_value = proto->mutable_value(); - auto uint64s = amount.to_uint64s(); - proto_value->set_first(uint64s.at(0)); - proto_value->set_second(uint64s.at(1)); - proto_value->set_third(uint64s.at(2)); - proto_value->set_fourth(uint64s.at(3)); - proto->set_precision(amount.getPrecision()); - }; - } - } // namespace proto -} // namespace shared_model - -#endif // IROHA_PROTO_BUILDER_HELPER_HPP diff --git a/shared_model/interfaces/CMakeLists.txt b/shared_model/interfaces/CMakeLists.txt index 0b57c56ef4..9a3c8a8d42 100644 --- a/shared_model/interfaces/CMakeLists.txt +++ b/shared_model/interfaces/CMakeLists.txt @@ -36,6 +36,7 @@ add_library(shared_model_interfaces queries/impl/get_pending_transactions.cpp queries/impl/blocks_query.cpp queries/impl/query_payload_meta.cpp + common_objects/impl/amount.cpp ) if (IROHA_ROOT_PROJECT) diff --git a/shared_model/interfaces/common_objects/amount.hpp b/shared_model/interfaces/common_objects/amount.hpp index 5fd0d7cb0f..3d54da2b35 100644 --- a/shared_model/interfaces/common_objects/amount.hpp +++ b/shared_model/interfaces/common_objects/amount.hpp @@ -1,30 +1,16 @@ /** - * Copyright Soramitsu Co., Ltd. 2017 All Rights Reserved. - * http://soramitsu.co.jp - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 */ #ifndef IROHA_SHARED_MODEL_AMOUNT_HPP #define IROHA_SHARED_MODEL_AMOUNT_HPP -#include -#include -#include - #include "interfaces/base/model_primitive.hpp" + +#include #include "interfaces/common_objects/types.hpp" -#include "utils/string_builder.hpp" +#include "utils/lazy_initializer.hpp" namespace shared_model { namespace interface { @@ -32,53 +18,52 @@ namespace shared_model { /** * Representation of fixed point number */ - class Amount : public ModelPrimitive { + class Amount final : public ModelPrimitive { public: + explicit Amount(const std::string &amount); + explicit Amount(std::string &&amount); + + Amount(const Amount &o); + Amount(Amount &&o) noexcept; + /** * Gets integer representation value, which ignores precision * @return amount represented as integer value, which ignores precision */ - virtual const boost::multiprecision::uint256_t &intValue() const = 0; + const boost::multiprecision::uint256_t &intValue() const; /** * Gets the position of precision * @return the position of precision */ - virtual types::PrecisionType precision() const = 0; + types::PrecisionType precision() const; /** - * Checks equality of objects inside - * @param rhs - other wrapped value - * @return true, if wrapped objects are same + * String representation. + * @return string representation of the asset. */ - bool operator==(const ModelType &rhs) const override { - return intValue() == rhs.intValue() and precision() == rhs.precision(); - } + std::string toStringRepr() const; /** - * String representation. - * @return string representation of the asset. + * Checks equality of objects inside + * @param rhs - other wrapped value + * @return true, if wrapped objects are same */ - std::string toStringRepr() const { - if (precision() > 0) { - boost::multiprecision::cpp_dec_float_50 float50(intValue()); - float50 /= pow(10, precision()); - return float50.str(precision(), std::ios_base::fixed); - } - return intValue().str(0, std::ios_base::fixed); - } + bool operator==(const ModelType &rhs) const override; /** * Stringify the data. * @return the content of asset. */ - std::string toString() const override { - return detail::PrettyStringBuilder() - .init("Amount") - .append("intValue", intValue().str()) - .append("precision", std::to_string(precision())) - .finalize(); - } + std::string toString() const override; + + Amount *clone() const override; + + private: + const std::string amount_; + + interface::types::PrecisionType precision_; + const boost::multiprecision::uint256_t multiprecision_repr_; }; } // namespace interface } // namespace shared_model diff --git a/shared_model/interfaces/common_objects/impl/amount.cpp b/shared_model/interfaces/common_objects/impl/amount.cpp new file mode 100644 index 0000000000..37b462034f --- /dev/null +++ b/shared_model/interfaces/common_objects/impl/amount.cpp @@ -0,0 +1,74 @@ +/** + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "interfaces/common_objects/amount.hpp" + +#include + +#include +#include "utils/string_builder.hpp" + +namespace shared_model { + namespace interface { + Amount::Amount(const std::string &amount) : Amount(std::string(amount)) {} + Amount::Amount(std::string &&amount) + : amount_(std::move(amount)), + precision_(0), + multiprecision_repr_([this] { + static const std::regex r("([0-9]+)(\\.([0-9]+))?"); + // 123.456 will have the following groups: + // [0] -> 123.456, [1] -> 123 + // [2] -> .456, [3] -> 456 + std::smatch match; + if (std::regex_match(this->amount_, match, r) + && match.size() == 4) { + this->precision_ = match[3].length(); + auto str = match[0].str(); + size_t pos = match[1].length(); + // remove dot if it exist + if (pos < str.size()) { + str.erase(str.begin() + pos); + } + // remove leading zeroes + str.erase(0, + std::min(str.find_first_not_of('0'), str.size() - 1)); + return boost::multiprecision::uint256_t(str); + } + return std::numeric_limits::min(); + }()) {} + + Amount::Amount(const Amount &o) : Amount(std::string(o.amount_)) {} + + Amount::Amount(Amount &&o) noexcept + : Amount(std::move(const_cast(o.amount_))) {} + + const boost::multiprecision::uint256_t &Amount::intValue() const { + return multiprecision_repr_; + } + + types::PrecisionType Amount::precision() const { + return precision_; + } + + std::string Amount::toStringRepr() const { + return amount_; + } + + bool Amount::operator==(const ModelType &rhs) const { + return amount_ == rhs.amount_; + } + + std::string Amount::toString() const { + return detail::PrettyStringBuilder() + .init("Amount") + .append("value", amount_) + .finalize(); + } + + Amount *Amount::clone() const { + return new Amount(*this); + } + } // namespace interface +} // namespace shared_model diff --git a/shared_model/packages/ios/ios-build.sh b/shared_model/packages/ios/ios-build.sh index 8bd9b86219..1b54e3fb35 100755 --- a/shared_model/packages/ios/ios-build.sh +++ b/shared_model/packages/ios/ios-build.sh @@ -109,7 +109,6 @@ mkdir lib mkdir include cp "$DEPS_DIR"/lib/lib${PROTOBUF_LIB_NAME}.a \ "$DEPS_DIR"/lib/libed25519.a \ - ./iroha/shared_model/build/amount/libiroha_amount.a \ ./iroha/shared_model/build/generator/libgenerator.a \ ./iroha/shared_model/build/bindings/libbindings.a \ ./iroha/shared_model/build/cryptography/ed25519_sha3_impl/internal/libed25519_crypto.a \ diff --git a/shared_model/schema/commands.proto b/shared_model/schema/commands.proto index f8ebcd93cd..c163755474 100644 --- a/shared_model/schema/commands.proto +++ b/shared_model/schema/commands.proto @@ -9,7 +9,7 @@ import "primitive.proto"; message AddAssetQuantity { string asset_id = 1; - Amount amount = 2; + string amount = 2; } message AddPeer { @@ -59,7 +59,7 @@ message TransferAsset { string dest_account_id = 2; string asset_id = 3; string description = 4; - Amount amount = 5; + string amount = 5; } message AppendRole { @@ -89,7 +89,7 @@ message RevokePermission { message SubtractAssetQuantity { string asset_id = 1; - Amount amount = 2; + string amount = 2; } message Command { diff --git a/shared_model/schema/primitive.proto b/shared_model/schema/primitive.proto index 3dd2ffe6c1..c5214e0b14 100644 --- a/shared_model/schema/primitive.proto +++ b/shared_model/schema/primitive.proto @@ -6,8 +6,7 @@ /** * Messages related to primitive types, used in Commands and Queries * - * Contains RolePermission, GrantablePermission, Signature, - * uint256, Amount, and Peer. + * Contains RolePermission, GrantablePermission, Signature, and Peer. */ syntax = "proto3"; @@ -91,18 +90,6 @@ message Signature { bytes signature = 2; } -message uint256 { - uint64 first = 1; - uint64 second = 2; - uint64 third = 3; - uint64 fourth = 4; -} - -message Amount { - uint256 value = 1; - uint32 precision = 2; -} - message Peer { string address = 1; bytes peer_key = 2; diff --git a/shared_model/schema/qry_responses.proto b/shared_model/schema/qry_responses.proto index af70fc8205..2726064f49 100644 --- a/shared_model/schema/qry_responses.proto +++ b/shared_model/schema/qry_responses.proto @@ -31,7 +31,7 @@ message Account { message AccountAsset { string asset_id = 1; string account_id = 2; - Amount balance = 3; + string balance = 3; } // *** Responses *** // diff --git a/shared_model/utils/amount_utils.cpp b/shared_model/utils/amount_utils.cpp index 45a88d5a10..4a908207d3 100644 --- a/shared_model/utils/amount_utils.cpp +++ b/shared_model/utils/amount_utils.cpp @@ -1,33 +1,17 @@ /** - * Copyright Soramitsu Co., Ltd. 2018 All Rights Reserved. - * http://soramitsu.co.jp - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 */ #include "utils/amount_utils.hpp" - #include -#include "builders/default_builders.hpp" -#include "builders/protobuf/common_objects/proto_amount_builder.hpp" - namespace shared_model { namespace detail { const boost::multiprecision::uint256_t ten = 10; boost::multiprecision::uint256_t increaseValuePrecision( - boost::multiprecision::uint256_t value, int degree) { + const boost::multiprecision::uint256_t &value, int degree) { return value * pow(ten, degree); } @@ -53,10 +37,12 @@ namespace shared_model { % b.intValue().str()) .str())); } - return shared_model::builder::AmountBuilderWithoutValidator() - .precision(max_precision) - .intValue(val_a + val_b) - .build(); + std::string val = (val_a + val_b).str(); + if (max_precision != 0) { + val.insert((val.rbegin() + max_precision).base(), '.'); + } + return iroha::expected::makeValue( + std::make_shared(std::move(val))); } /** @@ -85,10 +71,14 @@ namespace shared_model { return iroha::expected::makeError( std::make_shared("new precision overflows number")); } - return shared_model::builder::AmountBuilderWithoutValidator() - .precision(max_precision) - .intValue(val_a - val_b) - .build(); + auto diff = val_a - val_b; + std::string val = diff.str(); + if (max_precision != 0 && val.size() > max_precision + 1) { + auto ptr = val.rbegin() + max_precision; + val.insert(ptr.base(), '.'); + } + return iroha::expected::makeValue( + std::make_shared(std::move(val))); } /** @@ -113,10 +103,12 @@ namespace shared_model { return iroha::expected::makeError( std::make_shared("operation overflows number")); } - return shared_model::builder::AmountBuilderWithoutValidator() - .precision(new_precision) - .intValue(val_amount) - .build(); + std::string val = val_amount.str(); + if (new_precision != 0) { + val.insert((val.rbegin() + new_precision).base(), '.'); + } + return iroha::expected::makeValue( + std::make_shared(std::move(val))); } int compareAmount(const shared_model::interface::Amount &a, diff --git a/shared_model/utils/amount_utils.hpp b/shared_model/utils/amount_utils.hpp index 6d92c0c2d7..4e27208595 100644 --- a/shared_model/utils/amount_utils.hpp +++ b/shared_model/utils/amount_utils.hpp @@ -21,6 +21,7 @@ #include #include "common/result.hpp" +#include "interfaces/common_objects/amount.hpp" namespace shared_model { namespace interface { @@ -29,7 +30,7 @@ namespace shared_model { namespace detail { boost::multiprecision::uint256_t increaseValuePrecision( - boost::multiprecision::uint256_t value, int degree); + const boost::multiprecision::uint256_t &value, int degree); /** * Sums up two amounts. diff --git a/test/integration/transport/CMakeLists.txt b/test/integration/transport/CMakeLists.txt index ba36f9b9e6..8d8617e791 100644 --- a/test/integration/transport/CMakeLists.txt +++ b/test/integration/transport/CMakeLists.txt @@ -6,5 +6,4 @@ target_link_libraries(ordering_gate_service_test ordering_service shared_model_stateless_validation shared_model_cryptography_model - iroha_amount ) diff --git a/test/module/irohad/ametsuchi/ametsuchi_test.cpp b/test/module/irohad/ametsuchi/ametsuchi_test.cpp index b2ed612837..d772265637 100644 --- a/test/module/irohad/ametsuchi/ametsuchi_test.cpp +++ b/test/module/irohad/ametsuchi/ametsuchi_test.cpp @@ -37,17 +37,6 @@ using namespace shared_model::interface::permissions; auto zero_string = std::string(32, '0'); auto fake_hash = shared_model::crypto::Hash(zero_string); auto fake_pubkey = shared_model::crypto::PublicKey(zero_string); -using AmountBuilder = shared_model::builder::AmountBuilderWithoutValidator; - -/** - * Return shared pointer to amount from result, or throw exception - * @return amount from result - */ -std::shared_ptr getAmount( - const shared_model::builder::BuilderResult - &result) { - return framework::expected::val(result)->value; -} /** * Shortcut to create CallExact observable wrapper, subscribe with given lambda, @@ -231,9 +220,9 @@ TEST_F(AmetsuchiTest, SampleTest) { apply(storage, block2); validateAccountAsset( - wsv, user1id, assetid, *getAmount(AmountBuilder::fromString("50.0"))); + wsv, user1id, assetid, shared_model::interface::Amount("50.0")); validateAccountAsset( - wsv, user2id, assetid, *getAmount(AmountBuilder::fromString("100.0"))); + wsv, user2id, assetid, shared_model::interface::Amount("100.0")); // Block store tests auto hashes = {block1.hash(), block2.hash()}; @@ -327,9 +316,9 @@ TEST_F(AmetsuchiTest, queryGetAccountAssetTransactionsTest) { // Check querying assets for users validateAccountAsset( - wsv, user1id, asset1id, *getAmount(AmountBuilder::fromString("300.0"))); + wsv, user1id, asset1id, shared_model::interface::Amount("300.0")); validateAccountAsset( - wsv, user2id, asset2id, *getAmount(AmountBuilder::fromString("250.0"))); + wsv, user2id, asset2id, shared_model::interface::Amount("250.0")); // 2th tx (user1 -> user2 # asset1) auto txn2 = @@ -348,9 +337,9 @@ TEST_F(AmetsuchiTest, queryGetAccountAssetTransactionsTest) { // Check account asset after transfer assets validateAccountAsset( - wsv, user1id, asset1id, *getAmount(AmountBuilder::fromString("180.0"))); + wsv, user1id, asset1id, shared_model::interface::Amount("180.0")); validateAccountAsset( - wsv, user2id, asset1id, *getAmount(AmountBuilder::fromString("120.0"))); + wsv, user2id, asset1id, shared_model::interface::Amount("120.0")); // 3rd tx // (user2 -> user3 # asset2) @@ -372,11 +361,11 @@ TEST_F(AmetsuchiTest, queryGetAccountAssetTransactionsTest) { apply(storage, block3); validateAccountAsset( - wsv, user2id, asset2id, *getAmount(AmountBuilder::fromString("90.0"))); + wsv, user2id, asset2id, shared_model::interface::Amount("90.0")); validateAccountAsset( - wsv, user3id, asset2id, *getAmount(AmountBuilder::fromString("150.0"))); + wsv, user3id, asset2id, shared_model::interface::Amount("150.0")); validateAccountAsset( - wsv, user1id, asset2id, *getAmount(AmountBuilder::fromString("10.0"))); + wsv, user1id, asset2id, shared_model::interface::Amount("10.0")); // Block store test auto hashes = {block1.hash(), block2.hash(), block3.hash()}; diff --git a/test/module/irohad/execution/command_validate_execute_test.cpp b/test/module/irohad/execution/command_validate_execute_test.cpp index 9a2cd52553..d8dff8b10b 100644 --- a/test/module/irohad/execution/command_validate_execute_test.cpp +++ b/test/module/irohad/execution/command_validate_execute_test.cpp @@ -105,27 +105,12 @@ class CommandValidateExecuteTest : public ::testing::Test { FAIL() << *e.error; }); - shared_model::builder::AmountBuilder< - shared_model::proto::AmountBuilder, - shared_model::validation::FieldValidator>() - .intValue(150) - .precision(2) - .build() - .match( - [&](expected::Value< - std::shared_ptr> &v) { - balance = v.value; - }, - [](expected::Error> &e) { - FAIL() << *e.error; - }); - shared_model::builder::AccountAssetBuilder< shared_model::proto::AccountAssetBuilder, shared_model::validation::FieldValidator>() .assetId(kAssetId) .accountId(kAccountId) - .balance(*balance) + .balance(balance) .build() .match( [&](expected::Value< @@ -196,7 +181,8 @@ class CommandValidateExecuteTest : public ::testing::Test { shared_model::interface::RolePermissionSet role_permissions; std::shared_ptr creator, account; - std::shared_ptr balance; + shared_model::interface::Amount balance = + shared_model::interface::Amount("1.50"); std::shared_ptr asset; std::shared_ptr wallet; @@ -1221,13 +1207,13 @@ class TransferAssetTest : public CommandValidateExecuteTest { src_wallet = clone(shared_model::proto::AccountAssetBuilder() .assetId(kAssetId) .accountId(kAdminId) - .balance(*balance) + .balance(balance) .build()); dst_wallet = clone(shared_model::proto::AccountAssetBuilder() .assetId(kAssetId) .accountId(kAccountId) - .balance(*balance) + .balance(balance) .build()); role_permissions = {Role::kTransfer, Role::kReceive}; @@ -1616,20 +1602,15 @@ TEST_F(TransferAssetTest, InvalidWhenWrongPrecisionDuringExecute) { * @then execute fails */ TEST_F(TransferAssetTest, InvalidWhenAmountOverflow) { - std::shared_ptr max_balance = clone( - shared_model::proto::AmountBuilder() - .intValue( - std::numeric_limits::max()) - .precision(2) + std::shared_ptr max_wallet = clone( + shared_model::proto::AccountAssetBuilder() + .assetId(src_wallet->assetId()) + .accountId(src_wallet->accountId()) + .balance(shared_model::interface::Amount( + std::numeric_limits::max().str() + + ".00")) .build()); - std::shared_ptr max_wallet = - clone(shared_model::proto::AccountAssetBuilder() - .assetId(src_wallet->assetId()) - .accountId(src_wallet->accountId()) - .balance(*max_balance) - .build()); - EXPECT_CALL(*wsv_query, getAsset(transfer_asset->assetId())) .WillOnce(Return(asset)); EXPECT_CALL(*wsv_query, diff --git a/test/module/irohad/execution/query_execution_test.cpp b/test/module/irohad/execution/query_execution_test.cpp index 5c91a22e73..920123880b 100644 --- a/test/module/irohad/execution/query_execution_test.cpp +++ b/test/module/irohad/execution/query_execution_test.cpp @@ -19,11 +19,9 @@ #include "module/shared_model/builders/protobuf/test_transaction_builder.hpp" #include "builders/common_objects/account_asset_builder.hpp" -#include "builders/common_objects/amount_builder.hpp" #include "builders/common_objects/asset_builder.hpp" #include "builders/protobuf/common_objects/proto_account_asset_builder.hpp" #include "builders/protobuf/common_objects/proto_account_builder.hpp" -#include "builders/protobuf/common_objects/proto_amount_builder.hpp" #include "builders/protobuf/common_objects/proto_asset_builder.hpp" #include "builders/protobuf/queries.hpp" #include "builders/query_responses/block_query_response_builder.hpp" @@ -271,26 +269,12 @@ class GetAccountAssetsTest : public QueryValidateExecuteTest { void SetUp() override { QueryValidateExecuteTest::SetUp(); - std::shared_ptr amount; - shared_model::builder::AmountBuilder< - shared_model::proto::AmountBuilder, - shared_model::validation::FieldValidator>() - .intValue(100) - .precision(2) - .build() - .match( - [&](iroha::expected::Value< - std::shared_ptr> &v) { - amount = v.value; - }, - [](iroha::expected::Error>) {}); - shared_model::builder::AccountAssetBuilder< shared_model::proto::AccountAssetBuilder, shared_model::validation::FieldValidator>() .assetId(asset_id) .accountId(admin_id) - .balance(*amount) + .balance(shared_model::interface::Amount("1.00")) .build() .match( [&](iroha::expected::Value< diff --git a/test/module/irohad/model/converters/json_commands_test.cpp b/test/module/irohad/model/converters/json_commands_test.cpp index bd5443d7fe..6e086cda39 100644 --- a/test/module/irohad/model/converters/json_commands_test.cpp +++ b/test/module/irohad/model/converters/json_commands_test.cpp @@ -104,9 +104,8 @@ TEST_F(JsonCommandTest, create_domain) { TEST_F(JsonCommandTest, add_asset_quantity) { auto orig_command = std::make_shared(); - iroha::Amount amount(150, 2); - orig_command->amount = amount; + orig_command->amount = "1.50"; orig_command->asset_id = "23"; auto json_command = factory.serializeAddAssetQuantity(orig_command); @@ -124,9 +123,8 @@ TEST_F(JsonCommandTest, add_asset_quantity) { */ TEST_F(JsonCommandTest, subtract_asset_quantity) { auto orig_command = std::make_shared(); - iroha::Amount amount(150, 2); - orig_command->amount = amount; + orig_command->amount = "1.50"; orig_command->asset_id = "23"; auto json_command = factory.serializeSubtractAssetQuantity(orig_command); diff --git a/test/module/irohad/model/converters/pb_commands_test.cpp b/test/module/irohad/model/converters/pb_commands_test.cpp index db63409cd1..4ad27dbec4 100644 --- a/test/module/irohad/model/converters/pb_commands_test.cpp +++ b/test/module/irohad/model/converters/pb_commands_test.cpp @@ -48,9 +48,8 @@ void command_converter_test(iroha::model::Command &abstract_command) { TEST(CommandTest, add_asset_quantity) { auto orig_command = iroha::model::AddAssetQuantity(); - iroha::Amount amount(50, 1); - orig_command.amount = amount; + orig_command.amount = "5.0"; orig_command.asset_id = "23"; auto factory = iroha::model::converters::PbCommandFactory(); @@ -68,9 +67,8 @@ TEST(CommandTest, add_asset_quantity) { */ TEST(CommandTest, subtract_asset_quantity) { auto orig_command = iroha::model::SubtractAssetQuantity(); - iroha::Amount amount(50, 1); - orig_command.amount = amount; + orig_command.amount = "5.0"; orig_command.asset_id = "23"; auto factory = iroha::model::converters::PbCommandFactory(); diff --git a/test/module/irohad/model/converters/pb_query_responses_test.cpp b/test/module/irohad/model/converters/pb_query_responses_test.cpp index 010733f0aa..d29b2222c1 100644 --- a/test/module/irohad/model/converters/pb_query_responses_test.cpp +++ b/test/module/irohad/model/converters/pb_query_responses_test.cpp @@ -64,8 +64,7 @@ TEST(QueryResponseTest, AccountAsset) { model::AccountAsset account_asset; account_asset.account_id = "123"; account_asset.asset_id = "123"; - iroha::Amount amount(1); - account_asset.balance = amount; + account_asset.balance = "1"; auto pb_account_asset = pb_factory.serializeAccountAsset(account_asset); auto des_account_asset = pb_factory.deserializeAccountAsset(pb_account_asset); diff --git a/test/module/irohad/model/operators/model_operators_test.cpp b/test/module/irohad/model/operators/model_operators_test.cpp index 93bd5d2893..744be3b072 100644 --- a/test/module/irohad/model/operators/model_operators_test.cpp +++ b/test/module/irohad/model/operators/model_operators_test.cpp @@ -58,8 +58,7 @@ TEST(ModelOperatorTest, AddPeerTest) { AddAssetQuantity createAddAssetQuantity() { AddAssetQuantity aaq; - iroha::Amount amount(1010, 2); - aaq.amount = amount; + aaq.amount = "10.10"; aaq.asset_id = "123"; return aaq; } @@ -77,8 +76,7 @@ TEST(ModelOperatorTest, AddAssetQuantityTest) { SubtractAssetQuantity createSubtractAssetQuantity() { SubtractAssetQuantity saq; - iroha::Amount amount(1010, 2); - saq.amount = amount; + saq.amount = "10.10"; saq.asset_id = "ast"; return saq; } @@ -212,8 +210,7 @@ TEST(ModelOperatorTest, SetQuorumTest) { TransferAsset createTransferAsset() { TransferAsset transferAsset; transferAsset.asset_id = "123"; - iroha::Amount amount(1010, 2); - transferAsset.amount = amount; + transferAsset.amount = "10.10"; transferAsset.src_account_id = "1"; transferAsset.dest_account_id = "2"; transferAsset.description = "test"; @@ -284,18 +281,6 @@ TEST(ModelOperatorTest, RevokePermissionTest) { ASSERT_NE(first, second); } -// -----|Amount|----- - -TEST(ModelOperatorTest, AmountTest) { - iroha::Amount amount1(1010, 2); - - iroha::Amount amount2(1010, 2); - - ASSERT_EQ(amount1, amount2); - iroha::Amount amount3(1011, 2); - ASSERT_NE(amount1, amount3); -} - // -----|Signature|----- Signature createSignature() { diff --git a/test/module/irohad/ordering/CMakeLists.txt b/test/module/irohad/ordering/CMakeLists.txt index 5dc742dba8..6193e3b988 100644 --- a/test/module/irohad/ordering/CMakeLists.txt +++ b/test/module/irohad/ordering/CMakeLists.txt @@ -16,7 +16,6 @@ addtest(ordering_service_test ordering_service_test.cpp) target_link_libraries(ordering_service_test ordering_service shared_model_stateless_validation - iroha_amount ) addtest(ordering_gate_test ordering_gate_test.cpp) @@ -24,5 +23,4 @@ target_link_libraries(ordering_gate_test ordering_service shared_model_cryptography_model shared_model_stateless_validation - iroha_amount ) diff --git a/test/module/irohad/torii/torii_queries_test.cpp b/test/module/irohad/torii/torii_queries_test.cpp index f74f9d4d98..71aafdb22b 100644 --- a/test/module/irohad/torii/torii_queries_test.cpp +++ b/test/module/irohad/torii/torii_queries_test.cpp @@ -21,7 +21,6 @@ limitations under the License. #include "builders/protobuf/common_objects/proto_account_asset_builder.hpp" #include "builders/protobuf/common_objects/proto_account_builder.hpp" -#include "builders/protobuf/common_objects/proto_amount_builder.hpp" #include "builders/protobuf/common_objects/proto_asset_builder.hpp" #include "builders/protobuf/queries.hpp" #include "module/shared_model/builders/protobuf/test_query_builder.hpp" @@ -323,14 +322,11 @@ TEST_F(ToriiQueriesTest, FindAccountAssetWhenHasRolePermissions) { auto account = shared_model::proto::AccountBuilder().accountId("accountA").build(); - auto amount = - shared_model::proto::AmountBuilder().intValue(100).precision(2).build(); - std::shared_ptr account_asset = clone(shared_model::proto::AccountAssetBuilder() .accountId("accountA") .assetId("usd") - .balance(amount) + .balance(shared_model::interface::Amount("1.00")) .build()); auto asset = shared_model::proto::AssetBuilder() diff --git a/test/module/libs/CMakeLists.txt b/test/module/libs/CMakeLists.txt index d67148b9e0..cac6eb7650 100644 --- a/test/module/libs/CMakeLists.txt +++ b/test/module/libs/CMakeLists.txt @@ -3,7 +3,6 @@ set(CMAKE_BUILD_TYPE Debug) SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/test_bin) # Reusable tests -add_subdirectory(amount) add_subdirectory(cache) add_subdirectory(crypto) add_subdirectory(datetime) diff --git a/test/module/libs/amount/CMakeLists.txt b/test/module/libs/amount/CMakeLists.txt deleted file mode 100644 index 4491d1915e..0000000000 --- a/test/module/libs/amount/CMakeLists.txt +++ /dev/null @@ -1,24 +0,0 @@ -# -# Copyright Soramitsu Co., Ltd. 2017 All Rights Reserved. -# http://soramitsu.co.jp -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -# amount Test - -AddTest(amount_test amount_test.cpp) - -target_link_libraries(amount_test - iroha_amount - ) diff --git a/test/module/libs/amount/amount_test.cpp b/test/module/libs/amount/amount_test.cpp deleted file mode 100644 index 5894383a83..0000000000 --- a/test/module/libs/amount/amount_test.cpp +++ /dev/null @@ -1,95 +0,0 @@ -/** - * Copyright Soramitsu Co., Ltd. 2017 All Rights Reserved. - * http://soramitsu.co.jp - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include -#include -#include - -class AmountTest : public testing::Test {}; - -TEST_F(AmountTest, TestBasic) { - iroha::Amount a(123, 2); - - // check taking percentage - auto b = a.percentage(50); - ASSERT_EQ(b.to_string(), "0.61"); - - // check summation - auto c = a + b; - ASSERT_TRUE(c); - ASSERT_EQ(c->to_string(), "1.84"); - - // check subtraction by subtracting b from c - auto aa = c - b; - ASSERT_TRUE(aa); - ASSERT_EQ(a, *aa); - - auto d = a.percentage(*c); // taking 1.84% of 1.23 - ASSERT_EQ(d.to_string(), "0.02"); - - auto e = - a.percentage(iroha::Amount(256)); // taking 256% of the amount == 3.1488 - // assuming that we round down - ASSERT_EQ(e.to_string(), "3.14"); - - // check move constructor - iroha::Amount f(std::move(a)); - ASSERT_EQ(f.to_string(), "1.23"); - - // check equals when different precisions - ASSERT_EQ(iroha::Amount(110, 2), iroha::Amount(11, 1)); - - // check comparisons - ASSERT_LT(iroha::Amount(110, 2), iroha::Amount(111, 2)); - ASSERT_LE(iroha::Amount(110, 2), iroha::Amount(111, 2)); - ASSERT_LE(iroha::Amount(111, 2), iroha::Amount(111, 2)); - - ASSERT_GT(iroha::Amount(111, 2), iroha::Amount(110, 2)); - ASSERT_GE(iroha::Amount(111, 2), iroha::Amount(110, 2)); - ASSERT_GE(iroha::Amount(111, 2), iroha::Amount(111, 2)); - - // check conversion to four uint64 - iroha::Amount g(boost::multiprecision::uint256_t( - "12391826391263918264198246192846192846192412398162398")); - auto vec = g.to_uint64s(); - iroha::Amount h(vec.at(0), vec.at(1), vec.at(2), vec.at(3)); - ASSERT_EQ(h.getPrecision(), 0); - ASSERT_EQ(g, h); -} - -// test with different precisions and values -TEST_F(AmountTest, TestStringConversion) { - for (uint8_t precision = 0; precision < 255; precision++) { - for (int val = 0; val < 10; val++) { - iroha::Amount amount(val, precision); - auto amount_str = amount.to_string(); - auto converted_amount = iroha::Amount::createFromString(amount_str); - ASSERT_TRUE(converted_amount); - ASSERT_EQ(converted_amount.value(), amount); - ASSERT_EQ(converted_amount->to_string(), amount.to_string()); - } - } - - auto a = iroha::Amount::createFromString(".20"); - ASSERT_EQ(a.value(), iroha::Amount(20, 2)); - - ASSERT_FALSE(iroha::Amount::createFromString("-0.20")); - ASSERT_FALSE(iroha::Amount::createFromString("-0..20")); - ASSERT_FALSE(iroha::Amount::createFromString("0..20")); - ASSERT_FALSE(iroha::Amount::createFromString("-0.20")); -} diff --git a/test/module/shared_model/amount_utils_test.cpp b/test/module/shared_model/amount_utils_test.cpp index 114eabd626..e9e4074d8b 100644 --- a/test/module/shared_model/amount_utils_test.cpp +++ b/test/module/shared_model/amount_utils_test.cpp @@ -17,7 +17,6 @@ #include -#include "builders/default_builders.hpp" #include "utils/amount_utils.hpp" using namespace shared_model::detail; @@ -30,33 +29,14 @@ class AmountTest : public testing::Test {}; * @then correct amount is given */ TEST_F(AmountTest, PlusTest) { - iroha::expected::Result, - std::shared_ptr> - a = shared_model::builder::DefaultAmountBuilder::fromString("1234.567"); + shared_model::interface::Amount a("1234.567"), b("100"); - iroha::expected::Result, - std::shared_ptr> - b = shared_model::builder::DefaultAmountBuilder::fromString("100"); - - a.match( - [&b](const iroha::expected::Value< - std::shared_ptr> &a_value) { - b.match( - [&a_value](const iroha::expected::Value> &b_value) { - auto c = *a_value.value + *b_value.value; - c.match( - [](const iroha::expected::Value> &c_value) { - ASSERT_EQ(c_value.value->intValue(), 1334567); - ASSERT_EQ(c_value.value->precision(), 3); - }, - [](const iroha::expected::Error> - &e) { FAIL() << *e.error; }); - }, - [](const iroha::expected::Error> &e) { - FAIL() << *e.error; - }); + auto c = a + b; + c.match( + [](const iroha::expected::Value< + std::shared_ptr> &c_value) { + ASSERT_EQ(c_value.value->intValue(), 1334567); + ASSERT_EQ(c_value.value->precision(), 3); }, [](const iroha::expected::Error> &e) { FAIL() << *e.error; @@ -69,33 +49,13 @@ TEST_F(AmountTest, PlusTest) { * @then correct amount is given */ TEST_F(AmountTest, MinusTest) { - iroha::expected::Result, - std::shared_ptr> - a = shared_model::builder::DefaultAmountBuilder::fromString("1234.567"); - - iroha::expected::Result, - std::shared_ptr> - b = shared_model::builder::DefaultAmountBuilder::fromString("100"); - - a.match( - [&b](const iroha::expected::Value< - std::shared_ptr> &a_value) { - b.match( - [&a_value](const iroha::expected::Value> &b_value) { - auto c = *a_value.value - *b_value.value; - c.match( - [](const iroha::expected::Value> &c_value) { - ASSERT_EQ(c_value.value->intValue(), 1134567); - ASSERT_EQ(c_value.value->precision(), 3); - }, - [](const iroha::expected::Error> - &e) { FAIL() << *e.error; }); - }, - [](const iroha::expected::Error> &e) { - FAIL() << *e.error; - }); + shared_model::interface::Amount a("1234.567"), b("100"); + auto c = a - b; + c.match( + [](const iroha::expected::Value< + std::shared_ptr> &c_value) { + ASSERT_EQ(c_value.value->intValue(), 1134567); + ASSERT_EQ(c_value.value->precision(), 3); }, [](const iroha::expected::Error> &e) { FAIL() << *e.error; @@ -108,23 +68,13 @@ TEST_F(AmountTest, MinusTest) { * @then correct amount is given */ TEST_F(AmountTest, PrecisionTest) { - iroha::expected::Result, - std::shared_ptr> - a = shared_model::builder::DefaultAmountBuilder::fromString("1234.567"); - - a.match( + shared_model::interface::Amount a("1234.567"); + auto c = makeAmountWithPrecision(a, 4); + c.match( [](const iroha::expected::Value< - std::shared_ptr> &a_value) { - auto c = makeAmountWithPrecision(*a_value.value, 4); - c.match( - [](const iroha::expected::Value< - std::shared_ptr> &c_value) { - ASSERT_EQ(c_value.value->intValue(), 12345670); - ASSERT_EQ(c_value.value->precision(), 4); - }, - [](const iroha::expected::Error> &e) { - FAIL() << *e.error; - }); + std::shared_ptr> &c_value) { + ASSERT_EQ(c_value.value->intValue(), 12345670); + ASSERT_EQ(c_value.value->precision(), 4); }, [](const iroha::expected::Error> &e) { FAIL() << *e.error; @@ -140,33 +90,33 @@ TEST_F(AmountTest, PlusOverflowsTest) { const std::string &uint256_halfmax = "723700557733226221397318656304299424082937404160253525246609900049457060" "2495.0"; // 2**252 - 1 - iroha::expected::Result, - std::shared_ptr> - a = shared_model::builder::DefaultAmountBuilder::fromString( - uint256_halfmax); - iroha::expected::Result, - std::shared_ptr> - b = shared_model::builder::DefaultAmountBuilder::fromString("100.00"); + shared_model::interface::Amount a(uint256_halfmax), b("100.00"); + auto c = a + b; + c.match( + [](const iroha::expected::Value< + std::shared_ptr> &c_value) { + FAIL() << "Operation successful but shouldn't"; + }, + [](const iroha::expected::Error> &e) { + SUCCEED() << *e.error; + }); +} + +/** + * @given two amounts + * @when tries to sum it up + * @then correct amount is given + */ +TEST_F(AmountTest, LeadingZeroPlusTest) { + shared_model::interface::Amount a("12.3"), b("03.21"); - a.match( - [&b](const iroha::expected::Value< - std::shared_ptr> &a_value) { - b.match( - [&a_value](const iroha::expected::Value> &b_value) { - auto c = *a_value.value + *b_value.value; - c.match( - [](const iroha::expected::Value> &c_value) { - FAIL() << "Operation successful but shouldn't"; - }, - [](const iroha::expected::Error> - &e) { SUCCEED() << *e.error; }); - }, - [](const iroha::expected::Error> &e) { - FAIL() << *e.error; - }); + auto c = a + b; + c.match( + [](const iroha::expected::Value< + std::shared_ptr> &c_value) { + ASSERT_EQ(c_value.value->intValue(), 1551); + ASSERT_EQ(c_value.value->precision(), 2); }, [](const iroha::expected::Error> &e) { FAIL() << *e.error; diff --git a/test/module/shared_model/backend_proto/CMakeLists.txt b/test/module/shared_model/backend_proto/CMakeLists.txt index f64dee82b5..303d398f94 100644 --- a/test/module/shared_model/backend_proto/CMakeLists.txt +++ b/test/module/shared_model/backend_proto/CMakeLists.txt @@ -65,14 +65,6 @@ target_link_libraries(shared_proto_util_test shared_model_proto_backend ) -addtest(shared_proto_amount_test - common_objects/shared_proto_amount_test.cpp - ) - -target_link_libraries(shared_proto_amount_test - shared_model_proto_backend - ) - addtest(permissions_test permissions_test.cpp ) diff --git a/test/module/shared_model/backend_proto/common_objects/shared_proto_amount_test.cpp b/test/module/shared_model/backend_proto/common_objects/shared_proto_amount_test.cpp deleted file mode 100644 index 9c41e92aea..0000000000 --- a/test/module/shared_model/backend_proto/common_objects/shared_proto_amount_test.cpp +++ /dev/null @@ -1,62 +0,0 @@ -/** - * Copyright Soramitsu Co., Ltd. 2018 All Rights Reserved. - * http://soramitsu.co.jp - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "backend/protobuf/common_objects/amount.hpp" - -#include - -using boost::multiprecision::uint256_t; - -const uint256_t kOne = 1; -/// each 64 bit part starts with 1 -const uint256_t kExpectedValue = - (kOne << (64 * 3)) | (kOne << (64 * 2)) | (kOne << 64) | kOne; - -/** - * @given protobuf amount object - * @when shared_model proto object is constructed - * @then int value of shared model object equals to initial protobuf value - */ -TEST(AmountTest, AmountIntValueInitialization) { - iroha::protocol::Amount amount; - - amount.mutable_value()->set_first(kOne.template convert_to()); - amount.mutable_value()->set_second(kOne.template convert_to()); - amount.mutable_value()->set_third(kOne.template convert_to()); - amount.mutable_value()->set_fourth(kOne.template convert_to()); - - shared_model::proto::Amount proto_amount(amount); - - ASSERT_EQ(proto_amount.intValue(), kExpectedValue); -} - -/** - * @given uint256_t value - * @when proto value of amount is filled - * @then int value of proto object equals uint256_t value - */ -TEST(AmountTest, ProtoValueFromIntConversion) { - iroha::protocol::Amount amount; - - shared_model::proto::convertToProtoAmount(*amount.mutable_value(), - kExpectedValue); - - ASSERT_EQ(amount.value().first(), kOne); - ASSERT_EQ(amount.value().second(), kOne); - ASSERT_EQ(amount.value().third(), kOne); - ASSERT_EQ(amount.value().fourth(), kOne); -} diff --git a/test/module/shared_model/backend_proto/shared_proto_transaction_test.cpp b/test/module/shared_model/backend_proto/shared_proto_transaction_test.cpp index 2f20660fb5..87c319161e 100644 --- a/test/module/shared_model/backend_proto/shared_proto_transaction_test.cpp +++ b/test/module/shared_model/backend_proto/shared_proto_transaction_test.cpp @@ -50,8 +50,7 @@ iroha::protocol::AddAssetQuantity generateAddAssetQuantity( iroha::protocol::AddAssetQuantity command; command.set_asset_id(asset_id); - command.mutable_amount()->mutable_value()->set_fourth(1000); - command.mutable_amount()->set_precision(2); + command.set_amount("10.00"); return command; } diff --git a/test/module/shared_model/builders/common_objects/CMakeLists.txt b/test/module/shared_model/builders/common_objects/CMakeLists.txt index f656480ed2..3327ba3a9f 100644 --- a/test/module/shared_model/builders/common_objects/CMakeLists.txt +++ b/test/module/shared_model/builders/common_objects/CMakeLists.txt @@ -30,15 +30,6 @@ target_link_libraries(account_builder_test shared_model_stateless_validation ) -addtest(amount_builder_test - amount_builder_test.cpp - ) - -target_link_libraries(amount_builder_test - shared_model_proto_builders - shared_model_stateless_validation - ) - addtest(signature_builder_test signature_builder_test.cpp ) diff --git a/test/module/shared_model/builders/common_objects/account_asset_builder_test.cpp b/test/module/shared_model/builders/common_objects/account_asset_builder_test.cpp index e179c14567..def0d7bbfa 100644 --- a/test/module/shared_model/builders/common_objects/account_asset_builder_test.cpp +++ b/test/module/shared_model/builders/common_objects/account_asset_builder_test.cpp @@ -19,7 +19,6 @@ #include "builders/common_objects/account_asset_builder.hpp" #include "builders/protobuf/common_objects/proto_account_asset_builder.hpp" -#include "builders/protobuf/common_objects/proto_amount_builder.hpp" #include "builders_test_fixture.hpp" #include "validators/field_validator.hpp" @@ -39,8 +38,7 @@ TEST(AccountAssetBuilderTest, StatelessValidAllFields) { auto valid_account_id = "account@name"; auto valid_asset_id = "asset#coin"; - auto valid_balance = - shared_model::proto::AmountBuilder().intValue(100).precision(2).build(); + auto valid_balance = shared_model::interface::Amount("1.00"); auto account_asset = builder.accountId(valid_account_id) .assetId(valid_asset_id) @@ -73,8 +71,7 @@ TEST(AccountAssetBuilderTest, SeveralObjectsFromOneBuilder) { auto valid_account_id = "account@name"; auto valid_asset_id = "asset#coin"; - auto valid_balance = - shared_model::proto::AmountBuilder().intValue(100).precision(2).build(); + auto valid_balance = shared_model::interface::Amount("1.00"); auto state = builder.accountId(valid_account_id) .assetId(valid_asset_id) diff --git a/test/module/shared_model/builders/common_objects/amount_builder_test.cpp b/test/module/shared_model/builders/common_objects/amount_builder_test.cpp deleted file mode 100644 index 2dc5c2dadc..0000000000 --- a/test/module/shared_model/builders/common_objects/amount_builder_test.cpp +++ /dev/null @@ -1,81 +0,0 @@ -/** - * Copyright Soramitsu Co., Ltd. 2018 All Rights Reserved. - * http://soramitsu.co.jp - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include - -#include "builders/common_objects/amount_builder.hpp" -#include "builders/protobuf/common_objects/proto_amount_builder.hpp" -#include "builders_test_fixture.hpp" -#include "validators/field_validator.hpp" - -// TODO: 14.02.2018 nickaleks mock builder implementation IR-970 -// TODO: 14.02.2018 nickaleks mock field validator IR-971 - -/** - * @given field values which pass stateless validation - * @when AmountBuilder is invoked - * @then Amount object is successfully constructed and has valid fields - */ -TEST(AmountBuilderTest, StatelessValidAllFields) { - shared_model::builder::AmountBuilder - builder; - - boost::multiprecision::uint256_t valid_value = 100; - auto valid_precision = 2; - - auto amount = - builder.intValue(valid_value).precision(valid_precision).build(); - - amount.match( - [&](shared_model::builder::BuilderResult< - shared_model::interface::Amount>::ValueType &v) { - EXPECT_EQ(v.value->intValue(), valid_value); - EXPECT_EQ(v.value->precision(), valid_precision); - }, - [](shared_model::builder::BuilderResult< - shared_model::interface::Amount>::ErrorType &e) { - FAIL() << *e.error; - }); -} - -/** - * @given field values which pass stateless validation - * @when AmountBuilder is invoked twice - * @then Two identical (==) Amount objects are constructed - */ -TEST(AmountBuilderTest, SeveralObjectsFromOneBuilder) { - shared_model::builder::AmountBuilder - builder; - - boost::multiprecision::uint256_t valid_value = 100; - auto valid_precision = 2; - - auto state = builder.intValue(valid_value).precision(valid_precision); - - auto amount = state.build(); - auto amount2 = state.build(); - - testResultObjects(amount, amount2, [](auto &a, auto &b) { - // pointer points to different objects - ASSERT_TRUE(a != b); - - EXPECT_EQ(a->intValue(), b->intValue()); - EXPECT_EQ(a->precision(), b->precision()); - }); -} diff --git a/test/module/shared_model/builders/common_objects/query_response_builder_test.cpp b/test/module/shared_model/builders/common_objects/query_response_builder_test.cpp index 74af4e36c3..bdd9d998fe 100644 --- a/test/module/shared_model/builders/common_objects/query_response_builder_test.cpp +++ b/test/module/shared_model/builders/common_objects/query_response_builder_test.cpp @@ -22,7 +22,6 @@ #include "builders/default_builders.hpp" #include "builders/protobuf/builder_templates/query_response_template.hpp" #include "builders/protobuf/common_objects/proto_account_builder.hpp" -#include "builders/protobuf/common_objects/proto_amount_builder.hpp" #include "builders/protobuf/query_responses/proto_block_query_response_builder.hpp" #include "builders/query_responses/block_query_response_builder.hpp" #include "cryptography/keypair.hpp" @@ -40,26 +39,22 @@ const auto hash = std::string(32, '0'); uint64_t height = 1; uint8_t quorum = 2; -boost::multiprecision::uint256_t valid_value = 1000; auto valid_precision = 1; -const iroha::Amount amount(valid_value, valid_precision); -const auto proto_amount = shared_model::proto::AmountBuilder() - .intValue(valid_value) - .precision(valid_precision) - .build(); + const shared_model::interface::types::DetailType account_detail = "account-detail"; const auto query_hash = shared_model::interface::types::HashType("hashhash"); decltype(iroha::time::now()) created_time = iroha::time::now(); TEST(QueryResponseBuilderTest, AccountAssetResponse) { + const auto amount = shared_model::interface::Amount("10.00"); shared_model::proto::TemplateQueryResponseBuilder<> builder; shared_model::proto::QueryResponse query_response = builder.queryHash(query_hash) .accountAssetResponse({shared_model::proto::AccountAssetBuilder() .accountId(account_id) .assetId(asset_id) - .balance(proto_amount) + .balance(amount) .build()}) .build(); ASSERT_NO_THROW({ @@ -71,7 +66,7 @@ TEST(QueryResponseBuilderTest, AccountAssetResponse) { ASSERT_EQ(asset_response.assetId(), asset_id); ASSERT_EQ(asset_response.accountId(), account_id); - ASSERT_EQ(asset_response.balance(), proto_amount); + ASSERT_EQ(asset_response.balance(), amount); ASSERT_EQ(query_response.queryHash(), query_hash); }); } diff --git a/test/module/shared_model/builders/protobuf/CMakeLists.txt b/test/module/shared_model/builders/protobuf/CMakeLists.txt index 4a4314e8d4..41be205a07 100644 --- a/test/module/shared_model/builders/protobuf/CMakeLists.txt +++ b/test/module/shared_model/builders/protobuf/CMakeLists.txt @@ -38,14 +38,6 @@ target_link_libraries(proto_account_builder_test shared_model_proto_builders ) -addtest(proto_amount_builder_test - common_objects/proto_amount_builder_test.cpp - ) - -target_link_libraries(proto_amount_builder_test - shared_model_proto_builders - ) - addtest(proto_signature_builder_test common_objects/proto_signature_builder_test.cpp ) diff --git a/test/module/shared_model/builders/protobuf/common_objects/proto_account_asset_builder_test.cpp b/test/module/shared_model/builders/protobuf/common_objects/proto_account_asset_builder_test.cpp index a1aefcf30b..f95341d5ab 100644 --- a/test/module/shared_model/builders/protobuf/common_objects/proto_account_asset_builder_test.cpp +++ b/test/module/shared_model/builders/protobuf/common_objects/proto_account_asset_builder_test.cpp @@ -18,7 +18,6 @@ #include #include "builders/protobuf/common_objects/proto_account_asset_builder.hpp" -#include "builders/protobuf/common_objects/proto_amount_builder.hpp" /** * @given fields for AccountAsset object @@ -31,8 +30,7 @@ TEST(ProtoAccountAssetBuilder, AllFieldsBuild) { auto expected_account_id = "account@name"; auto expected_asset_id = "asset#coin"; - auto expected_balance = - shared_model::proto::AmountBuilder().intValue(100).precision(2).build(); + auto expected_balance = shared_model::interface::Amount("1.00"); auto account_asset = builder.accountId(expected_account_id) .assetId(expected_asset_id) @@ -54,8 +52,7 @@ TEST(ProtoAccountAssetBuilderTest, SeveralObjectsFromOneBuilder) { auto expected_account_id = "account@name"; auto expected_asset_id = "asset#coin"; - auto expected_balance = - shared_model::proto::AmountBuilder().intValue(100).precision(2).build(); + auto expected_balance = shared_model::interface::Amount("1.00"); auto state = builder.accountId(expected_account_id) .assetId(expected_asset_id) diff --git a/test/module/shared_model/builders/protobuf/common_objects/proto_amount_builder_test.cpp b/test/module/shared_model/builders/protobuf/common_objects/proto_amount_builder_test.cpp deleted file mode 100644 index d916619564..0000000000 --- a/test/module/shared_model/builders/protobuf/common_objects/proto_amount_builder_test.cpp +++ /dev/null @@ -1,62 +0,0 @@ -/** - * Copyright Soramitsu Co., Ltd. 2018 All Rights Reserved. - * http://soramitsu.co.jp - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include - -#include - -#include "builders/protobuf/common_objects/proto_amount_builder.hpp" - -/** - * @given fields for Amount object - * @when AmountBuilder is invoked - * @then Amount object is successfully constructed and has the same fields as - * provided - */ -TEST(ProtoAmountBuilderTest, AllFieldsBuild) { - shared_model::proto::AmountBuilder builder; - - boost::multiprecision::uint256_t expected_int_value = 100; - auto expected_precision = 2; - - auto amount = builder.intValue(expected_int_value) - .precision(expected_precision) - .build(); - - EXPECT_EQ(amount.intValue(), expected_int_value); - EXPECT_EQ(amount.precision(), expected_precision); -} - -/** - * @given fields for Amount object - * @when AmountBuilder is invoked twice with the same configuration - * @then Two constructed Amount objects are identical - */ -TEST(ProtoAmountBuilderTest, SeveralObjectsFromOneBuilder) { - shared_model::proto::AmountBuilder builder; - - boost::multiprecision::uint256_t expected_int_value{"123456789012345678901234567890"}; - auto expected_precision = 2; - - auto state = - builder.intValue(expected_int_value).precision(expected_precision); - auto amount = state.build(); - auto amount2 = state.build(); - - EXPECT_EQ(amount.intValue(), amount2.intValue()); - EXPECT_EQ(amount.precision(), amount2.precision()); -} diff --git a/test/module/shared_model/cryptography/CMakeLists.txt b/test/module/shared_model/cryptography/CMakeLists.txt index f704652d62..c2c2ea832a 100644 --- a/test/module/shared_model/cryptography/CMakeLists.txt +++ b/test/module/shared_model/cryptography/CMakeLists.txt @@ -8,7 +8,6 @@ target_link_libraries(crypto_usage_test shared_model_proto_builders shared_model_cryptography schema - iroha_amount ) addtest(security_signatures_test security_signatures_test.cpp) diff --git a/test/module/shared_model/validators/field_validator_test.cpp b/test/module/shared_model/validators/field_validator_test.cpp index 998ec124e4..51c54dd141 100644 --- a/test/module/shared_model/validators/field_validator_test.cpp +++ b/test/module/shared_model/validators/field_validator_test.cpp @@ -268,12 +268,9 @@ class FieldValidatorTest : public ValidatorsTest { idTestCases("asset_id", &FieldValidatorTest::asset_id, '#'); std::vector amount_test_cases{ - {"valid_amount", - [&] { amount.mutable_value()->set_fourth(100); }, - true, - ""}, + {"valid_amount", [&] { amount = "100"; }, true, ""}, {"zero_amount", - [&] { amount.mutable_value()->set_fourth(0); }, + [&] { amount = "0"; }, false, "Amount must be greater than 0, passed value: 0"}}; @@ -644,11 +641,12 @@ class FieldValidatorTest : public ValidatorsTest { &FieldValidator::validateAssetId, &FieldValidatorTest::asset_id, asset_id_test_cases), - makeTransformValidator("amount", - &FieldValidator::validateAmount, - &FieldValidatorTest::amount, - [](auto &&x) { return proto::Amount(x); }, - amount_test_cases), + makeTransformValidator( + "amount", + &FieldValidator::validateAmount, + &FieldValidatorTest::amount, + [](auto &&x) { return shared_model::interface::Amount(x); }, + amount_test_cases), makeTransformValidator("peer", &FieldValidator::validatePeer, &FieldValidatorTest::peer, diff --git a/test/module/shared_model/validators/validators_fixture.hpp b/test/module/shared_model/validators/validators_fixture.hpp index e4e6e39a9b..b844998567 100644 --- a/test/module/shared_model/validators/validators_fixture.hpp +++ b/test/module/shared_model/validators/validators_fixture.hpp @@ -73,9 +73,7 @@ class ValidatorsTest : public ::testing::Test { field_setters["tx_hashes"] = addString(hash); field_setters["quorum"] = setUInt32(quorum); field_setters["description"] = setString(""); - field_setters["amount"] = [&](auto refl, auto msg, auto field) { - refl->MutableMessage(msg, field)->CopyFrom(amount); - }; + field_setters["amount"] = setString(amount); field_setters["peer"] = [&](auto refl, auto msg, auto field) { refl->MutableMessage(msg, field)->CopyFrom(peer); }; @@ -172,8 +170,7 @@ class ValidatorsTest : public ::testing::Test { // Fill fields with valid values created_time = iroha::time::now(); precision = 2; - amount.set_precision(precision); - amount.mutable_value()->set_fourth(1000); + amount = "10.00"; public_key_size = 32; hash_size = 32; counter = 1048576; @@ -225,7 +222,7 @@ class ValidatorsTest : public ::testing::Test { iroha::protocol::GrantablePermission grantable_permission; uint8_t quorum; uint8_t precision; - iroha::protocol::Amount amount; + std::string amount; iroha::protocol::Peer peer; decltype(iroha::time::now()) created_time; iroha::protocol::QueryPayloadMeta meta; From 16c404d55de7ba6126dd9285604325bc91845a2b Mon Sep 17 00:00:00 2001 From: Akvinikym Date: Tue, 17 Jul 2018 16:26:51 +0300 Subject: [PATCH 51/97] Endpoint Receive Transaction List (#1568) Signed-off-by: Akvinikym --- irohad/torii/command_client.cpp | 6 + irohad/torii/command_client.hpp | 7 + irohad/torii/command_service.hpp | 25 +++- irohad/torii/impl/command_service.cpp | 139 ++++++++++++++---- .../protobuf/transaction_sequence_builder.hpp | 6 +- shared_model/schema/endpoint.proto | 5 + shared_model/validators/default_validator.hpp | 6 + .../irohad/torii/torii_service_test.cpp | 107 ++++++++++++++ .../protobuf/transport_builder_test.cpp | 98 +++++++----- 9 files changed, 332 insertions(+), 67 deletions(-) diff --git a/irohad/torii/command_client.cpp b/irohad/torii/command_client.cpp index 220d85cd8c..7b8a8cf860 100644 --- a/irohad/torii/command_client.cpp +++ b/irohad/torii/command_client.cpp @@ -56,6 +56,12 @@ namespace torii { return stub_->Torii(&context, tx, &a); } + grpc::Status CommandSyncClient::ListTorii(const iroha::protocol::TxList &tx_list) const { + google::protobuf::Empty a; + grpc::ClientContext context; + return stub_->ListTorii(&context, tx_list, &a); + } + grpc::Status CommandSyncClient::Status( const iroha::protocol::TxStatusRequest &request, iroha::protocol::ToriiResponse &response) const { diff --git a/irohad/torii/command_client.hpp b/irohad/torii/command_client.hpp index ec915d15c4..eb428ea821 100644 --- a/irohad/torii/command_client.hpp +++ b/irohad/torii/command_client.hpp @@ -46,6 +46,13 @@ namespace torii { */ grpc::Status Torii(const iroha::protocol::Transaction &tx) const; + /** + * requests list of txs to a torii server and returns response + * @param tx_list + * @return grpc::Status - returns connection is success or not. + */ + grpc::Status ListTorii(const iroha::protocol::TxList &tx_list) const; + /** * @param tx * @param response returns ToriiResponse if succeeded diff --git a/irohad/torii/command_service.hpp b/irohad/torii/command_service.hpp index 468710d6c3..4c6da721e8 100644 --- a/irohad/torii/command_service.hpp +++ b/irohad/torii/command_service.hpp @@ -38,9 +38,8 @@ namespace torii { public: /** * Creates a new instance of CommandService - * @param pb_factory - model->protobuf and vice versa converter * @param tx_processor - processor of received transactions - * @param block_query - to query transactions outside the cache + * @param storage - to query transactions outside the cache * @param initial_timeout - streaming timeout when tx is not received * @param nonfinal_timeout - streaming timeout when tx is being processed */ @@ -63,6 +62,12 @@ namespace torii { */ void Torii(const iroha::protocol::Transaction &tx); + /** + * Actual implementation of sync Torii in CommandService + * @param tx_lis - transactions we've received + */ + void ListTorii(const iroha::protocol::TxList &tx_list); + /** * Torii call via grpc * @param context - call context (see grpc docs for details) @@ -74,6 +79,17 @@ namespace torii { const iroha::protocol::Transaction *request, google::protobuf::Empty *response) override; + /** + * Torii call for transactions list via grpc + * @param context - call context (see grpc docs for details) + * @param request - list of transactions received + * @param response - no actual response (grpc stub for empty answer) + * @return - grpc::Status + */ + virtual grpc::Status ListTorii(grpc::ServerContext *context, + const iroha::protocol::TxList *request, + google::protobuf::Empty *response) override; + /** * Request to retrieve a status of any particular transaction * @param request - TxStatusRequest object which identifies transaction @@ -134,6 +150,11 @@ namespace torii { inline void handleEvents(rxcpp::composite_subscription &subscription, rxcpp::schedulers::run_loop &run_loop); + void addTxToCacheAndLog(const std::string &who, + const shared_model::crypto::Hash &hash, + const iroha::protocol::ToriiResponse &response); + + private: using CacheType = iroha::cache::Cache; diff --git a/irohad/torii/impl/command_service.cpp b/irohad/torii/impl/command_service.cpp index fc357ff63d..9278e8d337 100644 --- a/irohad/torii/impl/command_service.cpp +++ b/irohad/torii/impl/command_service.cpp @@ -19,18 +19,16 @@ #include -#include "backend/protobuf/transaction_responses/proto_tx_response.hpp" - #include "ametsuchi/block_query.hpp" #include "backend/protobuf/transaction.hpp" +#include "backend/protobuf/transaction_responses/proto_tx_response.hpp" #include "builders/protobuf/transaction_responses/proto_transaction_status_builder.hpp" -#include "builders/protobuf/transport_builder.hpp" +#include "builders/protobuf/transaction_sequence_builder.hpp" #include "common/byteutils.hpp" #include "common/is_any.hpp" #include "common/timeout.hpp" #include "common/types.hpp" #include "cryptography/default_hash_provider.hpp" -#include "endpoint.pb.h" #include "validators/default_validator.hpp" namespace torii { @@ -72,19 +70,16 @@ namespace torii { } void CommandService::Torii(const iroha::protocol::Transaction &request) { - shared_model::crypto::Hash tx_hash; - iroha::protocol::ToriiResponse response; - shared_model::proto::TransportBuilder< shared_model::proto::Transaction, shared_model::validation::DefaultSignableTransactionValidator>() .build(request) .match( - [this, &tx_hash, &response]( + [this]( // success case iroha::expected::Value &iroha_tx) { - tx_hash = iroha_tx.value.hash(); + auto tx_hash = iroha_tx.value.hash(); if (cache_->findItem(tx_hash) and iroha_tx.value.quorum() < 2) { log_->warn("Found transaction {} in cache, ignoring", tx_hash.hex()); @@ -92,6 +87,7 @@ namespace torii { } // setting response + iroha::protocol::ToriiResponse response; response.set_tx_hash( shared_model::crypto::toBinaryString(tx_hash)); response.set_tx_status( @@ -101,33 +97,105 @@ namespace torii { tx_processor_->transactionHandle( std::make_shared( std::move(iroha_tx.value))); + + this->addTxToCacheAndLog( + "Torii", std::move(tx_hash), std::move(response)); }, - [this, &tx_hash, &request, &response](const auto &error) { + [this, &request](const auto &error) { // getting hash from invalid transaction auto blobPayload = shared_model::proto::makeBlob(request.payload()); - tx_hash = shared_model::crypto::DefaultHashProvider::makeHash( - blobPayload); + auto tx_hash = + shared_model::crypto::DefaultHashProvider::makeHash( + blobPayload); log_->warn("Stateless invalid tx: {}, hash: {}", error.error, tx_hash.hex()); // setting response + iroha::protocol::ToriiResponse response; response.set_tx_hash( shared_model::crypto::toBinaryString(tx_hash)); response.set_tx_status( iroha::protocol::TxStatus::STATELESS_VALIDATION_FAILED); response.set_error_message(std::move(error.error)); + + this->addTxToCacheAndLog( + "Torii", std::move(tx_hash), std::move(response)); + }); + } + + void CommandService::ListTorii(const iroha::protocol::TxList &tx_list) { + shared_model::proto::TransportBuilder< + shared_model::interface::TransactionSequence, + shared_model::validation::DefaultUnsignedTxCollectionValidator>() + .build(tx_list) + .match( + [this]( + // success case + iroha::expected::Value< + shared_model::interface::TransactionSequence> + &tx_sequence) { + auto txs = tx_sequence.value.transactions(); + std::for_each(txs.begin(), txs.end(), [this](auto &tx) { + auto tx_hash = tx->hash(); + if (cache_->findItem(tx_hash) and tx->quorum() < 2) { + log_->warn("Found transaction {} in cache, ignoring", + tx_hash.hex()); + return; + } + + // setting response + iroha::protocol::ToriiResponse response; + response.set_tx_hash( + shared_model::crypto::toBinaryString(tx_hash)); + response.set_tx_status( + iroha::protocol::TxStatus::STATELESS_VALIDATION_SUCCESS); + + // Send transaction to iroha + tx_processor_->transactionHandle(tx); + + this->addTxToCacheAndLog( + "ToriiList", std::move(tx_hash), std::move(response)); + }); + }, + [this, &tx_list](auto &error) { + auto &txs = tx_list.transactions(); + // form an error message, shared between all txs in a sequence + auto first_tx_blob = + shared_model::proto::makeBlob(txs[0].payload()); + auto first_tx_hash = + shared_model::crypto::DefaultHashProvider::makeHash( + first_tx_blob); + auto last_tx_blob = + shared_model::proto::makeBlob(txs[txs.size() - 1].payload()); + auto last_tx_hash = + shared_model::crypto::DefaultHashProvider::makeHash( + last_tx_blob); + auto sequence_error = + "Stateless invalid tx in transaction sequence, beginning " + "with tx : " + + first_tx_hash.hex() + " and ending with tx " + + last_tx_hash.hex() + ". Error is: " + error.error; + + // set error response for each transaction in a sequence + std::for_each( + txs.begin(), txs.end(), [this, &sequence_error](auto &tx) { + auto hash = + shared_model::crypto::DefaultHashProvider::makeHash( + shared_model::proto::makeBlob(tx.payload())); + + iroha::protocol::ToriiResponse response; + response.set_tx_hash( + shared_model::crypto::toBinaryString(hash)); + response.set_tx_status( + iroha::protocol::TxStatus::STATELESS_VALIDATION_FAILED); + response.set_error_message(sequence_error); + + this->addTxToCacheAndLog( + "ToriiList", std::move(hash), std::move(response)); + }); }); - log_->debug("Torii: adding item to cache: {}, status {} ", - tx_hash.hex(), - response.tx_status()); - // transactions can be handled from multiple threads, therefore a lock is - // required - std::lock_guard lock(stateless_tx_status_notifier_mutex_); - stateless_notifier_.get_subscriber().on_next( - std::make_shared( - std::move(response))); } grpc::Status CommandService::Torii( @@ -138,6 +206,13 @@ namespace torii { return grpc::Status::OK; } + grpc::Status CommandService::ListTorii(grpc::ServerContext *context, + const iroha::protocol::TxList *request, + google::protobuf::Empty *response) { + ListTorii(*request); + return grpc::Status::OK; + } + void CommandService::Status(const iroha::protocol::TxStatusRequest &request, iroha::protocol::ToriiResponse &response) { auto tx_hash = shared_model::crypto::Hash(request.tx_hash()); @@ -154,10 +229,8 @@ namespace torii { iroha::bytestringToHexstring(request.tx_hash())); response.set_tx_status(iroha::protocol::TxStatus::NOT_RECEIVED); } - log_->debug("Status: adding item to cache: {}, status {}", - tx_hash.hex(), - response.tx_status()); - cache_->addItem(tx_hash, response); + this->addTxToCacheAndLog( + "Status", std::move(tx_hash), std::move(response)); } } @@ -291,4 +364,20 @@ namespace torii { } } + void CommandService::addTxToCacheAndLog( + const std::string &who, + const shared_model::crypto::Hash &hash, + const iroha::protocol::ToriiResponse &response) { + log_->debug("{}: adding item to cache: {}, status {} ", + who, + hash.hex(), + response.tx_status()); + // transactions can be handled from multiple threads, therefore a lock is + // required + std::lock_guard lock(stateless_tx_status_notifier_mutex_); + stateless_notifier_.get_subscriber().on_next( + std::make_shared( + std::move(response))); + } + } // namespace torii diff --git a/shared_model/builders/protobuf/transaction_sequence_builder.hpp b/shared_model/builders/protobuf/transaction_sequence_builder.hpp index 7a3a70a037..ef8198c8b6 100644 --- a/shared_model/builders/protobuf/transaction_sequence_builder.hpp +++ b/shared_model/builders/protobuf/transaction_sequence_builder.hpp @@ -8,6 +8,7 @@ #include "interfaces/common_objects/types.hpp" #include "interfaces/iroha_internal/transaction_sequence.hpp" +#include "builders/protobuf/transport_builder.hpp" namespace shared_model { namespace proto { @@ -32,10 +33,11 @@ namespace shared_model { template iroha::expected::Result build(T &transport) { + const auto &txs = transport.transactions(); std::vector> shm_txs; std::transform( - transport.begin(), - transport.end(), + txs.begin(), + txs.end(), std::back_inserter(shm_txs), [](const auto &tx) { return std::make_shared(tx); }); return interface::TransactionSequence::createTransactionSequence( diff --git a/shared_model/schema/endpoint.proto b/shared_model/schema/endpoint.proto index 38a1abfa56..6bc224b835 100644 --- a/shared_model/schema/endpoint.proto +++ b/shared_model/schema/endpoint.proto @@ -32,8 +32,13 @@ message TxStatusRequest{ bytes tx_hash = 1; } +message TxList { + repeated Transaction transactions = 1; +} + service CommandService { rpc Torii (Transaction) returns (google.protobuf.Empty); + rpc ListTorii (TxList) returns (google.protobuf.Empty); rpc Status (TxStatusRequest) returns (ToriiResponse); rpc StatusStream(TxStatusRequest) returns (stream ToriiResponse); } diff --git a/shared_model/validators/default_validator.hpp b/shared_model/validators/default_validator.hpp index a1055100a8..fb5b444d30 100644 --- a/shared_model/validators/default_validator.hpp +++ b/shared_model/validators/default_validator.hpp @@ -27,6 +27,7 @@ #include "validators/query_validator.hpp" #include "validators/signable_validator.hpp" #include "validators/transaction_validator.hpp" +#include "validators/transactions_collection/batch_order_validator.hpp" #include "validators/transactions_collection/signed_transactions_collection_validator.hpp" #include "validators/transactions_collection/unsigned_transactions_collection_validator.hpp" @@ -70,6 +71,11 @@ namespace shared_model { SignableModelValidator; + + using DefaultUnsignedTxCollectionValidator = + UnsignedTransactionsCollectionValidator; + } // namespace validation } // namespace shared_model diff --git a/test/module/irohad/torii/torii_service_test.cpp b/test/module/irohad/torii/torii_service_test.cpp index fa7ae3ef8b..a3def8102f 100644 --- a/test/module/irohad/torii/torii_service_test.cpp +++ b/test/module/irohad/torii/torii_service_test.cpp @@ -476,3 +476,110 @@ TEST_F(ToriiServiceTest, StreamingNoTx) { ASSERT_EQ(torii_response.at(0).tx_status(), iroha::protocol::TxStatus::NOT_RECEIVED); } + +/** + * Checks that torii is able to handle lists (sequences) of transactions + * + * @given torii service and collection of transactions + * @when that collection is asked to be processed by Torii + * @then statuses of all transactions from that request are STATELESS_VALID + */ +TEST_F(ToriiServiceTest, ListOfTxs) { + const auto test_txs_number = 5; + + // initial preparations: creation of variables and txs + auto client = torii::CommandSyncClient(ip, port); + std::vector tx_hashes( + test_txs_number); + iroha::protocol::TxList tx_list; + + for (auto i = 0; i < test_txs_number; ++i) { + auto shm_tx = shared_model::proto::TransactionBuilder() + .creatorAccountId("doge@master" + std::to_string(i)) + .createdTime(iroha::time::now()) + .setAccountQuorum("doge@master", 2) + .quorum(1) + .build() + .signAndAddSignature( + shared_model::crypto::DefaultCryptoAlgorithmType:: + generateKeypair()) + .finish(); + tx_hashes[i] = shm_tx.hash(); + new (tx_list.add_transactions()) + iroha::protocol::Transaction(shm_tx.getTransport()); + } + + // send the txs + client.ListTorii(tx_list); + + // check their statuses + std::for_each( + std::begin(tx_hashes), std::end(tx_hashes), [&client](auto &hash) { + iroha::protocol::TxStatusRequest tx_request; + tx_request.set_tx_hash(shared_model::crypto::toBinaryString(hash)); + iroha::protocol::ToriiResponse toriiResponse; + client.Status(tx_request, toriiResponse); + + ASSERT_EQ(toriiResponse.tx_status(), + iroha::protocol::TxStatus::STATELESS_VALIDATION_SUCCESS); + }); +} + +/** + * Checks that in case of failed transactions list every transaction has a + * related status and error message + * + * @given torii service and a list of bad-formed transactions + * @when checking those transactions after sending them to Torii + * @then every transaction from this list will have STATELESS_FAILED status @and + * the same corresponding error message + */ +TEST_F(ToriiServiceTest, FailedListOfTxs) { + const auto test_txs_number = 5; + + // initial preparations: creation of variables and txs + auto client = torii::CommandSyncClient(ip, port); + std::vector tx_hashes( + test_txs_number); + iroha::protocol::TxList tx_list; + + for (auto i = 0; i < test_txs_number; ++i) { + auto shm_tx = TestTransactionBuilder() + .creatorAccountId("doge@master" + std::to_string(i)) + .createdTime(iroha::time::now(std::chrono::hours(24) + + std::chrono::minutes(1))) + .setAccountQuorum("doge@master", 2) + .quorum(1) + .build(); + tx_hashes[i] = shm_tx.hash(); + new (tx_list.add_transactions()) + iroha::protocol::Transaction(shm_tx.getTransport()); + } + + // send the txs + client.ListTorii(tx_list); + + // actual error message is too big and hardly predictable, so we want at least + // to make sure that edges of tx list are right + auto error_msg_beginning = + "Stateless invalid tx in transaction sequence, beginning " + "with tx : " + + tx_hashes.front().hex() + " and ending with tx " + + tx_hashes.back().hex(); + + // check their statuses + std::for_each( + std::begin(tx_hashes), + std::end(tx_hashes), + [&client, &error_msg_beginning](auto &hash) { + iroha::protocol::TxStatusRequest tx_request; + tx_request.set_tx_hash(shared_model::crypto::toBinaryString(hash)); + iroha::protocol::ToriiResponse toriiResponse; + client.Status(tx_request, toriiResponse); + auto error_beginning = toriiResponse.error_message().substr(0, toriiResponse.error_message().find_first_of('.')); + + ASSERT_EQ(toriiResponse.tx_status(), + iroha::protocol::TxStatus::STATELESS_VALIDATION_FAILED); + ASSERT_EQ(error_beginning, error_msg_beginning); + }); +} diff --git a/test/module/shared_model/builders/protobuf/transport_builder_test.cpp b/test/module/shared_model/builders/protobuf/transport_builder_test.cpp index ec5b7dc1ba..cfad680c7a 100644 --- a/test/module/shared_model/builders/protobuf/transport_builder_test.cpp +++ b/test/module/shared_model/builders/protobuf/transport_builder_test.cpp @@ -14,6 +14,7 @@ #include "builders/protobuf/transaction_sequence_builder.hpp" #include "builders/protobuf/transport_builder.hpp" #include "common/types.hpp" +#include "endpoint.pb.h" #include "framework/result_fixture.hpp" #include "interfaces/common_objects/types.hpp" #include "interfaces/iroha_internal/transaction_sequence.hpp" @@ -197,22 +198,24 @@ class TransportBuilderTest : public ::testing::Test { /** * Receives model object, gets transport from it, converts transport into * model object and checks if original and obtained model objects are the same - * @tparam T model object type - * @tparam SV validator type + * @tparam ObjectOriginalModel - model object type + * @tparam Validator - validator type * @param orig_model * @param successCase function invoking if value exists * @param failCase function invoking when error returned */ - template - void testTransport(const T &orig_model, + void testTransport(const ObjectOriginalModel &orig_model, SuccessCase &&successCase, FailCase &&failCase) { auto proto_model = orig_model.getTransport(); - auto built_model = TransportBuilder().build(proto_model); + auto built_model = + TransportBuilder().build( + proto_model); built_model.match(successCase, failCase); } @@ -473,7 +476,7 @@ TEST_F(TransportBuilderTest, BlockVariantWithInvalidBlock) { * AND it containcs 0 transactions */ TEST_F(TransportBuilderTest, TransactionSequenceEmpty) { - std::vector tr; + iroha::protocol::TxList tx_list; auto val = framework::expected::val( TransportBuilder>, validation::BatchOrderValidator>>() - .build(tr)); + .build(tx_list)); ASSERT_TRUE(val); val | [](auto &seq) { ASSERT_EQ(boost::size(seq.value.transactions()), 0); }; } @@ -493,22 +496,33 @@ TEST_F(TransportBuilderTest, TransactionSequenceEmpty) { * @then built object contains TransactionSequence shared model object */ TEST_F(TransportBuilderTest, TransactionSequenceCorrect) { - std::vector transactions; + iroha::protocol::TxList tx_list; auto batch1 = getValidBatch(0, 10); auto batch2 = getValidBatch(20, 5); auto batch3 = getValidBatch(30, 5); - std::move( - std::begin(batch1), std::end(batch1), std::back_inserter(transactions)); - std::move( - std::begin(batch2), std::end(batch2), std::back_inserter(transactions)); - transactions.push_back(createTransaction()); - transactions.push_back(createTransaction()); - transactions.push_back(createTransaction()); - std::move( - std::begin(batch3), std::end(batch3), std::back_inserter(transactions)); - transactions.push_back(createTransaction()); - auto val = framework::expected::val( - TransactionSequenceBuilder().build(transactions)); + std::for_each(std::begin(batch1), std::end(batch1), [&tx_list](auto &tx) { + new (tx_list.add_transactions()) + iroha::protocol::Transaction(tx.getTransport()); + }); + std::for_each(std::begin(batch2), std::end(batch2), [&tx_list](auto &tx) { + new (tx_list.add_transactions()) + iroha::protocol::Transaction(tx.getTransport()); + }); + new (tx_list.add_transactions()) + iroha::protocol::Transaction(createTransaction().getTransport()); + new (tx_list.add_transactions()) + iroha::protocol::Transaction(createTransaction().getTransport()); + new (tx_list.add_transactions()) + iroha::protocol::Transaction(createTransaction().getTransport()); + std::for_each(std::begin(batch3), std::end(batch3), [&tx_list](auto &tx) { + new (tx_list.add_transactions()) + iroha::protocol::Transaction(tx.getTransport()); + }); + new (tx_list.add_transactions()) + iroha::protocol::Transaction(createTransaction().getTransport()); + + auto val = + framework::expected::val(TransactionSequenceBuilder().build(tx_list)); ASSERT_TRUE(val); val | [](auto &seq) { ASSERT_EQ(boost::size(seq.value.transactions()), 24); }; } @@ -518,16 +532,21 @@ TEST_F(TransportBuilderTest, TransactionSequenceCorrect) { * @then built an error */ TEST_F(TransportBuilderTest, TransactionInteraptedBatch) { - std::vector transactions; + iroha::protocol::TxList tx_list; auto batch = getValidBatch(0, 10); - std::move(std::begin(batch), - std::begin(batch) + 3, - std::back_inserter(transactions)); - transactions.push_back(createTransaction()); - std::move( - std::begin(batch) + 3, std::end(batch), std::back_inserter(transactions)); - auto error = framework::expected::err( - TransactionSequenceBuilder().build(transactions)); + std::for_each(std::begin(batch), std::begin(batch) + 3, [&tx_list](auto &tx) { + new (tx_list.add_transactions()) + iroha::protocol::Transaction(tx.getTransport()); + }); + new (tx_list.add_transactions()) + iroha::protocol::Transaction(createTransaction().getTransport()); + std::for_each(std::begin(batch) + 3, std::end(batch), [&tx_list](auto &tx) { + new (tx_list.add_transactions()) + iroha::protocol::Transaction(tx.getTransport()); + }); + + auto error = + framework::expected::err(TransactionSequenceBuilder().build(tx_list)); ASSERT_TRUE(error); } @@ -537,14 +556,17 @@ TEST_F(TransportBuilderTest, TransactionInteraptedBatch) { * @then built an error */ TEST_F(TransportBuilderTest, BatchWrongOrder) { - std::vector transactions; + iroha::protocol::TxList tx_list; auto batch = getValidBatch(0, 10); - std::move( - std::begin(batch) + 3, std::end(batch), std::back_inserter(transactions)); - std::move(std::begin(batch), - std::begin(batch) + 3, - std::back_inserter(transactions)); - auto error = framework::expected::err( - TransactionSequenceBuilder().build(transactions)); + std::for_each(std::begin(batch) + 3, std::end(batch), [&tx_list](auto &tx) { + new (tx_list.add_transactions()) + iroha::protocol::Transaction(tx.getTransport()); + }); + std::for_each(std::begin(batch), std::begin(batch) + 3, [&tx_list](auto &tx) { + new (tx_list.add_transactions()) + iroha::protocol::Transaction(tx.getTransport()); + }); + auto error = + framework::expected::err(TransactionSequenceBuilder().build(tx_list)); ASSERT_TRUE(error); } From 31d316d8f42a386e2e4cbeb361c1d700a5942052 Mon Sep 17 00:00:00 2001 From: Nikita Alekseev Date: Tue, 17 Jul 2018 16:56:35 +0300 Subject: [PATCH 52/97] Noncopyable proposal (#1567) Proposal is now implemented as move-only type (can still be copied via clone) Signed-off-by: Nikita Alekseev --- shared_model/backend/protobuf/CMakeLists.txt | 1 + .../backend/protobuf/impl/proposal.cpp | 35 +++++++++++++ shared_model/backend/protobuf/proposal.hpp | 52 ++++++------------- test/benchmark/bm_proto_creation.cpp | 28 +++++++--- .../protobuf/transport_builder_test.cpp | 2 +- 5 files changed, 74 insertions(+), 44 deletions(-) create mode 100644 shared_model/backend/protobuf/impl/proposal.cpp diff --git a/shared_model/backend/protobuf/CMakeLists.txt b/shared_model/backend/protobuf/CMakeLists.txt index ffafd6243f..afa9734548 100644 --- a/shared_model/backend/protobuf/CMakeLists.txt +++ b/shared_model/backend/protobuf/CMakeLists.txt @@ -5,6 +5,7 @@ add_library(shared_model_proto_backend impl/block.cpp + impl/proposal.cpp impl/permissions.cpp commands/impl/proto_add_asset_quantity.cpp commands/impl/proto_add_peer.cpp diff --git a/shared_model/backend/protobuf/impl/proposal.cpp b/shared_model/backend/protobuf/impl/proposal.cpp new file mode 100644 index 0000000000..53ddd5c71b --- /dev/null +++ b/shared_model/backend/protobuf/impl/proposal.cpp @@ -0,0 +1,35 @@ +/** + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "backend/protobuf/proposal.hpp" + +namespace shared_model { + namespace proto { + using namespace interface::types; + + Proposal::Proposal(Proposal &&o) noexcept + : NonCopyableProto(std::move(o.proto_)) {} + + Proposal &Proposal::operator=(Proposal &&o) noexcept { + proto_ = std::move(o.proto_); + transactions_.invalidate(); + + return *this; + } + + TransactionsCollectionType Proposal::transactions() const { + return *transactions_; + } + + TimestampType Proposal::createdTime() const { + return proto_.created_time(); + } + + HeightType Proposal::height() const { + return proto_.height(); + } + + } // namespace proto +} // namespace shared_model diff --git a/shared_model/backend/protobuf/proposal.hpp b/shared_model/backend/protobuf/proposal.hpp index 56a9f67f74..e16d0e4719 100644 --- a/shared_model/backend/protobuf/proposal.hpp +++ b/shared_model/backend/protobuf/proposal.hpp @@ -1,18 +1,6 @@ /** - * Copyright Soramitsu Co., Ltd. 2018 All Rights Reserved. - * http://soramitsu.co.jp - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 */ #ifndef IROHA_SHARED_MODEL_PROTO_PROPOSAL_HPP @@ -21,48 +9,38 @@ #include "backend/protobuf/transaction.hpp" #include "interfaces/iroha_internal/proposal.hpp" -#include "common_objects/trivial_proto.hpp" +#include "common_objects/noncopyable_proto.hpp" #include "interfaces/common_objects/types.hpp" #include "proposal.pb.h" #include "utils/lazy_initializer.hpp" namespace shared_model { - namespace proto { - class Proposal final : public CopyableProto { + class Proposal final : public NonCopyableProto { public: - template - explicit Proposal(ProposalType &&proposal) - : CopyableProto(std::forward(proposal)) {} - - Proposal(const Proposal &o) : Proposal(o.proto_) {} + using NonCopyableProto::NonCopyableProto; - Proposal(Proposal &&o) noexcept : Proposal(std::move(o.proto_)) {} + Proposal(Proposal &&o) noexcept; + Proposal &operator=(Proposal &&o) noexcept; interface::types::TransactionsCollectionType transactions() - const override { - return *transactions_; - } + const override; - interface::types::TimestampType createdTime() const override { - return proto_->created_time(); - } + interface::types::TimestampType createdTime() const override; - interface::types::HeightType height() const override { - return proto_->height(); - } + interface::types::HeightType height() const override; private: - // lazy template using Lazy = detail::LazyInitializer; const Lazy> transactions_{[this] { - return std::vector(proto_->transactions().begin(), - proto_->transactions().end()); + return std::vector( + proto_.mutable_transactions()->begin(), + proto_.mutable_transactions()->end()); }}; }; } // namespace proto diff --git a/test/benchmark/bm_proto_creation.cpp b/test/benchmark/bm_proto_creation.cpp index 521442eafa..73c44fc554 100644 --- a/test/benchmark/bm_proto_creation.cpp +++ b/test/benchmark/bm_proto_creation.cpp @@ -11,8 +11,8 @@ * * The purpose of this benchmark is to keep track of performance costs related * to blocks and proposals copying/moving. - * - * Each benchmark runs transaction() and commands() call to + * + * Each benchmark runs transaction() and commands() call to * initialize possibly lazy fields. */ @@ -171,9 +171,9 @@ BENCHMARK_DEFINE_F(BlockBenchmark, CloneTest)(benchmark::State &st) { } /** - * Benchmark proposal creation by copying another proposal + * Benchmark proposal creation by copying protobuf object */ -BENCHMARK_DEFINE_F(ProposalBenchmark, CopyTest)(benchmark::State &st) { +BENCHMARK_DEFINE_F(ProposalBenchmark, TransportCopyTest)(benchmark::State &st) { while (st.KeepRunning()) { auto proposal = complete_builder.build(); @@ -184,6 +184,21 @@ BENCHMARK_DEFINE_F(ProposalBenchmark, CopyTest)(benchmark::State &st) { } } +/** + * Benchmark proposal creation by moving protobuf object + */ +BENCHMARK_DEFINE_F(ProposalBenchmark, TransportMoveTest)(benchmark::State &st) { + while (st.KeepRunning()) { + auto proposal = complete_builder.build(); + iroha::protocol::Proposal proto_proposal = proposal.getTransport(); + + runBenchmark(st, [&proto_proposal] { + shared_model::proto::Proposal copy(std::move(proto_proposal)); + checkLoop(copy); + }); + } +} + /** * Benchmark proposal creation by moving another proposal */ @@ -213,11 +228,12 @@ BENCHMARK_DEFINE_F(ProposalBenchmark, CloneTest)(benchmark::State &st) { } BENCHMARK_REGISTER_F(BlockBenchmark, MoveTest)->UseManualTime(); +BENCHMARK_REGISTER_F(BlockBenchmark, CloneTest)->UseManualTime(); BENCHMARK_REGISTER_F(BlockBenchmark, TransportMoveTest)->UseManualTime(); BENCHMARK_REGISTER_F(BlockBenchmark, TransportCopyTest)->UseManualTime(); -BENCHMARK_REGISTER_F(BlockBenchmark, CloneTest)->UseManualTime(); -BENCHMARK_REGISTER_F(ProposalBenchmark, CopyTest)->UseManualTime(); BENCHMARK_REGISTER_F(ProposalBenchmark, MoveTest)->UseManualTime(); BENCHMARK_REGISTER_F(ProposalBenchmark, CloneTest)->UseManualTime(); +BENCHMARK_REGISTER_F(ProposalBenchmark, TransportMoveTest)->UseManualTime(); +BENCHMARK_REGISTER_F(ProposalBenchmark, TransportCopyTest)->UseManualTime(); BENCHMARK_MAIN(); diff --git a/test/module/shared_model/builders/protobuf/transport_builder_test.cpp b/test/module/shared_model/builders/protobuf/transport_builder_test.cpp index cfad680c7a..2d8f50b385 100644 --- a/test/module/shared_model/builders/protobuf/transport_builder_test.cpp +++ b/test/module/shared_model/builders/protobuf/transport_builder_test.cpp @@ -359,7 +359,7 @@ TEST_F(TransportBuilderTest, DISABLED_EmptyProposalCreationTest) { auto orig_model = createEmptyProposal(); testTransport( orig_model, - [](const Value) { FAIL(); }, + [](const Value &) { FAIL(); }, [](const Error &) { SUCCEED(); }); } From a4950d42594004e913a6e6a5b1947f7113ef7b6d Mon Sep 17 00:00:00 2001 From: Nikita Alekseev Date: Tue, 17 Jul 2018 19:14:41 +0300 Subject: [PATCH 53/97] Common objects factory (#1556) Allows to polymorphically create common objects Signed-off-by: Nikita Alekseev --- .../proto_common_objects_factory.hpp | 201 +++++++++++++ .../common_objects/common_objects_factory.hpp | 79 ++++++ .../shared_model/backend_proto/CMakeLists.txt | 9 + .../proto_common_objects_factory_test.cpp | 267 ++++++++++++++++++ 4 files changed, 556 insertions(+) create mode 100644 shared_model/backend/protobuf/common_objects/proto_common_objects_factory.hpp create mode 100644 shared_model/interfaces/common_objects/common_objects_factory.hpp create mode 100644 test/module/shared_model/backend_proto/common_objects/proto_common_objects_factory_test.cpp diff --git a/shared_model/backend/protobuf/common_objects/proto_common_objects_factory.hpp b/shared_model/backend/protobuf/common_objects/proto_common_objects_factory.hpp new file mode 100644 index 0000000000..5da490fc3f --- /dev/null +++ b/shared_model/backend/protobuf/common_objects/proto_common_objects_factory.hpp @@ -0,0 +1,201 @@ +/** + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef IROHA_PROTO_COMMON_OBJECTS_FACTORY_HPP +#define IROHA_PROTO_COMMON_OBJECTS_FACTORY_HPP + +#include + +#include "backend/protobuf/common_objects/account.hpp" +#include "backend/protobuf/common_objects/account_asset.hpp" +#include "backend/protobuf/common_objects/asset.hpp" +#include "backend/protobuf/common_objects/domain.hpp" +#include "backend/protobuf/common_objects/peer.hpp" +#include "backend/protobuf/common_objects/signature.hpp" +#include "common/result.hpp" +#include "interfaces/common_objects/common_objects_factory.hpp" +#include "primitive.pb.h" +#include "validators/answer.hpp" + +namespace shared_model { + namespace proto { + /** + * ProtoCommonObjectsFactory constructs protobuf-based objects. + * It performs stateless validation with provided validator + * @tparam Validator + */ + template + class ProtoCommonObjectsFactory : public interface::CommonObjectsFactory { + public: + FactoryResult> createPeer( + const interface::types::AddressType &address, + const interface::types::PubkeyType &public_key) override { + iroha::protocol::Peer peer; + peer.set_address(address); + peer.set_peer_key(crypto::toBinaryString(public_key)); + auto proto_peer = std::make_unique(std::move(peer)); + + auto errors = + validate(*proto_peer, [this](const auto &peer, auto &reasons) { + validator_.validatePeer(reasons, peer); + }); + + if (errors) { + return iroha::expected::makeError(errors.reason()); + } + + return iroha::expected::makeValue>( + std::move(proto_peer)); + } + + FactoryResult> createAccount( + const interface::types::AccountIdType &account_id, + const interface::types::DomainIdType &domain_id, + interface::types::QuorumType quorum, + const interface::types::JsonType &jsonData) override { + iroha::protocol::Account account; + account.set_account_id(account_id); + account.set_domain_id(domain_id); + account.set_quorum(quorum); + account.set_json_data(jsonData); + + auto proto_account = std::make_unique(std::move(account)); + + auto errors = validate( + *proto_account, [this](const auto &account, auto &reasons) { + validator_.validateAccountId(reasons, account.accountId()); + validator_.validateDomainId(reasons, account.domainId()); + validator_.validateQuorum(reasons, account.quorum()); + }); + + if (errors) { + return iroha::expected::makeError(errors.reason()); + } + + return iroha::expected::makeValue>( + std::move(proto_account)); + } + + FactoryResult> + createAccountAsset(const interface::types::AccountIdType &account_id, + const interface::types::AssetIdType &asset_id, + const interface::Amount &balance) override { + iroha::protocol::AccountAsset asset; + asset.set_account_id(account_id); + asset.set_asset_id(asset_id); + asset.set_balance(balance.toStringRepr()); + + auto proto_asset = std::make_unique(std::move(asset)); + + auto errors = + validate(*proto_asset, [this](const auto &asset, auto &reasons) { + validator_.validateAccountId(reasons, asset.accountId()); + validator_.validateAssetId(reasons, asset.assetId()); + }); + + if (errors) { + return iroha::expected::makeError(errors.reason()); + } + + return iroha::expected::makeValue< + std::unique_ptr>(std::move(proto_asset)); + } + + FactoryResult> createAsset( + const interface::types::AssetIdType &asset_id, + const interface::types::DomainIdType &domain_id, + interface::types::PrecisionType precision) override { + iroha::protocol::Asset asset; + asset.set_asset_id(asset_id); + asset.set_domain_id(domain_id); + asset.set_precision(precision); + + auto proto_asset = std::make_unique(std::move(asset)); + + auto errors = + validate(*proto_asset, [this](const auto &asset, auto &reasons) { + validator_.validateAssetId(reasons, asset.assetId()); + validator_.validateDomainId(reasons, asset.domainId()); + }); + + if (errors) { + return iroha::expected::makeError(errors.reason()); + } + + return iroha::expected::makeValue>( + std::move(proto_asset)); + } + + FactoryResult> createDomain( + const interface::types::DomainIdType &domain_id, + const interface::types::RoleIdType &default_role) override { + iroha::protocol::Domain domain; + domain.set_domain_id(domain_id); + domain.set_default_role(default_role); + + auto proto_domain = std::make_unique(std::move(domain)); + + auto errors = + validate(*proto_domain, [this](const auto &domain, auto &reason) { + validator_.validateDomainId(reason, domain.domainId()); + validator_.validateRoleId(reason, domain.defaultRole()); + }); + + if (errors) { + return iroha::expected::makeError(errors.reason()); + } + + return iroha::expected::makeValue>( + std::move(proto_domain)); + } + + FactoryResult> createSignature( + const interface::types::PubkeyType &key, + const interface::Signature::SignedType &signed_data) override { + iroha::protocol::Signature signature; + signature.set_pubkey(crypto::toBinaryString(key)); + signature.set_signature(crypto::toBinaryString(signed_data)); + + auto proto_singature = + std::make_unique(std::move(signature)); + + auto errors = validate( + *proto_singature, [this](const auto &signature, auto &reason) { + validator_.validatePubkey(reason, signature.publicKey()); + }); + + if (errors) { + return iroha::expected::makeError(errors.reason()); + } + + return iroha::expected::makeValue< + std::unique_ptr>(std::move(proto_singature)); + } + + private: + /** + * Perform validation of a given object + * @param o - object to be validated + * @param f - function which populates reason parameter with errors. + * second parameter (reasons) must be passed by non-const reference + * @return validation result + */ + template + validation::Answer validate(const T &o, ValidationFunc &&f) const { + shared_model::validation::Answer errors; + validation::ReasonsGroupType reasons; + f(o, reasons); + if (not reasons.second.empty()) { + errors.addReason(std::move(reasons)); + } + return errors; + } + + Validator validator_; + }; + } // namespace proto +} // namespace shared_model + +#endif // IROHA_PROTO_COMMON_OBJECTS_FACTORY_HPP diff --git a/shared_model/interfaces/common_objects/common_objects_factory.hpp b/shared_model/interfaces/common_objects/common_objects_factory.hpp new file mode 100644 index 0000000000..0381a71915 --- /dev/null +++ b/shared_model/interfaces/common_objects/common_objects_factory.hpp @@ -0,0 +1,79 @@ +/** + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef IROHA_COMMON_OBJECTS_FACTORY_HPP +#define IROHA_COMMON_OBJECTS_FACTORY_HPP + +#include + +#include "common/result.hpp" +#include "interfaces/common_objects/account.hpp" +#include "interfaces/common_objects/peer.hpp" +#include "interfaces/common_objects/types.hpp" +#include "interfaces/common_objects/domain.hpp" + +namespace shared_model { + namespace interface { + /** + * CommonObjectsFactory provides methods to construct common objects + * such as peer, account etc. + */ + class CommonObjectsFactory { + public: + template + using FactoryResult = iroha::expected::Result; + + /** + * Create peer instance + */ + virtual FactoryResult> createPeer( + const types::AddressType &address, + const types::PubkeyType &public_key) = 0; + + /** + * Create account instance + */ + virtual FactoryResult> createAccount( + const types::AccountIdType &account_id, + const types::DomainIdType &domain_id, + types::QuorumType quorum, + const types::JsonType &jsonData) = 0; + + /** + * Create account asset instance + */ + virtual FactoryResult> createAccountAsset( + const types::AccountIdType &account_id, + const types::AssetIdType &asset_id, + const Amount &balance) = 0; + + /** + * Create asset instance + */ + virtual FactoryResult> createAsset( + const types::AssetIdType &asset_id, + const types::DomainIdType &domain_id, + types::PrecisionType precision) = 0; + + /** + * Create domain instance + */ + virtual FactoryResult> createDomain( + const types::DomainIdType &domain_id, + const types::RoleIdType &default_role) = 0; + + /** + * Create signature instance + */ + virtual FactoryResult> createSignature( + const interface::types::PubkeyType &key, + const interface::Signature::SignedType &signed_data) = 0; + + virtual ~CommonObjectsFactory() = default; + }; + } // namespace interface +} // namespace shared_model + +#endif // IROHA_COMMONOBJECTSFACTORY_HPP diff --git a/test/module/shared_model/backend_proto/CMakeLists.txt b/test/module/shared_model/backend_proto/CMakeLists.txt index 303d398f94..9bd28b48f5 100644 --- a/test/module/shared_model/backend_proto/CMakeLists.txt +++ b/test/module/shared_model/backend_proto/CMakeLists.txt @@ -79,3 +79,12 @@ target_link_libraries(proto_transaction_sequence_test shared_model_proto_backend shared_model_stateless_validation ) + +addtest(proto_common_objects_factory_test + common_objects/proto_common_objects_factory_test.cpp + ) +target_link_libraries(proto_common_objects_factory_test + shared_model_cryptography + shared_model_stateless_validation + schema + ) diff --git a/test/module/shared_model/backend_proto/common_objects/proto_common_objects_factory_test.cpp b/test/module/shared_model/backend_proto/common_objects/proto_common_objects_factory_test.cpp new file mode 100644 index 0000000000..e3a76e3f6b --- /dev/null +++ b/test/module/shared_model/backend_proto/common_objects/proto_common_objects_factory_test.cpp @@ -0,0 +1,267 @@ +/** + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#include "backend/protobuf/common_objects/proto_common_objects_factory.hpp" +#include "builders/default_builders.hpp" +#include "cryptography/crypto_provider/crypto_defaults.hpp" +#include "framework/result_fixture.hpp" +#include "validators/field_validator.hpp" + +using namespace shared_model; +using namespace framework::expected; + +proto::ProtoCommonObjectsFactory factory; + +class PeerTest : public ::testing::Test { + public: + std::string valid_address = "127.0.0.1:8080"; + crypto::PublicKey valid_pubkey = + crypto::DefaultCryptoAlgorithmType::generateKeypair().publicKey(); + std::string invalid_address = "127.0.0.1"; +}; + +/** + * @given valid data for peer + * @when peer is created via factory + * @then peer is successfully initialized + */ +TEST_F(PeerTest, ValidPeerInitialization) { + auto peer = factory.createPeer(valid_address, valid_pubkey); + + peer.match( + [&](const ValueOf &v) { + ASSERT_EQ(v.value->address(), valid_address); + ASSERT_EQ(v.value->pubkey().hex(), valid_pubkey.hex()); + }, + [](const ErrorOf &e) { FAIL() << e.error; }); +} + +/** + * @given invalid data for peer + * @when peer is created via factory + * @then peer is not initialized correctly + */ +TEST_F(PeerTest, InvalidPeerInitialization) { + auto peer = factory.createPeer(invalid_address, valid_pubkey); + + peer.match( + [](const ValueOf &v) { FAIL() << "Expected error case"; }, + [](const ErrorOf &e) { SUCCEED(); }); +} + +class AccountTest : public ::testing::Test { + public: + interface::types::AccountIdType valid_account_id = "hello@world"; + interface::types::DomainIdType valid_domain_id = "bit.connect"; + interface::types::QuorumType valid_quorum = 1; + interface::types::JsonType valid_json = R"({"name": "json" })"; + + interface::types::AccountIdType invalid_account_id = "hello123"; +}; + +/** + * @given valid data for account + * @when account is created via factory + * @then peer is successfully initialized + */ +TEST_F(AccountTest, ValidAccountInitialization) { + auto account = factory.createAccount( + valid_account_id, valid_domain_id, valid_quorum, valid_json); + + account.match( + [&](const ValueOf &v) { + ASSERT_EQ(v.value->accountId(), valid_account_id); + ASSERT_EQ(v.value->domainId(), valid_domain_id); + ASSERT_EQ(v.value->quorum(), valid_quorum); + ASSERT_EQ(v.value->jsonData(), valid_json); + }, + [](const ErrorOf &e) { FAIL() << e.error; }); +} + +/** + * @given invalid data for account + * @when account is created via factory + * @then account is not initialized correctly + */ +TEST_F(AccountTest, InvalidAccountInitialization) { + auto account = factory.createAccount( + invalid_account_id, valid_domain_id, valid_quorum, valid_json); + + account.match( + [](const ValueOf &v) { + FAIL() << "Expected error case"; + }, + [](const ErrorOf &e) { SUCCEED(); }); +} + +class AccountAssetTest : public ::testing::Test { + public: + interface::types::AccountIdType valid_account_id = "hello@world"; + interface::types::AssetIdType valid_asset_id = "bit#connect"; + interface::Amount valid_amount = interface::Amount("10.00"); + + interface::types::AccountIdType invalid_account_id = "hello123"; +}; + +/** + * @given valid data for account asset + * @when account asset is created via factory + * @then account asset is successfully initialized + */ +TEST_F(AccountAssetTest, ValidAccountAssetInitialization) { + auto account_asset = factory.createAccountAsset( + valid_account_id, valid_asset_id, valid_amount); + + account_asset.match( + [&](const ValueOf &v) { + ASSERT_EQ(v.value->accountId(), valid_account_id); + ASSERT_EQ(v.value->assetId(), valid_asset_id); + ASSERT_EQ(v.value->balance(), valid_amount); + }, + [](const ErrorOf &e) { FAIL() << e.error; }); +} + +/** + * @given invalid data for account asset + * @when account asset is created via factory + * @then account asset is not initialized correctly + */ +TEST_F(AccountAssetTest, InvalidAccountAssetInitialization) { + auto account_asset = factory.createAccountAsset( + invalid_account_id, valid_asset_id, valid_amount); + + account_asset.match( + [](const ValueOf &v) { + FAIL() << "Expected error case"; + }, + [](const ErrorOf &e) { SUCCEED(); }); +} + +class AssetTest : public ::testing::Test { + public: + interface::types::AssetIdType valid_asset_id = "bit#connect"; + interface::types::DomainIdType valid_domain_id = "iroha.com"; + interface::types::PrecisionType valid_precision = 2; + + interface::types::AssetIdType invalid_asset_id = "bit"; +}; + +/** + * @given valid data for asset + * @when asset is created via factory + * @then asset is successfully initialized + */ +TEST_F(AssetTest, ValidAssetInitialization) { + auto asset = + factory.createAsset(valid_asset_id, valid_domain_id, valid_precision); + + asset.match( + [&](const ValueOf &v) { + ASSERT_EQ(v.value->assetId(), valid_asset_id); + ASSERT_EQ(v.value->domainId(), valid_domain_id); + ASSERT_EQ(v.value->precision(), valid_precision); + }, + [](const ErrorOf &e) { FAIL() << e.error; }); +} + +/** + * @given invalid data for asset + * @when asset is created via factory + * @then asset is not initialized correctly + */ +TEST_F(AssetTest, InvalidAssetInitialization) { + auto asset = + factory.createAsset(invalid_asset_id, valid_domain_id, valid_precision); + + asset.match( + [](const ValueOf &v) { + FAIL() << "Expected error case"; + }, + [](const ErrorOf &e) { SUCCEED(); }); +} + +class DomainTest : public ::testing::Test { + public: + interface::types::DomainIdType valid_domain_id = "iroha.com"; + interface::types::RoleIdType valid_role_id = "admin"; + + interface::types::DomainIdType invalid_domain_id = "123irohacom"; +}; + +/** + * @given valid data for domain + * @when domain is created via factory + * @then domain is successfully initialized + */ +TEST_F(DomainTest, ValidDomainInitialization) { + auto domain = + factory.createDomain(valid_domain_id, valid_role_id); + + domain.match( + [&](const ValueOf &v) { + ASSERT_EQ(v.value->domainId(), valid_domain_id); + ASSERT_EQ(v.value->defaultRole(), valid_role_id); + }, + [](const ErrorOf &e) { FAIL() << e.error; }); +} + +/** + * @given invalid data for domain + * @when domain is created via factory + * @then domain is not initialized correctly + */ +TEST_F(DomainTest, InvalidDomainInitialization) { + auto domain = + factory.createDomain(invalid_domain_id, valid_role_id); + + domain.match( + [](const ValueOf &v) { + FAIL() << "Expected error case"; + }, + [](const ErrorOf &e) { SUCCEED(); }); +} + +class SignatureTest : public ::testing::Test { + public: + crypto::PublicKey valid_pubkey = + crypto::DefaultCryptoAlgorithmType::generateKeypair().publicKey(); + crypto::Signed valid_data{"hello"}; + crypto::PublicKey invalid_pubkey{"1234"}; +}; + +/** + * @given valid data for signature + * @when signature is created via factory + * @then signature is successfully initialized + */ +TEST_F(SignatureTest, ValidSignatureInitialization) { + auto signature = + factory.createSignature(valid_pubkey, valid_data); + + signature.match( + [&](const ValueOf &v) { + ASSERT_EQ(v.value->publicKey().hex(), valid_pubkey.hex()); + ASSERT_EQ(v.value->signedData().hex(), valid_data.hex()); + }, + [](const ErrorOf &e) { FAIL() << e.error; }); +} + +/** + * @given invalid data for signature + * @when signature is created via factory + * @then signature is not initialized correctly + */ +TEST_F(SignatureTest, InvalidSignatureInitialization) { + auto signature = + factory.createSignature(invalid_pubkey, valid_data); + + signature.match( + [](const ValueOf &v) { + FAIL() << "Expected error case"; + }, + [](const ErrorOf &e) { SUCCEED(); }); +} From b75291c31c8321d6aebfe6d20831d5a5b0aa628a Mon Sep 17 00:00:00 2001 From: Kitsu Date: Wed, 18 Jul 2018 15:18:08 +0300 Subject: [PATCH 54/97] New client api (#1543) Signed-off-by: Kitsu --- shared_model/bindings/CMakeLists.txt | 1 + shared_model/bindings/bindings.i | 2 + shared_model/bindings/client_api.cpp | 124 +++++++++++++++ shared_model/bindings/client_api.hpp | 56 +++++++ .../irohad/torii/torii_service_test.cpp | 3 +- .../shared_model/bindings/CMakeLists.txt | 7 +- .../shared_model/bindings/ClientTest.java | 141 ++++++++++++++++++ .../shared_model/bindings/client-test.py | 65 ++++++++ 8 files changed, 397 insertions(+), 2 deletions(-) create mode 100644 shared_model/bindings/client_api.cpp create mode 100644 shared_model/bindings/client_api.hpp create mode 100644 test/module/shared_model/bindings/ClientTest.java create mode 100644 test/module/shared_model/bindings/client-test.py diff --git a/shared_model/bindings/CMakeLists.txt b/shared_model/bindings/CMakeLists.txt index 98e7fedb10..8337f6923a 100644 --- a/shared_model/bindings/CMakeLists.txt +++ b/shared_model/bindings/CMakeLists.txt @@ -18,6 +18,7 @@ add_library(bindings model_query_builder.cpp model_blocks_query_builder.cpp model_crypto.cpp + client_api.cpp ) target_link_libraries(bindings diff --git a/shared_model/bindings/bindings.i b/shared_model/bindings/bindings.i index 07ed2f83da..e530555100 100644 --- a/shared_model/bindings/bindings.i +++ b/shared_model/bindings/bindings.i @@ -124,6 +124,7 @@ namespace std { #include "bindings/model_crypto.hpp" #include "bindings/model_proto.hpp" #include "builders/protobuf/unsigned_proto.hpp" +#include "bindings/client_api.hpp" %} %include "cryptography/blob.hpp" @@ -146,6 +147,7 @@ namespace std { %include "bindings/model_blocks_query_builder.hpp" %include "bindings/model_crypto.hpp" %include "bindings/model_proto.hpp" +%include "bindings/client_api.hpp" %template (UnsignedTx) shared_model::proto::UnsignedWrapper; %template (UnsignedQuery) shared_model::proto::UnsignedWrapper; diff --git a/shared_model/bindings/client_api.cpp b/shared_model/bindings/client_api.cpp new file mode 100644 index 0000000000..e989bd95cf --- /dev/null +++ b/shared_model/bindings/client_api.cpp @@ -0,0 +1,124 @@ +/** + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "bindings/client_api.hpp" +#include "backend/protobuf/transaction.hpp" +#include "backend/protobuf/util.hpp" +#include "common/types.hpp" +#include "cryptography/crypto_provider/crypto_signer.hpp" +#include "cryptography/ed25519_sha3_impl/internal/sha3_hash.hpp" +#include "validators/default_validator.hpp" + +namespace shared_model { + namespace bindings { + + std::string convert(const Blob &blob) { + return std::string(blob.begin(), blob.end()); + } + + template + boost::optional get(const std::string &blob) { + Proto proto; + if (proto.ParseFromString(blob)) { + return proto; + } + return {}; + } + + using namespace iroha; + void validateTransaction(const Blob &b) { + auto blob = convert(b); + auto s = get(blob) | [](auto tx) { + static validation::DefaultSignableTransactionValidator val; + return boost::make_optional( + val.validate(proto::Transaction(tx)).reason()); + }; + if (s) { + auto &r = s.value(); + if (r == "") { + return; + } + throw std::invalid_argument(r); + } + throw std::invalid_argument("unknown object"); + } + + void validateQuery(const Blob &b) { + auto blob = convert(b); + auto s = get(blob) | [](auto qry) { + static validation::DefaultSignableQueryValidator val; + return boost::make_optional(val.validate(proto::Query(qry)).reason()); + }; + if (s) { + auto &r = s.value(); + if (r == "") { + return; + } + throw std::invalid_argument(r); + } + throw std::invalid_argument("unknown object"); + } + + Blob signTransaction(const Blob &b, const crypto::Keypair &key) { + auto blob = convert(b); + auto s = get(blob) | [&key](auto tx) { + auto signature = + crypto::CryptoSigner<>::sign(proto::makeBlob(tx.payload()), key); + + auto sig = tx.add_signatures(); + sig->set_signature(crypto::toBinaryString(signature)); + sig->set_pubkey(crypto::toBinaryString(key.publicKey())); + return boost::make_optional(tx); + }; + if (s) { + return proto::makeBlob(s.value()).blob(); + } + throw std::invalid_argument("unknown object"); + } + + Blob signQuery(const Blob &b, const crypto::Keypair &key) { + auto blob = convert(b); + auto s = get(blob) | [&key](auto qry) { + auto signature = + crypto::CryptoSigner<>::sign(proto::makeBlob(qry.payload()), key); + + auto sig = qry.mutable_signature(); + sig->set_signature(crypto::toBinaryString(signature)); + sig->set_pubkey(crypto::toBinaryString(key.publicKey())); + return boost::make_optional(qry); + }; + if (s) { + return proto::makeBlob(s.value()).blob(); + } + throw std::invalid_argument("unknown object"); + } + + Blob hashTransaction(const Blob &b) { + auto blob = convert(b); + auto s = get(blob) | [](auto tx) { + auto pl = proto::makeBlob(tx.payload()); + return boost::make_optional( + sha3_256(pl.blob().data(), pl.blob().size())); + }; + if (s) { + return Blob(s->begin(), s->end()); + } + throw std::invalid_argument("unknown object"); + } + + Blob hashQuery(const Blob &b) { + auto blob = convert(b); + auto s = get(blob) | [](auto qry) { + auto pl = proto::makeBlob(qry.payload()); + return boost::make_optional( + sha3_256(pl.blob().data(), pl.blob().size())); + }; + if (s) { + return Blob(s->begin(), s->end()); + } + throw std::invalid_argument("unknown object"); + } + } // namespace bindings +} // namespace shared_model diff --git a/shared_model/bindings/client_api.hpp b/shared_model/bindings/client_api.hpp new file mode 100644 index 0000000000..591ba18b70 --- /dev/null +++ b/shared_model/bindings/client_api.hpp @@ -0,0 +1,56 @@ +/** + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "cryptography/keypair.hpp" + +namespace shared_model { + namespace bindings { + using Blob = std::vector; + + /** + * Validate protobuf transaction + * @param blob to validate + * @return string with errors, empty if none + */ + void validateTransaction(const Blob &); + + /** + * Validate protobuf query + * @param blob to validate + * @return string with errors, empty if none + */ + void validateQuery(const Blob &); + + /** + * Signs protobuf transaction + * @param blob to sign + * @param key is keypair for signing + * @return signed blob + */ + Blob signTransaction(const Blob &, const crypto::Keypair &); + + /** + * Signs protobuf query + * @param blob to sign + * @param key is keypair for signing + * @return signed blob + */ + Blob signQuery(const Blob &, const crypto::Keypair &); + + /** + * Get the hash of given protobuf transaction + * @param blob to calculate hash from + * @return hash of the blob + */ + Blob hashTransaction(const Blob &); + + /** + * Get the hash of given protobuf query + * @param blob to calculate hash from + * @return hash of the blob + */ + Blob hashQuery(const Blob &); + } // namespace bindings +} // namespace shared_model diff --git a/test/module/irohad/torii/torii_service_test.cpp b/test/module/irohad/torii/torii_service_test.cpp index a3def8102f..3f1f35cfcf 100644 --- a/test/module/irohad/torii/torii_service_test.cpp +++ b/test/module/irohad/torii/torii_service_test.cpp @@ -576,7 +576,8 @@ TEST_F(ToriiServiceTest, FailedListOfTxs) { tx_request.set_tx_hash(shared_model::crypto::toBinaryString(hash)); iroha::protocol::ToriiResponse toriiResponse; client.Status(tx_request, toriiResponse); - auto error_beginning = toriiResponse.error_message().substr(0, toriiResponse.error_message().find_first_of('.')); + auto error_beginning = toriiResponse.error_message().substr( + 0, toriiResponse.error_message().find_first_of('.')); ASSERT_EQ(toriiResponse.tx_status(), iroha::protocol::TxStatus::STATELESS_VALIDATION_FAILED); diff --git a/test/module/shared_model/bindings/CMakeLists.txt b/test/module/shared_model/bindings/CMakeLists.txt index e533e6b756..dbafdac111 100644 --- a/test/module/shared_model/bindings/CMakeLists.txt +++ b/test/module/shared_model/bindings/CMakeLists.txt @@ -65,6 +65,11 @@ if (SWIG_PYTHON) env "PYTHONPATH=${SWIG_LIB_DIR}${SEPARATOR}$ENV{PYTHONPATH}" ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/blocks-query-test.py WORKING_DIRECTORY ${SWIG_BUILD_DIR}) + add_test(NAME python_client_test + COMMAND ${CMAKE_COMMAND} -E + env "PYTHONPATH=${SWIG_LIB_DIR}${SEPARATOR}$ENV{PYTHONPATH}" + ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/client-test.py + WORKING_DIRECTORY ${SWIG_BUILD_DIR}) foreach(item "block" "transaction" "commands" "primitive" "queries") compile_proto_to_python("${item}.proto") @@ -73,7 +78,7 @@ if (SWIG_PYTHON) add_custom_target(python_tests ALL DEPENDS "${PROTO_SWIG_DEPS}") - foreach(test "python_transaction_test" "python_query_test" "python_blocks_query_test") + foreach(test "python_transaction_test" "python_query_test" "python_blocks_query_test" "python_client_test") set_tests_properties(${test} PROPERTIES REQUIRED_FILES "${PROTO_SWIG_DEPS}" DEPENDS python_tests) diff --git a/test/module/shared_model/bindings/ClientTest.java b/test/module/shared_model/bindings/ClientTest.java new file mode 100644 index 0000000000..bc40380463 --- /dev/null +++ b/test/module/shared_model/bindings/ClientTest.java @@ -0,0 +1,141 @@ +/** + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Disabled; + +import java.math.BigInteger; +import java.io.InputStream; +import java.io.IOException; + +import iroha.protocol.*; +import iroha.protocol.TransactionOuterClass.Transaction; +import iroha.protocol.Commands.Command; +import com.google.protobuf.ByteString; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +public class ClientTest { + static { + try { + System.loadLibrary("irohajava"); + } catch (UnsatisfiedLinkError e) { + System.err.println("Native code library failed to load. \n" + e); + System.exit(1); + } + } + + private Keypair keys; + + @BeforeEach + void setUp() { + keys = new ModelCrypto().generateKeypair(); + } + + Command.Builder validAddPeer() { + return Command.newBuilder().setAddPeer(Commands.AddPeer.newBuilder().setPeer( + Primitive.Peer.newBuilder() + .setAddress("127.0.0.1:50500") + .setPeerKey(ByteString.copyFrom(String.format("%32.32s", " ").getBytes())))); + } + + Transaction.Payload.ReducedPayload.Builder defaultPayload() { + return Transaction.Payload.ReducedPayload.newBuilder() + .setCreatorAccountId("admin@test") + .setCreatedTime(System.currentTimeMillis()) + .setQuorum(1); + } + + ByteVector serialize(Transaction tx) { + ByteVector vec = new ByteVector(); + for (byte b : tx.toByteArray()) { + vec.add(b); + } + return vec; + } + + class ParseInputStream extends InputStream { + ParseInputStream(ByteVector vec) { + this.vec = vec; + index = 0; + } + + @Override + public int read() { + if (vec.size() > index) { + return vec.get(index++); + } + return -1; + } + + ByteVector vec; + int index; + } + + Transaction deserialize(ByteVector vec) { + System.out.println(vec.size()); + try { + return Transaction.parseFrom(new ParseInputStream(vec)); + } catch (IOException e) { + System.out.println(e.getMessage()); + System.exit(3); + } + return null; + } + + @Test + void hash() { + Transaction tx = Transaction.newBuilder() + .setPayload(Transaction.Payload.newBuilder().setReducedPayload( + defaultPayload().addCommands(validAddPeer()))) + .build(); + ByteVector h = iroha.hashTransaction(serialize(tx)); + assertEquals(h.size(), 32); + } + + @Test + void sign() { + Transaction tx = Transaction.newBuilder() + .setPayload(Transaction.Payload.newBuilder().setReducedPayload( + defaultPayload().addCommands(validAddPeer()))) + .build(); + assertEquals(tx.getSignaturesList().size(), 0); + Transaction signed_tx = deserialize(iroha.signTransaction(serialize(tx), keys)); + assertEquals(signed_tx.getSignaturesList().size(), 1); + } + + @Test + void validateWithoutCommand() { + Transaction tx = deserialize(iroha.signTransaction( + serialize( + Transaction.newBuilder() + .setPayload(Transaction.Payload.newBuilder().setReducedPayload(defaultPayload())) + .build()), + keys)); + assertThrows(IllegalArgumentException.class, () -> iroha.validateTransaction(serialize(tx))); + } + + @Test + void validateUnsigned() { + Transaction tx = Transaction.newBuilder() + .setPayload(Transaction.Payload.newBuilder().setReducedPayload( + defaultPayload().addCommands(validAddPeer()))) + .build(); + assertThrows(IllegalArgumentException.class, () -> iroha.validateTransaction(serialize(tx))); + } + + @Test + void validateCorrect() { + Transaction tx = deserialize( + iroha.signTransaction(serialize(Transaction.newBuilder() + .setPayload(Transaction.Payload.newBuilder().setReducedPayload( + defaultPayload().addCommands(validAddPeer()))) + .build()), + keys)); + iroha.validateTransaction(serialize(tx)); + } +} diff --git a/test/module/shared_model/bindings/client-test.py b/test/module/shared_model/bindings/client-test.py new file mode 100644 index 0000000000..3e184ba63c --- /dev/null +++ b/test/module/shared_model/bindings/client-test.py @@ -0,0 +1,65 @@ +# +# Copyright Soramitsu Co., Ltd. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +# + +from time import time +import unittest + +import transaction_pb2 as trx +import commands_pb2 as cmd +import iroha + +class ClientTest(unittest.TestCase): + def setUp(self): + self.keys = iroha.ModelCrypto().generateKeypair() + + def valid_add_peer_command(self): + command = cmd.Command() + command.add_peer.peer.address = "127.0.0.1:50500" + command.add_peer.peer.peer_key = b'A' * 32 + return command + + def unsigned_tx(self): + tx = trx.Transaction() + tx.payload.reduced_payload.creator_account_id = "admin@test" + tx.payload.reduced_payload.created_time = int(time() * 1000) + tx.payload.reduced_payload.quorum = 1 + return tx + + def test_hash(self): + tx = self.unsigned_tx() + tx.payload.reduced_payload.commands.extend([self.valid_add_peer_command()]) + h = iroha.hashTransaction(iroha.Blob(tx.SerializeToString()).blob()) + self.assertEqual(len(h), 32) + + def test_sign(self): + tx = self.unsigned_tx() + tx.payload.reduced_payload.commands.extend([self.valid_add_peer_command()]) + self.assertEqual(len(tx.signatures), 0) + tx_blob = iroha.signTransaction(iroha.Blob(tx.SerializeToString()).blob(), self.keys) + signed_tx = trx.Transaction() + signed_tx.ParseFromString(bytearray(tx_blob)) + self.assertEqual(len(signed_tx.signatures), 1) + + def test_validate_without_cmd(self): + tx = self.unsigned_tx() + tx_blob = iroha.signTransaction(iroha.Blob(tx.SerializeToString()).blob(), self.keys) + with self.assertRaises(ValueError): + iroha.validateTransaction(tx_blob) + + def test_validate_unsigned_tx(self): + tx = self.unsigned_tx() + tx.payload.reduced_payload.commands.extend([self.valid_add_peer_command()]) + self.assertEqual(len(tx.signatures), 0) + with self.assertRaises(ValueError): + iroha.validateTransaction(iroha.Blob(tx.SerializeToString()).blob()) + + def test_validate_correct_tx(self): + tx = self.unsigned_tx() + tx.payload.reduced_payload.commands.extend([self.valid_add_peer_command()]) + self.assertEqual(len(tx.signatures), 0) + iroha.signTransaction(iroha.Blob(tx.SerializeToString()).blob(), self.keys) + +if __name__ == '__main__': + unittest.main() From a02079e7cd7a5d8bc77bb7b7ee0abc0b4ee913bb Mon Sep 17 00:00:00 2001 From: Kitsu Date: Wed, 18 Jul 2018 15:19:07 +0300 Subject: [PATCH 55/97] Refactor QueryProcessor interface (#1572) Since simple queries are performed in synchronous manner there's a reason in queryNotifier removing and returning the response at queryHandle call. Also some functions was refactored, so that they accept reference, instead of shared_ptr. Signed-off-by: Kitsu --- irohad/torii/impl/query_service.cpp | 35 ++------ .../processor/impl/query_processor_impl.cpp | 32 +++---- irohad/torii/processor/query_processor.hpp | 19 ++-- .../torii/processor/query_processor_impl.hpp | 28 +----- .../torii/processor/query_processor_test.cpp | 56 ++++-------- .../irohad/torii/query_service_test.cpp | 88 +++++-------------- test/module/irohad/torii/torii_mocks.hpp | 9 +- .../irohad/torii/torii_service_query_test.cpp | 14 +-- 8 files changed, 76 insertions(+), 205 deletions(-) diff --git a/irohad/torii/impl/query_service.cpp b/irohad/torii/impl/query_service.cpp index d1736ac53c..6c7cfa4741 100644 --- a/irohad/torii/impl/query_service.cpp +++ b/irohad/torii/impl/query_service.cpp @@ -25,19 +25,7 @@ namespace torii { QueryService::QueryService( std::shared_ptr query_processor) - : query_processor_(query_processor), log_(logger::log("Query Service")) { - // Subscribe on result from iroha - query_processor_->queryNotifier().subscribe( - [this](const std::shared_ptr - &iroha_response) { - auto proto_response = - static_cast(*iroha_response) - .getTransport(); - - auto hash = iroha_response->queryHash(); - cache_.addItem(hash, proto_response); - }); - } + : query_processor_(query_processor), log_(logger::log("Query Service")) {} void QueryService::Find(iroha::protocol::Query const &request, iroha::protocol::QueryResponse &response) { @@ -61,16 +49,12 @@ namespace torii { const iroha::expected::Value &query) { // Send query to iroha - query_processor_->queryHandle( - std::make_shared(query.value)); - auto result_response = cache_.findItem(hash); - if (result_response) { - response.CopyFrom(result_response.value()); - } else { - response.mutable_error_response()->set_reason( - iroha::protocol::ErrorResponse::NOT_SUPPORTED); - cache_.addItem(hash, response); - } + auto result_response = + static_cast( + *query_processor_->queryHandle(query.value)) + .getTransport(); + response.CopyFrom(result_response); + cache_.addItem(hash, response); }, [&hash, &response](const iroha::expected::Error &error) { @@ -103,10 +87,7 @@ namespace torii { const iroha::expected::Value &query) { rxcpp::composite_subscription sub; - query_processor_ - ->blocksQueryHandle( - std::make_shared( - query.value)) + query_processor_->blocksQueryHandle(query.value) .as_blocking() .subscribe( sub, diff --git a/irohad/torii/processor/impl/query_processor_impl.cpp b/irohad/torii/processor/impl/query_processor_impl.cpp index e00adfd0c1..9ff16695c2 100644 --- a/irohad/torii/processor/impl/query_processor_impl.cpp +++ b/irohad/torii/processor/impl/query_processor_impl.cpp @@ -27,7 +27,7 @@ namespace iroha { * @param hash - original query hash * @return QueryRepsonse */ - std::shared_ptr buildStatefulError( + auto buildStatefulError( const shared_model::interface::types::HashType &hash) { return clone( shared_model::proto::TemplateQueryResponseBuilder<>() @@ -78,32 +78,27 @@ namespace iroha { QueryProcessorImpl::checkSignatories( const shared_model::interface::BlocksQuery &); - void QueryProcessorImpl::queryHandle( - std::shared_ptr qry) { - if (not checkSignatories(*qry)) { - auto response = buildStatefulError(qry->hash()); - std::lock_guard lock(notifier_mutex_); - subject_.get_subscriber().on_next(response); - return; + std::unique_ptr + QueryProcessorImpl::queryHandle(const shared_model::interface::Query &qry) { + if (not checkSignatories(qry)) { + return buildStatefulError(qry.hash()); } const auto &wsv_query = storage_->getWsvQuery(); auto qpf = QueryProcessingFactory(wsv_query, storage_->getBlockQuery()); - auto qpf_response = qpf.validateAndExecute(*qry); + auto qpf_response = qpf.validateAndExecute(qry); auto qry_resp = std::static_pointer_cast( qpf_response); - std::lock_guard lock(notifier_mutex_); - subject_.get_subscriber().on_next( - std::make_shared( - qry_resp->getTransport())); + return std::make_unique( + qry_resp->getTransport()); } rxcpp::observable< std::shared_ptr> QueryProcessorImpl::blocksQueryHandle( - std::shared_ptr qry) { - if (not checkSignatories(*qry)) { + const shared_model::interface::BlocksQuery &qry) { + if (not checkSignatories(qry)) { auto response = buildBlocksQueryError("Wrong signatories"); return rxcpp::observable<>::just( std::shared_ptr( @@ -111,7 +106,7 @@ namespace iroha { } const auto &wsv_query = storage_->getWsvQuery(); auto qpf = QueryProcessingFactory(wsv_query, storage_->getBlockQuery()); - if (not qpf.validate(*qry)) { + if (not qpf.validate(qry)) { auto response = buildBlocksQueryError("Stateful invalid"); return rxcpp::observable<>::just( std::shared_ptr( @@ -120,10 +115,5 @@ namespace iroha { return blocksQuerySubject_.get_observable(); } - rxcpp::observable> - QueryProcessorImpl::queryNotifier() { - return subject_.get_observable(); - } - } // namespace torii } // namespace iroha diff --git a/irohad/torii/processor/query_processor.hpp b/irohad/torii/processor/query_processor.hpp index 7267d717a8..fabcab71d7 100644 --- a/irohad/torii/processor/query_processor.hpp +++ b/irohad/torii/processor/query_processor.hpp @@ -34,11 +34,12 @@ namespace iroha { class QueryProcessor { public: /** - * Register client query - * @param query - client intent + * Perform client query + * @param qry - client intent + * @return resulted response */ - virtual void queryHandle( - std::shared_ptr qry) = 0; + virtual std::unique_ptr + queryHandle(const shared_model::interface::Query &qry) = 0; /** * Register client blocks query * @param query - client intent @@ -46,15 +47,7 @@ namespace iroha { */ virtual rxcpp::observable< std::shared_ptr> - blocksQueryHandle( - std::shared_ptr qry) = 0; - /** - * Subscribe for query responses - * @return observable with query responses - */ - virtual rxcpp::observable< - std::shared_ptr> - queryNotifier() = 0; + blocksQueryHandle(const shared_model::interface::BlocksQuery &qry) = 0; virtual ~QueryProcessor(){}; }; diff --git a/irohad/torii/processor/query_processor_impl.hpp b/irohad/torii/processor/query_processor_impl.hpp index 6079b53779..ba290b7520 100644 --- a/irohad/torii/processor/query_processor_impl.hpp +++ b/irohad/torii/processor/query_processor_impl.hpp @@ -26,7 +26,7 @@ namespace iroha { namespace torii { /** - * QueryProcessor provides start point for queries in the whole system + * QueryProcessorImpl provides implementation of QueryProcessor */ class QueryProcessorImpl : public QueryProcessor { public: @@ -40,39 +40,19 @@ namespace iroha { template bool checkSignatories(const Q &qry); - /** - * Register client query - * @param query - client intent - */ - void queryHandle( - std::shared_ptr qry) override; + std::unique_ptr queryHandle( + const shared_model::interface::Query &qry) override; - /** - * Register client block query - * @param query - client intent - * @return observable with block query responses - */ rxcpp::observable< std::shared_ptr> blocksQueryHandle( - std::shared_ptr qry) override; - - /** - * Subscribe for query responses - * @return observable with query responses - */ - rxcpp::observable> - queryNotifier() override; + const shared_model::interface::BlocksQuery &qry) override; private: - rxcpp::subjects::subject< - std::shared_ptr> - subject_; rxcpp::subjects::subject< std::shared_ptr> blocksQuerySubject_; std::shared_ptr storage_; - std::mutex notifier_mutex_; }; } // namespace torii } // namespace iroha diff --git a/test/module/irohad/torii/processor/query_processor_test.cpp b/test/module/irohad/torii/processor/query_processor_test.cpp index 6f06449611..a38b5d7956 100644 --- a/test/module/irohad/torii/processor/query_processor_test.cpp +++ b/test/module/irohad/torii/processor/query_processor_test.cpp @@ -115,15 +115,11 @@ TEST_F(QueryProcessorTest, QueryProcessorWhereInvokeInvalidQuery) { EXPECT_CALL(*wsv_queries, getSignatories(account_id)) .WillRepeatedly(Return(signatories)); - auto wrapper = make_test_subscriber(qpi.queryNotifier(), 1); - wrapper.subscribe([](auto response) { - ASSERT_NO_THROW(boost::apply_visitor( - framework::SpecifiedVisitor(), - response->get())); - }); - qpi.queryHandle( - std::make_shared(query.getTransport())); - ASSERT_TRUE(wrapper.validate()); + auto response = qpi.queryHandle(query); + ASSERT_TRUE(response); + ASSERT_NO_THROW(boost::apply_visitor( + framework::SpecifiedVisitor(), + response->get())); } /** @@ -162,16 +158,12 @@ TEST_F(QueryProcessorTest, QueryProcessorWithWrongKey) { EXPECT_CALL(*wsv_queries, getSignatories(account_id)) .WillRepeatedly(Return(signatories)); - auto wrapper = make_test_subscriber(qpi.queryNotifier(), 1); - wrapper.subscribe([](auto response) { - ASSERT_TRUE(boost::apply_visitor( - shared_model::interface::QueryErrorResponseChecker< - shared_model::interface::StatefulFailedErrorResponse>(), - response->get())); - }); - qpi.queryHandle( - std::make_shared(query.getTransport())); - ASSERT_TRUE(wrapper.validate()); + auto response = qpi.queryHandle(query); + ASSERT_TRUE(response); + ASSERT_NO_THROW(boost::apply_visitor( + shared_model::interface::QueryErrorResponseChecker< + shared_model::interface::StatefulFailedErrorResponse>(), + response->get())); } /** @@ -214,9 +206,7 @@ TEST_F(QueryProcessorTest, GetBlocksQuery) { .WillRepeatedly(Return(signatories)); auto wrapper = make_test_subscriber( - qpi.blocksQueryHandle(std::make_shared( - blockQuery.getTransport())), - blockNumber); + qpi.blocksQueryHandle(blockQuery), blockNumber); wrapper.subscribe([](auto response) { ASSERT_NO_THROW({ boost::apply_visitor( @@ -249,13 +239,7 @@ TEST_F(QueryProcessorTest, GetBlocksQueryNoPerms) { iroha::torii::QueryProcessorImpl qpi(storage); - auto blockQuery = TestUnsignedBlocksQueryBuilder() - .createdTime(created_time) - .creatorAccountId(account_id) - .queryCounter(counter) - .build() - .signAndAddSignature(keypair) - .finish(); + auto blockQuery = getBlocksQuery(account_id); std::shared_ptr shared_account = clone( shared_model::proto::AccountBuilder().accountId(account_id).build()); @@ -271,10 +255,8 @@ TEST_F(QueryProcessorTest, GetBlocksQueryNoPerms) { EXPECT_CALL(*wsv_queries, getSignatories(account_id)) .WillRepeatedly(Return(signatories)); - auto wrapper = make_test_subscriber( - qpi.blocksQueryHandle(std::make_shared( - blockQuery.getTransport())), - 1); + auto wrapper = + make_test_subscriber(qpi.blocksQueryHandle(blockQuery), 1); wrapper.subscribe([](auto response) { ASSERT_NO_THROW({ boost::apply_visitor(framework::SpecifiedVisitor< @@ -357,9 +339,7 @@ TEST_F(QueryProcessorTest, NoOneSeesStatefulInvalidButCaller) { // check that admin can get block and do not get anything but block responses auto admin_wrapper = make_test_subscriber( - qpi.blocksQueryHandle(std::make_shared( - admin_block_query.getTransport())), - 1); + qpi.blocksQueryHandle(admin_block_query), 1); admin_wrapper.subscribe([&expected_block]( const std::shared_ptr< shared_model::interface::BlockQueryResponse> @@ -377,9 +357,7 @@ TEST_F(QueryProcessorTest, NoOneSeesStatefulInvalidButCaller) { // check that user without can_get_blocks permission will not get anything but // block error response auto user_wrapper = make_test_subscriber( - qpi.blocksQueryHandle(std::make_shared( - user_block_query.getTransport())), - 1); + qpi.blocksQueryHandle(user_block_query), 1); user_wrapper.subscribe( [](const std::shared_ptr &block) { diff --git a/test/module/irohad/torii/query_service_test.cpp b/test/module/irohad/torii/query_service_test.cpp index 863188becc..0a4ba15247 100644 --- a/test/module/irohad/torii/query_service_test.cpp +++ b/test/module/irohad/torii/query_service_test.cpp @@ -51,25 +51,25 @@ class QueryServiceTest : public ::testing::Test { shared_model::crypto::DefaultCryptoAlgorithmType:: generateKeypair()) .finish()); - - auto account = shared_model::proto::AccountBuilder() - .accountId("a") - .domainId("ru") - .quorum(2) - .build(); - - model_response = clone(TestQueryResponseBuilder() - .accountResponse(account, {"user"}) - .queryHash(query->hash()) - .build()); } void init() { query_service = std::make_shared(query_processor); } + std::unique_ptr getResponse() { + static auto account = shared_model::proto::AccountBuilder() + .accountId("a") + .domainId("ru") + .quorum(2) + .build(); + return clone(TestQueryResponseBuilder() + .accountResponse(account, {"user"}) + .queryHash(query->hash()) + .build()); + } + std::shared_ptr query; - std::shared_ptr model_response; std::shared_ptr query_service; std::shared_ptr query_processor; }; @@ -81,60 +81,19 @@ class QueryServiceTest : public ::testing::Test { */ TEST_F(QueryServiceTest, ValidWhenUniqueHash) { // unique query => query handled by query processor - rxcpp::subjects::subject< - std::shared_ptr> - notifier; - EXPECT_CALL(*query_processor, queryNotifier()) - .WillOnce(Return(notifier.get_observable())); - EXPECT_CALL( - *query_processor, - queryHandle( - // match by shared_ptr's content - Truly([this](std::shared_ptr rhs) { - return *rhs == *query; - }))) - .WillOnce(Invoke([this, ¬ifier](auto q) { - notifier.get_subscriber().on_next(model_response); - })); - init(); - - protocol::QueryResponse response; - query_service->Find(query->getTransport(), response); - auto resp = shared_model::proto::QueryResponse(response); - ASSERT_EQ(resp, *model_response); -} - -/** - * @given query and expected response - * @when query is sent to query service and query_processor does not process - * query - * @then NOT_SUPPORTED error response is returned - */ -TEST_F(QueryServiceTest, InvalidWhenUniqueHash) { - // unique query => query handled by query processor - rxcpp::subjects::subject< - std::shared_ptr> - notifier; - EXPECT_CALL(*query_processor, queryNotifier()) - .WillOnce(Return(notifier.get_observable())); - EXPECT_CALL( - *query_processor, - queryHandle( - // match by shared_ptr's content - Truly([this](std::shared_ptr rhs) { - return *rhs == *query; - }))) - .WillOnce(Return()); + EXPECT_CALL(*query_processor, + queryHandle( + // match by shared_ptr's content + Truly([this](const shared_model::interface::Query &rhs) { + return rhs == *query; + }))) + .WillOnce(Invoke([this](auto &) { return this->getResponse(); })); init(); protocol::QueryResponse response; query_service->Find(query->getTransport(), response); - ASSERT_TRUE(response.has_error_response()); auto resp = shared_model::proto::QueryResponse(response); - ASSERT_TRUE(boost::apply_visitor( - shared_model::interface::QueryErrorResponseChecker< - shared_model::interface::NotSupportedErrorResponse>(), - resp.get())); + ASSERT_EQ(resp, *getResponse()); } /** @@ -145,10 +104,9 @@ TEST_F(QueryServiceTest, InvalidWhenUniqueHash) { */ TEST_F(QueryServiceTest, InvalidWhenDuplicateHash) { // two same queries => only first query handled by query processor - EXPECT_CALL(*query_processor, queryNotifier()) - .WillOnce( - Return(rxcpp::observable<>::empty>())); - EXPECT_CALL(*query_processor, queryHandle(_)).WillOnce(Return()); + EXPECT_CALL(*query_processor, queryHandle(_)).WillOnce(Invoke([this](auto &) { + return this->getResponse(); + })); init(); diff --git a/test/module/irohad/torii/torii_mocks.hpp b/test/module/irohad/torii/torii_mocks.hpp index 974d56334f..b30968fbee 100644 --- a/test/module/irohad/torii/torii_mocks.hpp +++ b/test/module/irohad/torii/torii_mocks.hpp @@ -28,16 +28,13 @@ namespace iroha { class MockQueryProcessor : public QueryProcessor { public: MOCK_METHOD1(queryHandle, - void(std::shared_ptr)); + std::unique_ptr( + const shared_model::interface::Query &)); MOCK_METHOD1( blocksQueryHandle, rxcpp::observable< std::shared_ptr>( - std::shared_ptr)); - MOCK_METHOD0( - queryNotifier, - rxcpp::observable< - std::shared_ptr>()); + const shared_model::interface::BlocksQuery &)); }; } // namespace torii } // namespace iroha diff --git a/test/module/irohad/torii/torii_service_query_test.cpp b/test/module/irohad/torii/torii_service_query_test.cpp index a2ece6a01e..2c5d5b3ddb 100644 --- a/test/module/irohad/torii/torii_service_query_test.cpp +++ b/test/module/irohad/torii/torii_service_query_test.cpp @@ -33,10 +33,6 @@ class ToriiQueryServiceTest : public ::testing::Test { // ----------- Command Service -------------- query_processor = std::make_shared(); - EXPECT_CALL(*query_processor, queryNotifier()) - .WillOnce( - Return(rxcpp::observable<>::empty< - std::shared_ptr>())); //----------- Server run ---------------- runner->append(std::make_unique(query_processor)) @@ -91,12 +87,10 @@ TEST_F(ToriiQueryServiceTest, FetchBlocksWhenValidQuery) { .blockResponse(*proto_block) .build(); - EXPECT_CALL( - *query_processor, - blocksQueryHandle( - Truly([&blocks_query]( - const std::shared_ptr - query) { return *query == *blocks_query; }))) + EXPECT_CALL(*query_processor, + blocksQueryHandle(Truly([&blocks_query](auto &query) { + return query == *blocks_query; + }))) .WillOnce(Return(rxcpp::observable<>::just(block_response))); auto client = torii_utils::QuerySyncClient(ip, port); From 8e20ce308273dcb0126269597361b98e29e1cca0 Mon Sep 17 00:00:00 2001 From: Akvinikym Date: Thu, 19 Jul 2018 13:19:38 +0300 Subject: [PATCH 56/97] Removed exception in batch SFV (#1574) Signed-off-by: Akvinikym --- .../impl/stateful_validator_impl.cpp | 33 ++++++++++++++----- .../validation/stateful_validator_test.cpp | 13 ++++---- 2 files changed, 32 insertions(+), 14 deletions(-) diff --git a/irohad/validation/impl/stateful_validator_impl.cpp b/irohad/validation/impl/stateful_validator_impl.cpp index be45190862..51560f1793 100644 --- a/irohad/validation/impl/stateful_validator_impl.cpp +++ b/irohad/validation/impl/stateful_validator_impl.cpp @@ -152,12 +152,14 @@ namespace iroha { * @param txs to be validated * @param temporary_wsv to apply transactions on * @param transactions_errors_log to write errors to + * @param log to write errors to console * @return vector of proto transactions, which passed stateful validation */ static std::vector validateTransactions( const shared_model::interface::types::TransactionsCollectionType &txs, ametsuchi::TemporaryWsv &temporary_wsv, - validation::TransactionsErrors &transactions_errors_log) { + validation::TransactionsErrors &transactions_errors_log, + const logger::Logger &log) { std::vector valid_proto_txs{}; // TODO: kamilsa IR-1010 20.02.2018 rework validation logic, so that // casts to proto are not needed and stateful validator does not know @@ -186,17 +188,32 @@ namespace iroha { return tx.reducedHash() == batch_end_hash; }); if (batch_end_it == txs_end) { - // for peer review: adequate exception variants? - throw std::runtime_error("Batch is formed incorrectly"); + // exceptional case, such batch should not have passed stateless + // validation, so fail the whole proposal + auto batch_error_msg = + (boost::format("batch is formed incorrectly: could not " + "find end of batch; " + "first transaction is %s, supposed last " + "transaction is %s") + % current_tx_it->hash().hex() % batch_end_hash.hex()) + .str(); + transactions_errors_log.emplace_back(std::make_pair( + validation::CommandError{ + "batch stateful validation", batch_error_msg, true}, + current_tx_it->hash())); + log->error(std::move(batch_error_msg)); + return std::vector{}; } // check all batch's transactions for validness auto savepoint = temporary_wsv.createSavepoint( "batch_" + current_tx_it->hash().hex()); - if (std::all_of(current_tx_it, batch_end_it + 1, [&temporary_wsv, &transactions_errors_log](auto &tx) { - return checkTransactions( - temporary_wsv, transactions_errors_log, tx); - })) { + if (std::all_of(current_tx_it, + batch_end_it + 1, + [&temporary_wsv, &transactions_errors_log](auto &tx) { + return checkTransactions( + temporary_wsv, transactions_errors_log, tx); + })) { // batch is successful; add it to the list of valid_txs and // release savepoint std::transform( @@ -229,7 +246,7 @@ namespace iroha { auto transactions_errors_log = validation::TransactionsErrors{}; auto valid_proto_txs = validateTransactions( - proposal.transactions(), temporaryWsv, transactions_errors_log); + proposal.transactions(), temporaryWsv, transactions_errors_log, log_); auto validated_proposal = shared_model::proto::ProposalBuilder() .createdTime(proposal.createdTime()) .height(proposal.height()) diff --git a/test/module/irohad/validation/stateful_validator_test.cpp b/test/module/irohad/validation/stateful_validator_test.cpp index 83177f66aa..5d48328e36 100644 --- a/test/module/irohad/validation/stateful_validator_test.cpp +++ b/test/module/irohad/validation/stateful_validator_test.cpp @@ -109,21 +109,22 @@ class Validator : public testing::Test { shared_model::interface::types::BatchType batch_type) { std::vector reduced_hashes; std::vector txs; + auto current_time = iroha::time::now(); - for (const auto &creator : creators) { + for (size_t i = 0; i < creators.size(); ++i) { auto tx = TestTransactionBuilder() - .creatorAccountId(creator) - .createdTime(iroha::time::now()) + .creatorAccountId(creators[i]) + .createdTime(current_time + i) .quorum(1) .createAsset("doge", "coin", 1) .build(); reduced_hashes.push_back(tx.reducedHash()); } - for (const auto &creator : creators) { + for (size_t i = 0; i < creators.size(); ++i) { txs.push_back(TestTransactionBuilder() - .creatorAccountId(creator) - .createdTime(iroha::time::now()) + .creatorAccountId(creators[i]) + .createdTime(current_time + i) .quorum(1) .createAsset("doge", "coin", 1) .batchMeta(batch_type, reduced_hashes) From 94bda4049441865afa61206c9c35f69ff9743f13 Mon Sep 17 00:00:00 2001 From: kamilsa Date: Thu, 19 Jul 2018 15:16:27 +0300 Subject: [PATCH 57/97] Transaction batch (#1565) * Create transaction batch Signed-off-by: kamilsa --- .../protobuf/transaction_sequence_builder.hpp | 15 +- .../builders/protobuf/unsigned_proto.hpp | 2 +- shared_model/interfaces/CMakeLists.txt | 1 + .../transaction_sequence_common.hpp | 6 +- .../iroha_internal/transaction_batch.cpp | 137 +++++++++++++ .../iroha_internal/transaction_batch.hpp | 93 +++++++++ .../iroha_internal/transaction_sequence.cpp | 107 ++++++---- .../iroha_internal/transaction_sequence.hpp | 26 +-- .../transactions_collection_validator.hpp | 4 + test/framework/batch_helper.hpp | 156 ++++++++++++-- .../shared_model/backend_proto/CMakeLists.txt | 8 + .../backend_proto/proto_batch_test.cpp | 193 ++++++++++++++++++ .../proto_transaction_sequence_test.cpp | 62 +++--- .../protobuf/transport_builder_test.cpp | 109 +++++----- 14 files changed, 743 insertions(+), 176 deletions(-) create mode 100644 shared_model/interfaces/iroha_internal/transaction_batch.cpp create mode 100644 shared_model/interfaces/iroha_internal/transaction_batch.hpp create mode 100644 test/module/shared_model/backend_proto/proto_batch_test.cpp diff --git a/shared_model/builders/protobuf/transaction_sequence_builder.hpp b/shared_model/builders/protobuf/transaction_sequence_builder.hpp index ef8198c8b6..0caf7f68b0 100644 --- a/shared_model/builders/protobuf/transaction_sequence_builder.hpp +++ b/shared_model/builders/protobuf/transaction_sequence_builder.hpp @@ -6,9 +6,9 @@ #ifndef IROHA_TRANSACTION_SEQUENCE_BUILDER_HPP #define IROHA_TRANSACTION_SEQUENCE_BUILDER_HPP +#include "builders/protobuf/transport_builder.hpp" #include "interfaces/common_objects/types.hpp" #include "interfaces/iroha_internal/transaction_sequence.hpp" -#include "builders/protobuf/transport_builder.hpp" namespace shared_model { namespace proto { @@ -32,14 +32,15 @@ namespace shared_model { */ template iroha::expected::Result - build(T &transport) { + build(const T &transport) { const auto &txs = transport.transactions(); std::vector> shm_txs; - std::transform( - txs.begin(), - txs.end(), - std::back_inserter(shm_txs), - [](const auto &tx) { return std::make_shared(tx); }); + std::transform(txs.begin(), + txs.end(), + std::back_inserter(shm_txs), + [](const iroha::protocol::Transaction &tx) { + return std::make_shared(tx); + }); return interface::TransactionSequence::createTransactionSequence( shm_txs, stateless_validator_); } diff --git a/shared_model/builders/protobuf/unsigned_proto.hpp b/shared_model/builders/protobuf/unsigned_proto.hpp index d87d464bc3..48448b3c04 100644 --- a/shared_model/builders/protobuf/unsigned_proto.hpp +++ b/shared_model/builders/protobuf/unsigned_proto.hpp @@ -102,7 +102,7 @@ namespace shared_model { typename std::enable_if< std::is_base_of::value, interface::types::HashType>::type - reduced_hash() { + reducedHash() { return object_.reducedHash(); } diff --git a/shared_model/interfaces/CMakeLists.txt b/shared_model/interfaces/CMakeLists.txt index 9a3c8a8d42..73d4cb6215 100644 --- a/shared_model/interfaces/CMakeLists.txt +++ b/shared_model/interfaces/CMakeLists.txt @@ -56,6 +56,7 @@ if (IROHA_ROOT_PROJECT) query_responses/impl/block_response.cpp iroha_internal/block_variant.cpp iroha_internal/transaction_sequence.cpp + iroha_internal/transaction_batch.cpp ) endif () diff --git a/shared_model/interfaces/common_objects/transaction_sequence_common.hpp b/shared_model/interfaces/common_objects/transaction_sequence_common.hpp index 972aad70be..4b11eebcee 100644 --- a/shared_model/interfaces/common_objects/transaction_sequence_common.hpp +++ b/shared_model/interfaces/common_objects/transaction_sequence_common.hpp @@ -13,6 +13,8 @@ namespace shared_model { namespace interface { + class TransactionBatch; + namespace types { using TransactionsForwardCollectionType = @@ -24,9 +26,7 @@ namespace shared_model { // TODO: IR-1514 kamilsa 09.07.2018 Introduce batch type with batch // invariant and return range of them - using BatchesType = boost::any_range; + using BatchesCollectionType = std::vector; } // namespace types } // namespace interface } // namespace shared_model diff --git a/shared_model/interfaces/iroha_internal/transaction_batch.cpp b/shared_model/interfaces/iroha_internal/transaction_batch.cpp new file mode 100644 index 0000000000..654539731f --- /dev/null +++ b/shared_model/interfaces/iroha_internal/transaction_batch.cpp @@ -0,0 +1,137 @@ +/** + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "interfaces/iroha_internal/transaction_batch.hpp" +#include "validators/field_validator.hpp" +#include "validators/transaction_validator.hpp" +#include "validators/transactions_collection/batch_order_validator.hpp" + +namespace shared_model { + namespace interface { + + /** + * check if all transactions belong to the same batch + * @param txs transactions to be checked + * @return true if all transactions from the same batch and false otherwise + */ + static bool allTxsInSameBatch(const types::SharedTxsCollectionType &txs) { + // Empty batch is still batch, so txs can be empty + if (txs.empty()) { + return true; + } + + // take batch meta of the first transaction and compare it with batch + // metas of remaining transactions + auto batch_meta = txs.front()->batchMeta(); + if (not batch_meta) { + return false; + } + + return std::none_of(++txs.begin(), + txs.end(), + [front_batch_meta = batch_meta.value()]( + const std::shared_ptr tx) { + return tx->batchMeta() + ? **tx->batchMeta() != *front_batch_meta + : false; + }); + }; + + template + iroha::expected::Result + TransactionBatch::createTransactionBatch( + const types::SharedTxsCollectionType &transactions, + const validation::TransactionsCollectionValidator + &validator) { + auto answer = validator.validatePointers(transactions); + if (not allTxsInSameBatch(transactions)) { + answer.addReason(std::make_pair( + "Transaction batch: ", + std::vector{ + "Provided transactions are not from the same batch"})); + } + + if (answer.hasErrors()) { + return iroha::expected::makeError(answer.reason()); + } + return iroha::expected::makeValue(TransactionBatch(transactions)); + } + + template iroha::expected::Result + TransactionBatch::createTransactionBatch( + const types::SharedTxsCollectionType &transactions, + const validation::TransactionsCollectionValidator< + validation::TransactionValidator< + validation::FieldValidator, + validation::CommandValidatorVisitor< + validation::FieldValidator>>, + validation::BatchOrderValidator> &validator); + + template iroha::expected::Result + TransactionBatch::createTransactionBatch( + const types::SharedTxsCollectionType &transactions, + const validation::TransactionsCollectionValidator< + validation::TransactionValidator< + validation::FieldValidator, + validation::CommandValidatorVisitor< + validation::FieldValidator>>, + validation::AnyOrderValidator> &validator); + + template + iroha::expected::Result + TransactionBatch::createTransactionBatch( + std::shared_ptr transaction, + const TransactionValidator &transaction_validator) { + auto answer = transaction_validator.validate(*transaction); + if (answer.hasErrors()) { + return iroha::expected::makeError(answer.reason()); + } + return iroha::expected::makeValue( + TransactionBatch(types::SharedTxsCollectionType{transaction})); + }; + + template iroha::expected::Result + TransactionBatch::createTransactionBatch( + std::shared_ptr transaction, + const validation::TransactionValidator< + validation::FieldValidator, + validation::CommandValidatorVisitor> + &validator); + + const types::SharedTxsCollectionType &TransactionBatch::transactions() + const { + return transactions_; + } + + const types::HashType &TransactionBatch::reducedHash() const { + if (not reduced_hash_) { + reduced_hash_ = TransactionBatch::calculateReducedBatchHash( + transactions_ | boost::adaptors::transformed([](const auto &tx) { + return tx->reducedHash(); + })); + } + return reduced_hash_.value(); + } + + bool TransactionBatch::hasAllSignatures() const { + return std::all_of( + transactions_.begin(), transactions_.end(), [](const auto tx) { + return boost::size(tx->signatures()) >= tx->quorum(); + }); + } + + types::HashType TransactionBatch::calculateReducedBatchHash( + const boost::any_range + &reduced_hashes) { + std::stringstream concatenated_hash; + for (const auto &hash : reduced_hashes) { + concatenated_hash << hash.hex(); + } + return types::HashType::fromHexString(concatenated_hash.str()); + } + + } // namespace interface +} // namespace shared_model diff --git a/shared_model/interfaces/iroha_internal/transaction_batch.hpp b/shared_model/interfaces/iroha_internal/transaction_batch.hpp new file mode 100644 index 0000000000..9f113db703 --- /dev/null +++ b/shared_model/interfaces/iroha_internal/transaction_batch.hpp @@ -0,0 +1,93 @@ +/** + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef IROHA_TRANSACTION_BATCH_HPP +#define IROHA_TRANSACTION_BATCH_HPP + +#include "common/result.hpp" +#include "interfaces/common_objects/transaction_sequence_common.hpp" +#include "validators/transactions_collection/transactions_collection_validator.hpp" + +namespace shared_model { + namespace interface { + + class TransactionBatch { + public: + + /** + * Create transaction batch out of collection of transactions + * @tparam TransactionValidator validates every single transaction + * @tparam OrderValidator validates order of transactions + * @param transactions collection of transactions, should be from the same + * batch + * @param validator transactions collection validator with provided + * transaction validator and order validator + * @return valid batch of transactions + */ + template + static iroha::expected::Result + createTransactionBatch(const types::SharedTxsCollectionType &transactions, + const validation::TransactionsCollectionValidator< + TransactionValidator, + OrderValidator> &validator); + + /** + * Creates transaction batch from single transaction + * @tparam TransactionValidator validates every single transaction + * @param transaction is transaction being validated and used to create + * batch + * @param transaction_validator transaction validation logic + * @return batch with single transaction + * @note transactions in such batches may not have batch meta information + */ + template + static iroha::expected::Result + createTransactionBatch(std::shared_ptr transaction, + const TransactionValidator &transaction_validator = + TransactionValidator()); + + /** + * Get transactions list + * @return list of transactions from the batch + */ + const types::SharedTxsCollectionType &transactions() const; + + /** + * Get the concatenation of reduced hashes as a single hash + * @param reduced_hashes collection of reduced hashes + * @return concatenated reduced hashes + */ + const types::HashType &reducedHash() const; + + /** + * Checks if every transaction has quorum signatures + * @return true if every transaction has quorum signatures, false + * otherwise + */ + bool hasAllSignatures() const; + + /** + * Get the concatenation of reduced hashes as a single hash + * That kind of hash does not respect batch type + * @param reduced_hashes + * @return concatenated reduced hashes + */ + static types::HashType calculateReducedBatchHash( + const boost::any_range + &reduced_hashes); + + private: + explicit TransactionBatch( + const types::SharedTxsCollectionType &transactions) + : transactions_(transactions) {} + + types::SharedTxsCollectionType transactions_; + + mutable boost::optional reduced_hash_; + }; + } // namespace interface +} // namespace shared_model + +#endif // IROHA_TRANSACTION_BATCH_HPP diff --git a/shared_model/interfaces/iroha_internal/transaction_sequence.cpp b/shared_model/interfaces/iroha_internal/transaction_sequence.cpp index 0790ff02f5..c988e244cb 100644 --- a/shared_model/interfaces/iroha_internal/transaction_sequence.cpp +++ b/shared_model/interfaces/iroha_internal/transaction_sequence.cpp @@ -4,9 +4,10 @@ */ #include "interfaces/iroha_internal/transaction_sequence.hpp" + +#include "interfaces/iroha_internal/transaction_batch.hpp" #include "validators/field_validator.hpp" #include "validators/transaction_validator.hpp" -#include "validators/transactions_collection/any_order_validator.hpp" #include "validators/transactions_collection/batch_order_validator.hpp" namespace shared_model { @@ -19,11 +20,49 @@ namespace shared_model { const validation::TransactionsCollectionValidator &validator) { - auto answer = validator.validatePointers(transactions); - if (answer.hasErrors()) { - return iroha::expected::makeError(answer.reason()); + std::unordered_map>, + interface::types::HashType::Hasher> + extracted_batches; + std::vector batches; + + auto transaction_validator = validator.getTransactionValidator(); + + auto insert_batch = + [&batches](const iroha::expected::Value &value) { + batches.push_back(value.value); + }; + + validation::Answer result; + for (const auto &tx : transactions) { + if (auto meta = tx->batchMeta()) { + auto hashes = meta.get()->transactionHashes(); + auto batch_hash = TransactionBatch::calculateReducedBatchHash(hashes); + extracted_batches[batch_hash].push_back(tx); + } else { + TransactionBatch::createTransactionBatch(tx, transaction_validator) + .match(insert_batch, [&tx, &result](const auto &err) { + result.addReason(std::make_pair( + std::string("Error in transaction with reduced hash: ") + + tx->reducedHash().hex(), + std::vector{err.error})); + }); + } + } + + for (const auto &it : extracted_batches) { + TransactionBatch::createTransactionBatch(it.second, validator) + .match(insert_batch, [&it, &result](const auto &err) { + result.addReason(std::make_pair( + it.first.toString(), std::vector{err.error})); + }); } - return iroha::expected::makeValue(TransactionSequence(transactions)); + + if (result.hasErrors()) { + return iroha::expected::makeError(result.reason()); + } + + return iroha::expected::makeValue(TransactionSequence(batches)); } template iroha::expected::Result @@ -46,47 +85,33 @@ namespace shared_model { validation::FieldValidator>>, validation::BatchOrderValidator> &validator); - types::SharedTxsCollectionType TransactionSequence::transactions() const { - return transactions_; - } - - TransactionSequence::TransactionSequence( - const types::SharedTxsCollectionType &transactions) - : transactions_(transactions) {} - - types::BatchesType TransactionSequence::batches() const { - if (batches_) { - return batches_.value(); - } - - std::unordered_map>> - extracted_batches; - std::vector batches; - for (const auto &tx : transactions_) { - if (auto meta = tx->batchMeta()) { - auto hashes = meta.get()->transactionHashes(); - auto batch_hash = this->calculateBatchHash(hashes); - extracted_batches[batch_hash].push_back(tx); - } else { - batches.emplace_back(std::vector>{tx}); + const types::SharedTxsCollectionType &TransactionSequence::transactions() + const { + if (not transactions_) { + types::SharedTxsCollectionType result; + auto transactions_amount = 0u; + for (const auto &batch : batches_) { + transactions_amount += batch.transactions().size(); } + result.reserve(transactions_amount); + for (const auto &batch : batches_) { + auto &transactions = batch.transactions(); + std::copy(transactions.begin(), + transactions.end(), + std::back_inserter(result)); + } + transactions_.emplace(std::move(result)); } - for (auto it : extracted_batches) { - batches.emplace_back(it.second); - } - - batches_.emplace(batches); - return batches; + return transactions_.value(); } - std::string TransactionSequence::calculateBatchHash( - std::vector reduced_hashes) const { - std::stringstream concatenated_hashes_stream; - for (const auto &hash : reduced_hashes) { - concatenated_hashes_stream << hash.hex(); - } - return concatenated_hashes_stream.str(); + const types::BatchesCollectionType &TransactionSequence::batches() const { + return batches_; } + TransactionSequence::TransactionSequence( + const types::BatchesCollectionType &batches) + : batches_(batches) {} + } // namespace interface } // namespace shared_model diff --git a/shared_model/interfaces/iroha_internal/transaction_sequence.hpp b/shared_model/interfaces/iroha_internal/transaction_sequence.hpp index c28928724b..1ed6cc4af8 100644 --- a/shared_model/interfaces/iroha_internal/transaction_sequence.hpp +++ b/shared_model/interfaces/iroha_internal/transaction_sequence.hpp @@ -8,6 +8,7 @@ #include "common/result.hpp" #include "interfaces/common_objects/transaction_sequence_common.hpp" +#include "interfaces/iroha_internal/transaction_batch.hpp" #include "validators/transactions_collection/transactions_collection_validator.hpp" namespace shared_model { @@ -21,7 +22,6 @@ namespace shared_model { */ class TransactionSequence { public: - TransactionSequence() = delete; /** * Creator of transaction sequence @@ -39,10 +39,10 @@ namespace shared_model { OrderValidator> &validator); /** - * Get transactions collection - * @return transactions collection + * Retrieves transactions from all batches as single collection + * @return all batches transactions */ - types::SharedTxsCollectionType transactions() const; + const types::SharedTxsCollectionType &transactions() const; /** * Get batches in transaction sequence @@ -50,23 +50,13 @@ namespace shared_model { * single transaction * @return collection of batches from transaction sequence */ - types::BatchesType batches() const; + const types::BatchesCollectionType &batches() const; private: - explicit TransactionSequence( - const types::SharedTxsCollectionType &transactions); + explicit TransactionSequence(const types::BatchesCollectionType &batches); - /** - * Get the concatenation of reduced hashes - * @param reduced_hashes collection of reduced hashes - * @return concatenated redueced hashes - */ - std::string calculateBatchHash( - std::vector reduced_hashes) const; - - types::SharedTxsCollectionType transactions_; - - mutable boost::optional batches_; + types::BatchesCollectionType batches_; + mutable boost::optional transactions_; }; } // namespace interface diff --git a/shared_model/validators/transactions_collection/transactions_collection_validator.hpp b/shared_model/validators/transactions_collection/transactions_collection_validator.hpp index 96d76ffe18..d8e2b5f0b9 100644 --- a/shared_model/validators/transactions_collection/transactions_collection_validator.hpp +++ b/shared_model/validators/transactions_collection/transactions_collection_validator.hpp @@ -49,6 +49,10 @@ namespace shared_model { return validatePointers(res); } + const TransactionValidator &getTransactionValidator() const { + return transaction_validator_; + } + virtual Answer validatePointers( const interface::types::SharedTxsCollectionType &transactions) const = 0; diff --git a/test/framework/batch_helper.hpp b/test/framework/batch_helper.hpp index 8d14a6e8e3..58cd0706b2 100644 --- a/test/framework/batch_helper.hpp +++ b/test/framework/batch_helper.hpp @@ -6,6 +6,7 @@ #ifndef IROHA_BATCH_HELPER_HPP #define IROHA_BATCH_HELPER_HPP +#include #include "module/shared_model/builders/protobuf/test_transaction_builder.hpp" namespace framework { @@ -13,42 +14,157 @@ namespace framework { /** * Creates transaction builder with set creator + * @tparam TransactionBuilderType type of tranasction builder * @return prepared transaction builder */ - auto prepareTransactionBuilder(const std::string &creator) { - return TestTransactionBuilder().creatorAccountId(creator); + template + auto prepareTransactionBuilder( + const std::string &creator, + const size_t &created_time = iroha::time::now(), + const shared_model::interface::types::QuorumType &quorum = 1) { + return TransactionBuilderType() + .setAccountQuorum(creator, 1) + .creatorAccountId(creator) + .createdTime(created_time) + .quorum(quorum); } /** - * Creates atomic batch from provided creator accounts - * @param creators vector of creator account ids - * @return atomic batch of the same size as the size of creator account ids + * Creates unsigned transaction builder with set creator + * @return prepared transaction builder + */ + auto prepareUnsignedTransactionBuilder( + const std::string &creator, + const size_t &created_time = iroha::time::now(), + const shared_model::interface::types::QuorumType &quorum = 1) { + return prepareTransactionBuilder( + creator, created_time, quorum); + } + + /** + * Create unsigned batch with given fields of transactions: batch type and + * creator account. + * @param btype_creator_pairs vector of pairs. First element in every pair + * is batch type and second is creator account + * @param now created time for every transaction + * @return batch with the same size as size of range of pairs */ - auto createUnsignedBatch(shared_model::interface::types::BatchType, - std::vector creators) { + auto createUnsignedBatchTransactions( + std::vector> btype_creator_pairs, + size_t now = iroha::time::now()) { std::vector reduced_hashes; + for (const auto &btype_creator : btype_creator_pairs) { + auto tx = prepareTransactionBuilder(btype_creator.second, now).build(); + reduced_hashes.push_back(tx.reducedHash()); + } + + shared_model::interface::types::SharedTxsCollectionType txs; - for (const auto &creator : creators) { - auto tx = prepareTransactionBuilder(creator).build(); + std::transform( + btype_creator_pairs.begin(), + btype_creator_pairs.end(), + std::back_inserter(txs), + [&now, &reduced_hashes](const auto &btype_creator) + -> shared_model::interface::types::SharedTxsCollectionType:: + value_type { + return clone( + prepareTransactionBuilder(btype_creator.second, now) + .batchMeta(btype_creator.first, reduced_hashes) + .build()); + }); + return txs; + } + + /** + * Creates batch transactions, where every transaction has single signature + * @param btype_creator_pairs vector of pairs of batch type and creator + * account id + * @param now created time for every transaction + * @param quorum quorum for every transaction + * @return batch with the same size as size of range of pairs + */ + auto createBatchOneSignTransactions( + std::vector> btype_creator_pairs, + size_t now = iroha::time::now(), + const shared_model::interface::types::QuorumType &quorum = 1) { + std::vector reduced_hashes; + for (const auto &btype_creator : btype_creator_pairs) { + auto tx = + prepareUnsignedTransactionBuilder(btype_creator.second, now, quorum) + .build(); reduced_hashes.push_back(tx.reducedHash()); } shared_model::interface::types::SharedTxsCollectionType txs; - std::for_each( - creators.begin(), - creators.end(), - [&txs, &reduced_hashes](const auto &creator) { - txs.emplace_back( - clone(prepareTransactionBuilder(creator) - .batchMeta( - shared_model::interface::types::BatchType::ATOMIC, - reduced_hashes) - .build())); - }); + std::transform( + btype_creator_pairs.begin(), + btype_creator_pairs.end(), + std::back_inserter(txs), + [&now, &reduced_hashes, &quorum](const auto &btype_creator) + -> shared_model::interface::types::SharedTxsCollectionType:: + value_type { + return clone( + prepareUnsignedTransactionBuilder( + btype_creator.second, now, quorum) + .batchMeta(btype_creator.first, reduced_hashes) + .build() + .signAndAddSignature( + shared_model::crypto:: + DefaultCryptoAlgorithmType:: + generateKeypair()) + .finish()); + }); return txs; } + /** + * Creates atomic batch from provided creator accounts + * @param creators vector of creator account ids + * @return unsigned batch of the same size as the size of creator account + * ids + */ + auto createUnsignedBatchTransactions( + shared_model::interface::types::BatchType batch_type, + const std::vector &creators, + size_t now = iroha::time::now()) { + std::vector> fields; + std::transform(creators.begin(), + creators.end(), + std::back_inserter(fields), + [&batch_type](const auto creator) { + return std::make_pair(batch_type, creator); + }); + return createUnsignedBatchTransactions(fields, now); + } + + /** + * Creates transaction collection for the batch of given type and size + * @param batch_type type of the creted transactions + * @param batch_size size of the created collection of transactions + * @param now created time for every transactions + * @return unsigned batch + */ + auto createUnsignedBatchTransactions( + shared_model::interface::types::BatchType batch_type, + uint32_t batch_size, + size_t now = iroha::time::now()) { + auto range = boost::irange(0, (int)batch_size); + std::vector creators; + + std::transform(range.begin(), + range.end(), + std::back_inserter(creators), + [](const auto &id) { + return std::string("account") + std::to_string(id) + + "@domain"; + }); + + return createUnsignedBatchTransactions(batch_type, creators, now); + } + } // namespace batch } // namespace framework diff --git a/test/module/shared_model/backend_proto/CMakeLists.txt b/test/module/shared_model/backend_proto/CMakeLists.txt index 9bd28b48f5..d06d9e80da 100644 --- a/test/module/shared_model/backend_proto/CMakeLists.txt +++ b/test/module/shared_model/backend_proto/CMakeLists.txt @@ -72,6 +72,14 @@ target_link_libraries(permissions_test shared_model_proto_backend ) +addtest(proto_batch_test + proto_batch_test.cpp + ) +target_link_libraries(proto_batch_test + shared_model_proto_backend + shared_model_stateless_validation + ) + addtest(proto_transaction_sequence_test proto_transaction_sequence_test.cpp ) diff --git a/test/module/shared_model/backend_proto/proto_batch_test.cpp b/test/module/shared_model/backend_proto/proto_batch_test.cpp new file mode 100644 index 0000000000..2dbb7a860d --- /dev/null +++ b/test/module/shared_model/backend_proto/proto_batch_test.cpp @@ -0,0 +1,193 @@ +/** + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "framework/batch_helper.hpp" +#include "framework/result_fixture.hpp" +#include "interfaces/iroha_internal/transaction_batch.hpp" +#include "validators/field_validator.hpp" +#include "validators/transaction_validator.hpp" +#include "validators/transactions_collection/batch_order_validator.hpp" +#include "validators/transactions_collection/unsigned_transactions_collection_validator.hpp" + +using namespace shared_model; +using ::testing::_; +using ::testing::Return; +using ::testing::Test; +using ::testing::Truly; + +using TxValidator = validation::TransactionValidator< + validation::FieldValidator, + validation::CommandValidatorVisitor>; + +using TxsValidator = validation::UnsignedTransactionsCollectionValidator< + TxValidator, + validation::BatchOrderValidator>; + +/** + * Creates valid unsigned transaction + * @param created_time assigned to transactions + * @return std::shared_ptr containing valid unsigned + * transaction + */ +auto createValidUnsignedTransaction(size_t created_time = iroha::time::now()) { + return std::shared_ptr(clone( + framework::batch::prepareTransactionBuilder("valid@account", created_time) + .build())); +} + +/** + * Creates invalid unsigned transaction + * @param created_time assigned to transactions + * @return std::shared_ptr containing invalid unsigned + * transaction + */ +auto createInvalidUnsignedTransaction( + size_t created_time = iroha::time::now()) { + return std::shared_ptr( + clone(framework::batch::prepareTransactionBuilder("invalid#@account", + created_time) + .build())); +} + +/** + * @given valid transactions sequence from the single batch + * @when createTransactionBatch is invoked on that sequence + * @then transaction batch is created + */ +TEST(TransactionBatchTest, CreateTransactionBatchWhenValid) { + auto txs = framework::batch::createUnsignedBatchTransactions( + interface::types::BatchType::ATOMIC, + std::vector{"a@domain", "b@domain"}); + auto transaction_batch = + interface::TransactionBatch::createTransactionBatch(txs, TxsValidator()); + ASSERT_TRUE(framework::expected::val(transaction_batch)) + << framework::expected::err(transaction_batch).value().error; +} + +/** + * @given transactions sequence from the single batch containing valid + * transactions but with different batch types + * @when createTransactionBatch is invoked on that sequence + * @then transaction batch is not created + */ +TEST(TransactionBatchTest, CreateTransactionBatchWhenDifferentBatchType) { + auto tx1_fields = std::make_pair(interface::types::BatchType::ORDERED, + std::string("a@domain")); + auto tx2_fields = std::make_pair(interface::types::BatchType::ATOMIC, + std::string("b@domain")); + + auto txs = framework::batch::createUnsignedBatchTransactions( + std::vector{tx1_fields, tx2_fields}); + + auto transaction_batch = + interface::TransactionBatch::createTransactionBatch(txs, TxsValidator()); + ASSERT_TRUE(framework::expected::err(transaction_batch)); +} + +/** + * @given transactions sequence from the single batch containing one valid and + * one invalid transactions + * @when createTransactionBatch is invoked on that sequence + * @then transaction batch is not created + */ +TEST(TransactionBatchTest, CreateBatchWithValidAndInvalidTx) { + auto txs = framework::batch::createUnsignedBatchTransactions( + interface::types::BatchType::ATOMIC, + std::vector{"valid@name", "invalid#@name"}); + + auto transaction_batch = + interface::TransactionBatch::createTransactionBatch(txs, TxsValidator()); + ASSERT_TRUE(framework::expected::err(transaction_batch)); +} + +/** + * @given single valid transaction + * @when createTransactionBatch is invoked on that transaction + * @then transaction batch is created + */ +TEST(TransactionBatchTest, CreateSingleTxBatchWhenValid) { + TxValidator transaction_validator; + + auto tx1 = createValidUnsignedTransaction(); + + auto transaction_batch = interface::TransactionBatch::createTransactionBatch( + tx1, transaction_validator); + + ASSERT_TRUE(framework::expected::val(transaction_batch)) + << framework::expected::err(transaction_batch).value().error; +} + +/** + * @given single invalid transaction + * @when createTransactionBatch is invoked on that transaction + * @then transaction batch is not created + */ +TEST(TransactionBatchTest, CreateSingleTxBatchWhenInvalid) { + TxValidator transaction_validator; + + auto tx1 = createInvalidUnsignedTransaction(); + + auto transaction_batch = interface::TransactionBatch::createTransactionBatch( + tx1, transaction_validator); + + ASSERT_TRUE(framework::expected::err(transaction_batch)); +} + +/** + * Creates batch from transactions with provided quorum size and one signature + * @param quorum quorum size + * @return batch with transactions with one signature and quorum size + */ +auto createBatchWithTransactionsWithQuorum( + const interface::types::QuorumType &quorum) { + auto keypair = crypto::DefaultCryptoAlgorithmType::generateKeypair(); + + auto now = iroha::time::now(); + + auto batch_type = shared_model::interface::types::BatchType::ATOMIC; + std::string userone = "a@domain"; + std::string usertwo = "b@domain"; + + auto transactions = framework::batch::createBatchOneSignTransactions( + std::vector>{ + std::make_pair(batch_type, userone), + std::make_pair(batch_type, usertwo)}, + now, + quorum); + + return interface::TransactionBatch::createTransactionBatch(transactions, + TxsValidator()); +} + +/** + * @given transactions with transaction with quorum size 1 @and signatures size + * equals 1, @and all transactions are from the same batch + * @when transaction batch is created from that transactions + * @then created batch has all signatures + */ +TEST(TransactionBatchTest, BatchWithAllSignatures) { + auto quorum = 1; + auto transaction_batch = createBatchWithTransactionsWithQuorum(quorum); + auto transaction_batch_val = framework::expected::val(transaction_batch); + ASSERT_TRUE(transaction_batch_val) + << framework::expected::err(transaction_batch).value().error; + ASSERT_TRUE(transaction_batch_val->value.hasAllSignatures()); +} + +/** + * @given transactions with transaction with quorum size 2 @and signatures size + * equals 1, @and all transactions are from the same batch + * @when transaction batch is created from that transactions + * @then created batch does not have all signatures + */ +TEST(TransactionBatchTest, BatchWithMissingSignatures) { + auto quorum = 2; + auto transaction_batch = createBatchWithTransactionsWithQuorum(quorum); + auto transaction_batch_val = framework::expected::val(transaction_batch); + ASSERT_TRUE(transaction_batch_val) + << framework::expected::err(transaction_batch).value().error; + ASSERT_FALSE(transaction_batch_val->value.hasAllSignatures()); +} diff --git a/test/module/shared_model/backend_proto/proto_transaction_sequence_test.cpp b/test/module/shared_model/backend_proto/proto_transaction_sequence_test.cpp index 9d942cc687..d5e029611d 100644 --- a/test/module/shared_model/backend_proto/proto_transaction_sequence_test.cpp +++ b/test/module/shared_model/backend_proto/proto_transaction_sequence_test.cpp @@ -16,7 +16,7 @@ using ::testing::Return; using ::testing::Test; class MockTransactionCollectionValidator - : public validation::SignedTransactionsCollectionValidator< + : public validation::UnsignedTransactionsCollectionValidator< validation::TransactionValidator>> { @@ -26,6 +26,13 @@ class MockTransactionCollectionValidator validation::Answer(const interface::types::SharedTxsCollectionType &)); }; +shared_model::validation::Answer createAnswerWithErrors() { + shared_model::validation::Answer answer; + answer.addReason( + std::make_pair("transaction", std::vector{"some reason"})); + return answer; +} + /** * @given Transaction collection of several transactions * @when create transaction sequence @@ -39,7 +46,10 @@ TEST(TransactionSequenceTest, CreateTransactionSequenceWhenValid) { .WillOnce(Return(validation::Answer())); std::shared_ptr tx(clone( - framework::batch::prepareTransactionBuilder("account@domain").build())); + framework::batch::prepareTransactionBuilder("account@domain") + .batchMeta(shared_model::interface::types::BatchType::ATOMIC, + std::vector{}) + .build())); auto tx_sequence = interface::TransactionSequence::createTransactionSequence( std::vector{tx, tx, tx}, transactions_validator); @@ -56,14 +66,14 @@ TEST(TransactionSequenceTest, CreateTransactionSequenceWhenValid) { TEST(TransactionSequenceTest, CreateTransactionSequenceWhenInvalid) { MockTransactionCollectionValidator res; - validation::Answer answer; - answer.addReason( - std::make_pair("transaction", std::vector{"some reason"})); - - EXPECT_CALL(res, validatePointers(_)).WillOnce(Return(answer)); + EXPECT_CALL(res, validatePointers(_)) + .WillOnce(Return(createAnswerWithErrors())); std::shared_ptr tx(clone( - framework::batch::prepareTransactionBuilder("account@domain").build())); + framework::batch::prepareTransactionBuilder("account@domain") + .batchMeta(shared_model::interface::types::BatchType::ATOMIC, + std::vector{}) + .build())); auto tx_sequence = interface::TransactionSequence::createTransactionSequence( std::vector{tx, tx, tx}, res); @@ -79,25 +89,23 @@ TEST(TransactionSequenceTest, CreateTransactionSequenceWhenInvalid) { * @then expected number of batches is created */ TEST(TransactionSequenceTest, CreateBatches) { - MockTransactionCollectionValidator txs_validator; - - EXPECT_CALL(txs_validator, validatePointers(_)) - .WillOnce(Return(validation::Answer())); - size_t batches_number = 3; size_t txs_in_batch = 2; size_t single_transactions = 1; + MockTransactionCollectionValidator txs_validator; + + EXPECT_CALL(txs_validator, validatePointers(_)) + .Times(batches_number) + .WillRepeatedly(Return(validation::Answer())); + interface::types::SharedTxsCollectionType tx_collection; + auto now = iroha::time::now(); for (size_t i = 0; i < batches_number; i++) { - std::vector creators; - for (size_t j = 0; j < txs_in_batch; j++) { - creators.push_back("batch" + std::to_string(i) + "account" - + std::to_string(j) + "@domain"); - } - - auto batch = framework::batch::createUnsignedBatch( - shared_model::interface::types::BatchType::ATOMIC, creators); + auto batch = framework::batch::createUnsignedBatchTransactions( + shared_model::interface::types::BatchType::ATOMIC, + txs_in_batch, + now + i); tx_collection.insert(tx_collection.begin(), batch.begin(), batch.end()); } @@ -113,10 +121,16 @@ TEST(TransactionSequenceTest, CreateBatches) { txs_validator); auto tx_sequence = framework::expected::val(tx_sequence_opt); - ASSERT_TRUE(tx_sequence); + ASSERT_TRUE(tx_sequence) + << framework::expected::err(tx_sequence_opt).value().error; - ASSERT_EQ(boost::size(tx_sequence->value.transactions()), - batches_number * txs_in_batch + single_transactions); ASSERT_EQ(boost::size(tx_sequence->value.batches()), batches_number + single_transactions); + + size_t total_transactions = boost::accumulate( + tx_sequence->value.batches(), 0ul, [](auto sum, const auto &batch) { + return sum + boost::size(batch.transactions()); + }); + ASSERT_EQ(total_transactions, + batches_number * txs_in_batch + single_transactions); } diff --git a/test/module/shared_model/builders/protobuf/transport_builder_test.cpp b/test/module/shared_model/builders/protobuf/transport_builder_test.cpp index 2d8f50b385..4750d2cf01 100644 --- a/test/module/shared_model/builders/protobuf/transport_builder_test.cpp +++ b/test/module/shared_model/builders/protobuf/transport_builder_test.cpp @@ -5,6 +5,7 @@ #include +#include #include "builders/protobuf/block.hpp" #include "builders/protobuf/block_variant_transport_builder.hpp" #include "builders/protobuf/empty_block.hpp" @@ -15,6 +16,7 @@ #include "builders/protobuf/transport_builder.hpp" #include "common/types.hpp" #include "endpoint.pb.h" +#include "framework/batch_helper.hpp" #include "framework/result_fixture.hpp" #include "interfaces/common_objects/types.hpp" #include "interfaces/iroha_internal/transaction_sequence.hpp" @@ -32,7 +34,7 @@ using iroha::operator|; using TransactionSequenceBuilder = TransportBuilder< interface::TransactionSequence, - validation::SignedTransactionsCollectionValidator< + validation::UnsignedTransactionsCollectionValidator< validation::TransactionValidator< validation::FieldValidator, validation::CommandValidatorVisitor>, @@ -168,33 +170,6 @@ class TransportBuilderTest : public ::testing::Test { .transactions(std::vector()) .build(); } - - //--------------------------------Batch--------------------------------------- - auto getValidBatch( - int seed, - int size, - interface::types::BatchType type = interface::types::BatchType::ATOMIC) { - std::vector transactions; - std::vector builders; - std::vector batch_hashes; - for (int i = 0; i < size; i++) { - auto reduced_tr = TestUnsignedTransactionBuilder() - .createdTime(created_time + seed + i) - .quorum(quorum) - .setAccountQuorum(account_id, quorum) - .creatorAccountId(account_id); - builders.push_back(reduced_tr); - batch_hashes.push_back(reduced_tr.build().reduced_hash()); - } - for (auto &builder : builders) { - auto tr = builder.batchMeta(type, batch_hashes) - .build() - .signAndAddSignature(keypair) - .finish(); - transactions.push_back(tr); - } - return transactions; - } /** * Receives model object, gets transport from it, converts transport into * model object and checks if original and obtained model objects are the same @@ -214,8 +189,7 @@ class TransportBuilderTest : public ::testing::Test { auto proto_model = orig_model.getTransport(); auto built_model = - TransportBuilder().build( - proto_model); + TransportBuilder().build(proto_model); built_model.match(successCase, failCase); } @@ -477,19 +451,19 @@ TEST_F(TransportBuilderTest, BlockVariantWithInvalidBlock) { */ TEST_F(TransportBuilderTest, TransactionSequenceEmpty) { iroha::protocol::TxList tx_list; - auto val = framework::expected::val( - TransportBuilder>, - validation::BatchOrderValidator>>() - .build(tx_list)); + auto val = + framework::expected::val(TransactionSequenceBuilder().build(tx_list)); ASSERT_TRUE(val); - val | [](auto &seq) { ASSERT_EQ(boost::size(seq.value.transactions()), 0); }; + val | [](auto &seq) { EXPECT_EQ(boost::size(seq.value.transactions()), 0); }; } +struct getProtocolTx { + iroha::protocol::Transaction operator()( + const std::shared_ptr tx) const { + return std::static_pointer_cast(tx)->getTransport(); + } +}; + /** * @given sequence of transaction with a right order * @when TransportBuilder tries to build TransactionSequence object @@ -497,17 +471,23 @@ TEST_F(TransportBuilderTest, TransactionSequenceEmpty) { */ TEST_F(TransportBuilderTest, TransactionSequenceCorrect) { iroha::protocol::TxList tx_list; - auto batch1 = getValidBatch(0, 10); - auto batch2 = getValidBatch(20, 5); - auto batch3 = getValidBatch(30, 5); + auto now = iroha::time::now(); + auto batch1 = framework::batch::createUnsignedBatchTransactions( + interface::types::BatchType::ATOMIC, 10, now); + auto batch2 = framework::batch::createUnsignedBatchTransactions( + interface::types::BatchType::ATOMIC, 5, now + 1); + auto batch3 = framework::batch::createUnsignedBatchTransactions( + interface::types::BatchType::ATOMIC, 5, now + 2); std::for_each(std::begin(batch1), std::end(batch1), [&tx_list](auto &tx) { - new (tx_list.add_transactions()) - iroha::protocol::Transaction(tx.getTransport()); + new (tx_list.add_transactions()) iroha::protocol::Transaction( + std::static_pointer_cast(tx)->getTransport()); }); + std::for_each(std::begin(batch2), std::end(batch2), [&tx_list](auto &tx) { - new (tx_list.add_transactions()) - iroha::protocol::Transaction(tx.getTransport()); + new (tx_list.add_transactions()) iroha::protocol::Transaction( + std::static_pointer_cast(tx)->getTransport()); }); + new (tx_list.add_transactions()) iroha::protocol::Transaction(createTransaction().getTransport()); new (tx_list.add_transactions()) @@ -515,34 +495,38 @@ TEST_F(TransportBuilderTest, TransactionSequenceCorrect) { new (tx_list.add_transactions()) iroha::protocol::Transaction(createTransaction().getTransport()); std::for_each(std::begin(batch3), std::end(batch3), [&tx_list](auto &tx) { - new (tx_list.add_transactions()) - iroha::protocol::Transaction(tx.getTransport()); + new (tx_list.add_transactions()) iroha::protocol::Transaction( + std::static_pointer_cast(tx)->getTransport()); }); new (tx_list.add_transactions()) iroha::protocol::Transaction(createTransaction().getTransport()); auto val = framework::expected::val(TransactionSequenceBuilder().build(tx_list)); - ASSERT_TRUE(val); - val | [](auto &seq) { ASSERT_EQ(boost::size(seq.value.transactions()), 24); }; + + val | [](auto &seq) { EXPECT_EQ(boost::size(seq.value.transactions()), 24); }; } /** * @given batch of transaction with transaction in the middle * @when TransportBuilder tries to build TransactionSequence object * @then built an error + * @note disabled because current algorithm of creating batches can process list + * of transaction where in the middle of the batch can appear single independent + * transaction */ -TEST_F(TransportBuilderTest, TransactionInteraptedBatch) { +TEST_F(TransportBuilderTest, DISABLED_TransactionInteraptedBatch) { iroha::protocol::TxList tx_list; - auto batch = getValidBatch(0, 10); + auto batch = framework::batch::createUnsignedBatchTransactions( + interface::types::BatchType::ATOMIC, 10); std::for_each(std::begin(batch), std::begin(batch) + 3, [&tx_list](auto &tx) { - new (tx_list.add_transactions()) - iroha::protocol::Transaction(tx.getTransport()); + new (tx_list.add_transactions()) iroha::protocol::Transaction( + std::static_pointer_cast(tx)->getTransport()); }); new (tx_list.add_transactions()) iroha::protocol::Transaction(createTransaction().getTransport()); std::for_each(std::begin(batch) + 3, std::end(batch), [&tx_list](auto &tx) { - new (tx_list.add_transactions()) - iroha::protocol::Transaction(tx.getTransport()); + new (tx_list.add_transactions()) iroha::protocol::Transaction( + std::static_pointer_cast(tx)->getTransport()); }); auto error = @@ -557,14 +541,15 @@ TEST_F(TransportBuilderTest, TransactionInteraptedBatch) { */ TEST_F(TransportBuilderTest, BatchWrongOrder) { iroha::protocol::TxList tx_list; - auto batch = getValidBatch(0, 10); + auto batch = framework::batch::createUnsignedBatchTransactions( + interface::types::BatchType::ATOMIC, 10); std::for_each(std::begin(batch) + 3, std::end(batch), [&tx_list](auto &tx) { - new (tx_list.add_transactions()) - iroha::protocol::Transaction(tx.getTransport()); + new (tx_list.add_transactions()) iroha::protocol::Transaction( + std::static_pointer_cast(tx)->getTransport()); }); std::for_each(std::begin(batch), std::begin(batch) + 3, [&tx_list](auto &tx) { - new (tx_list.add_transactions()) - iroha::protocol::Transaction(tx.getTransport()); + new (tx_list.add_transactions()) iroha::protocol::Transaction( + std::static_pointer_cast(tx)->getTransport()); }); auto error = framework::expected::err(TransactionSequenceBuilder().build(tx_list)); From 9669314b83771977957ce04eb9c2fb7873a43907 Mon Sep 17 00:00:00 2001 From: kamilsa Date: Thu, 19 Jul 2018 15:32:56 +0300 Subject: [PATCH 58/97] Batch to pcs and tx processor (#1571) * Batch to pcs and tx processor Signed-off-by: kamilsa --- .../impl/peer_communication_service_impl.cpp | 14 ++++- .../impl/peer_communication_service_impl.hpp | 5 +- irohad/network/peer_communication_service.hpp | 10 ++- .../impl/transaction_processor_impl.cpp | 20 +++++- .../torii/processor/transaction_processor.hpp | 12 +++- .../processor/transaction_processor_impl.hpp | 6 +- test/framework/batch_helper.hpp | 30 +++++++++ test/module/irohad/network/network_mocks.hpp | 6 +- .../processor/transaction_processor_test.cpp | 62 +++++++++++++++++++ .../irohad/torii/torii_service_test.cpp | 5 +- 10 files changed, 161 insertions(+), 9 deletions(-) diff --git a/irohad/network/impl/peer_communication_service_impl.cpp b/irohad/network/impl/peer_communication_service_impl.cpp index 58a029a000..3dddd7bb66 100644 --- a/irohad/network/impl/peer_communication_service_impl.cpp +++ b/irohad/network/impl/peer_communication_service_impl.cpp @@ -16,6 +16,8 @@ limitations under the License. #include "network/impl/peer_communication_service_impl.hpp" +#include "interfaces/iroha_internal/transaction_batch.hpp" + namespace iroha { namespace network { PeerCommunicationServiceImpl::PeerCommunicationServiceImpl( @@ -30,12 +32,20 @@ namespace iroha { } void PeerCommunicationServiceImpl::propagate_transaction( - std::shared_ptr - transaction) { + std::shared_ptr transaction) + const { log_->info("propagate tx"); ordering_gate_->propagateTransaction(transaction); } + void PeerCommunicationServiceImpl::propagate_batch( + const shared_model::interface::TransactionBatch &batch) const { + log_->info("propagate batch"); + for (const auto tx : batch.transactions()) { + ordering_gate_->propagateTransaction(tx); + } + } + rxcpp::observable> PeerCommunicationServiceImpl::on_proposal() const { return ordering_gate_->on_proposal(); diff --git a/irohad/network/impl/peer_communication_service_impl.hpp b/irohad/network/impl/peer_communication_service_impl.hpp index 8ac586efde..489236a092 100644 --- a/irohad/network/impl/peer_communication_service_impl.hpp +++ b/irohad/network/impl/peer_communication_service_impl.hpp @@ -37,7 +37,10 @@ namespace iroha { void propagate_transaction( std::shared_ptr - transaction) override; + transaction) const override; + + void propagate_batch(const shared_model::interface::TransactionBatch + &batch) const override; rxcpp::observable> on_proposal() const override; diff --git a/irohad/network/peer_communication_service.hpp b/irohad/network/peer_communication_service.hpp index be4243abc1..6a6bf6c6da 100644 --- a/irohad/network/peer_communication_service.hpp +++ b/irohad/network/peer_communication_service.hpp @@ -27,6 +27,7 @@ namespace shared_model { class Block; class Proposal; class Transaction; + class TransactionBatch; } // namespace interface } // namespace shared_model @@ -48,7 +49,14 @@ namespace iroha { */ virtual void propagate_transaction( std::shared_ptr - transaction) = 0; + transaction) const = 0; + + /** + * Propagate batch to the network + * @param batch - batch for propagation + */ + virtual void propagate_batch( + const shared_model::interface::TransactionBatch &batch) const = 0; /** * Event is triggered when proposal arrives from network. diff --git a/irohad/torii/processor/impl/transaction_processor_impl.cpp b/irohad/torii/processor/impl/transaction_processor_impl.cpp index f53c994dcd..332bec52de 100644 --- a/irohad/torii/processor/impl/transaction_processor_impl.cpp +++ b/irohad/torii/processor/impl/transaction_processor_impl.cpp @@ -22,6 +22,7 @@ #include "backend/protobuf/transaction.hpp" #include "interfaces/iroha_internal/block.hpp" #include "interfaces/iroha_internal/proposal.hpp" +#include "interfaces/iroha_internal/transaction_sequence.hpp" #include "validation/stateful_validator_common.hpp" namespace iroha { @@ -146,7 +147,8 @@ namespace iroha { } void TransactionProcessorImpl::transactionHandle( - std::shared_ptr transaction) { + std::shared_ptr transaction) + const { log_->info("handle transaction"); if (boost::size(transaction->signatures()) < transaction->quorum()) { log_->info("waiting for quorum signatures"); @@ -158,6 +160,22 @@ namespace iroha { pcs_->propagate_transaction(transaction); } + void TransactionProcessorImpl::transactionSequenceHandle( + const shared_model::interface::TransactionSequence + &transaction_sequence) const { + for (const auto &batch : transaction_sequence.batches()) { + if (batch.hasAllSignatures()) { + pcs_->propagate_batch(batch); + } else { + // TODO kamilsa 16.07.18 propagate full batch to mst when its + // interface is updated + for (const auto tx : batch.transactions()) { + mst_processor_->propagateTransaction(tx); + } + } + } + } + rxcpp::observable< std::shared_ptr> TransactionProcessorImpl::transactionNotifier() { diff --git a/irohad/torii/processor/transaction_processor.hpp b/irohad/torii/processor/transaction_processor.hpp index 3892ae10d6..e40b9dbfc1 100644 --- a/irohad/torii/processor/transaction_processor.hpp +++ b/irohad/torii/processor/transaction_processor.hpp @@ -24,6 +24,7 @@ namespace shared_model { namespace interface { class Transaction; class TransactionResponse; + class TransactionSequence; } // namespace interface } // namespace shared_model @@ -42,7 +43,16 @@ namespace iroha { */ virtual void transactionHandle( std::shared_ptr - transaction) = 0; + transaction) const = 0; + + /** + * Process transaction sequence and propagate batches from it either to + * the MST or PCS + * @param transaction_sequence - transaction sequence for processing + */ + virtual void transactionSequenceHandle( + const shared_model::interface::TransactionSequence + &transaction_sequence) const = 0; /** * Subscribers will be notified with transaction status diff --git a/irohad/torii/processor/transaction_processor_impl.hpp b/irohad/torii/processor/transaction_processor_impl.hpp index 6f8915ce7a..7a2ff0c53e 100644 --- a/irohad/torii/processor/transaction_processor_impl.hpp +++ b/irohad/torii/processor/transaction_processor_impl.hpp @@ -40,7 +40,11 @@ namespace iroha { void transactionHandle( std::shared_ptr transaction) - override; + const override; + + void transactionSequenceHandle( + const shared_model::interface::TransactionSequence + &transaction_sequence) const override; rxcpp::observable< std::shared_ptr> diff --git a/test/framework/batch_helper.hpp b/test/framework/batch_helper.hpp index 58cd0706b2..929747bcb1 100644 --- a/test/framework/batch_helper.hpp +++ b/test/framework/batch_helper.hpp @@ -7,7 +7,11 @@ #define IROHA_BATCH_HELPER_HPP #include + +#include "framework/result_fixture.hpp" +#include "interfaces/iroha_internal/transaction_batch.hpp" #include "module/shared_model/builders/protobuf/test_transaction_builder.hpp" +#include "validators/transactions_collection/batch_order_validator.hpp" namespace framework { namespace batch { @@ -165,6 +169,32 @@ namespace framework { return createUnsignedBatchTransactions(batch_type, creators, now); } + auto createValidBatch(const size_t &size) { + using namespace shared_model::validation; + using TxValidator = + TransactionValidator>; + + using TxsValidator = + UnsignedTransactionsCollectionValidator; + + auto batch_type = shared_model::interface::types::BatchType::ATOMIC; + std::vector> + transaction_fields; + for (size_t i = 0; i < size; ++i) { + transaction_fields.push_back(std::make_pair( + batch_type, "account" + std::to_string(i) + "@domain")); + } + + auto txs = createBatchOneSignTransactions(transaction_fields); + auto result_batch = + shared_model::interface::TransactionBatch::createTransactionBatch( + txs, TxsValidator()); + + return framework::expected::val(result_batch).value().value; + } + } // namespace batch } // namespace framework diff --git a/test/module/irohad/network/network_mocks.hpp b/test/module/irohad/network/network_mocks.hpp index 62c540a10b..b37eac58a2 100644 --- a/test/module/irohad/network/network_mocks.hpp +++ b/test/module/irohad/network/network_mocks.hpp @@ -19,6 +19,7 @@ #define IROHA_NETWORK_MOCKS_HPP #include +#include "interfaces/iroha_internal/transaction_batch.hpp" #include "network/block_loader.hpp" #include "network/consensus_gate.hpp" #include "network/ordering_gate.hpp" @@ -34,10 +35,13 @@ namespace iroha { namespace network { class MockPeerCommunicationService : public PeerCommunicationService { public: - MOCK_METHOD1( + MOCK_CONST_METHOD1( propagate_transaction, void(std::shared_ptr)); + MOCK_CONST_METHOD1(propagate_batch, + void(const shared_model::interface::TransactionBatch &)); + MOCK_CONST_METHOD0( on_proposal, rxcpp::observable< diff --git a/test/module/irohad/torii/processor/transaction_processor_test.cpp b/test/module/irohad/torii/processor/transaction_processor_test.cpp index 157923f328..4765b570a0 100644 --- a/test/module/irohad/torii/processor/transaction_processor_test.cpp +++ b/test/module/irohad/torii/processor/transaction_processor_test.cpp @@ -7,8 +7,10 @@ #include "builders/protobuf/common_objects/proto_signature_builder.hpp" #include "builders/protobuf/proposal.hpp" #include "builders/protobuf/transaction.hpp" +#include "framework/batch_helper.hpp" #include "framework/specified_visitor.hpp" #include "framework/test_subscriber.hpp" +#include "interfaces/iroha_internal/transaction_sequence.hpp" #include "module/irohad/multi_sig_transactions/mst_mocks.hpp" #include "module/irohad/network/network_mocks.hpp" #include "module/shared_model/builders/protobuf/test_block_builder.hpp" @@ -140,6 +142,66 @@ TEST_F(TransactionProcessorTest, TransactionProcessorOnProposalTest) { validateStatuses(txs); } +/** + * @given transactions from the same batch + * @when transactions sequence is created and propagated @and all transactions + * were returned by pcs in proposal notifier + * @then all transactions have stateless valid status + */ +TEST_F(TransactionProcessorTest, TransactionProcessorOnProposalBatchTest) { + using namespace shared_model::validation; + using TxValidator = + TransactionValidator>; + + using TxsValidator = + UnsignedTransactionsCollectionValidator; + + auto transactions = + framework::batch::createValidBatch(proposal_size).transactions(); + + auto wrapper = + make_test_subscriber(tp->transactionNotifier(), proposal_size); + wrapper.subscribe([this](auto response) { + status_map[response->transactionHash()] = response; + }); + + auto transaction_sequence_result = + shared_model::interface::TransactionSequence::createTransactionSequence( + transactions, TxsValidator()); + auto transaction_sequence = + framework::expected::val(transaction_sequence_result).value().value; + + EXPECT_CALL(*mp, propagateTransactionImpl(_)).Times(0); + EXPECT_CALL(*pcs, propagate_batch(_)) + .Times(transaction_sequence.batches().size()); + + tp->transactionSequenceHandle(transaction_sequence); + + // create proposal from sequence transactions and notify about it + std::vector proto_transactions; + + std::transform( + transactions.begin(), + transactions.end(), + std::back_inserter(proto_transactions), + [](const auto tx) { + return *std::static_pointer_cast(tx); + }); + + auto proposal = std::make_shared( + TestProposalBuilder().transactions(proto_transactions).build()); + + prop_notifier.get_subscriber().on_next(proposal); + prop_notifier.get_subscriber().on_completed(); + + ASSERT_TRUE(wrapper.validate()); + + SCOPED_TRACE("Stateless valid status verification"); + validateStatuses( + proto_transactions); +} + /** * @given transaction processor * @when transactions compose proposal which is sent to peer diff --git a/test/module/irohad/torii/torii_service_test.cpp b/test/module/irohad/torii/torii_service_test.cpp index 3f1f35cfcf..c28deb1aa1 100644 --- a/test/module/irohad/torii/torii_service_test.cpp +++ b/test/module/irohad/torii/torii_service_test.cpp @@ -62,7 +62,10 @@ class CustomPeerCommunicationServiceMock : public PeerCommunicationService { void propagate_transaction( std::shared_ptr transaction) - override {} + const override {} + + void propagate_batch( + const shared_model::interface::TransactionBatch &batch) const override {} rxcpp::observable> on_proposal() const override { From 719d34449730742527c270855bb941e16125dd07 Mon Sep 17 00:00:00 2001 From: Nikita Alekseev Date: Thu, 19 Jul 2018 16:43:34 +0300 Subject: [PATCH 59/97] Replace builders with common_objects_factory in wsv_query (#1573) Signed-off-by: Nikita Alekseev --- irohad/ametsuchi/CMakeLists.txt | 4 + .../ametsuchi/impl/mutable_storage_impl.cpp | 6 +- .../ametsuchi/impl/mutable_storage_impl.hpp | 5 +- .../ametsuchi/impl/postgres_block_index.hpp | 2 +- .../ametsuchi/impl/postgres_block_query.hpp | 2 +- ...gres_ordering_service_persistent_state.hpp | 2 +- .../ametsuchi/impl/postgres_wsv_command.cpp | 8 + .../ametsuchi/impl/postgres_wsv_command.hpp | 2 +- irohad/ametsuchi/impl/postgres_wsv_common.hpp | 176 ------------------ irohad/ametsuchi/impl/postgres_wsv_query.cpp | 77 ++++++-- irohad/ametsuchi/impl/postgres_wsv_query.hpp | 17 +- irohad/ametsuchi/impl/soci_utils.hpp | 42 +++++ irohad/ametsuchi/impl/storage_impl.cpp | 21 ++- irohad/ametsuchi/impl/storage_impl.hpp | 20 +- irohad/ametsuchi/impl/temporary_wsv_impl.cpp | 6 +- irohad/ametsuchi/impl/temporary_wsv_impl.hpp | 5 +- irohad/main/application.cpp | 7 +- .../common_objects/common_objects_factory.hpp | 5 +- test/module/irohad/ametsuchi/CMakeLists.txt | 1 + .../irohad/ametsuchi/ametsuchi_fixture.hpp | 10 +- .../irohad/ametsuchi/ametsuchi_test.cpp | 7 +- .../irohad/ametsuchi/kv_storage_test.cpp | 2 +- .../irohad/ametsuchi/storage_init_test.cpp | 11 +- .../ametsuchi/wsv_query_command_test.cpp | 4 +- 24 files changed, 210 insertions(+), 232 deletions(-) delete mode 100644 irohad/ametsuchi/impl/postgres_wsv_common.hpp create mode 100644 irohad/ametsuchi/impl/soci_utils.hpp diff --git a/irohad/ametsuchi/CMakeLists.txt b/irohad/ametsuchi/CMakeLists.txt index 0a3be8a12d..cf95605824 100644 --- a/irohad/ametsuchi/CMakeLists.txt +++ b/irohad/ametsuchi/CMakeLists.txt @@ -25,3 +25,7 @@ target_link_libraries(ametsuchi SOCI::core SOCI::postgresql ) + +target_compile_definitions(ametsuchi + PRIVATE SOCI_USE_BOOST HAVE_BOOST + ) diff --git a/irohad/ametsuchi/impl/mutable_storage_impl.cpp b/irohad/ametsuchi/impl/mutable_storage_impl.cpp index f29fa24f5e..c934aa99a7 100644 --- a/irohad/ametsuchi/impl/mutable_storage_impl.cpp +++ b/irohad/ametsuchi/impl/mutable_storage_impl.cpp @@ -23,16 +23,18 @@ #include "ametsuchi/impl/postgres_wsv_command.hpp" #include "ametsuchi/impl/postgres_wsv_query.hpp" #include "ametsuchi/wsv_command.hpp" +#include "interfaces/common_objects/common_objects_factory.hpp" #include "model/sha3_hash.hpp" namespace iroha { namespace ametsuchi { MutableStorageImpl::MutableStorageImpl( shared_model::interface::types::HashType top_hash, - std::unique_ptr sql) + std::unique_ptr sql, + std::shared_ptr factory) : top_hash_(top_hash), sql_(std::move(sql)), - wsv_(std::make_shared(*sql_)), + wsv_(std::make_shared(*sql_, factory)), executor_(std::make_shared(*sql_)), block_index_(std::make_unique(*sql_)), command_executor_(std::make_shared(wsv_, executor_)), diff --git a/irohad/ametsuchi/impl/mutable_storage_impl.hpp b/irohad/ametsuchi/impl/mutable_storage_impl.hpp index 9ca05de4eb..bed99caa4b 100644 --- a/irohad/ametsuchi/impl/mutable_storage_impl.hpp +++ b/irohad/ametsuchi/impl/mutable_storage_impl.hpp @@ -23,6 +23,7 @@ #include "ametsuchi/mutable_storage.hpp" #include "execution/command_executor.hpp" +#include "interfaces/common_objects/common_objects_factory.hpp" #include "logger/logger.hpp" namespace iroha { @@ -37,7 +38,9 @@ namespace iroha { public: MutableStorageImpl(shared_model::interface::types::HashType top_hash, - std::unique_ptr sql); + std::unique_ptr sql, + std::shared_ptr + factory); bool apply( const shared_model::interface::Block &block, diff --git a/irohad/ametsuchi/impl/postgres_block_index.hpp b/irohad/ametsuchi/impl/postgres_block_index.hpp index a2f0e20141..60a917bc83 100644 --- a/irohad/ametsuchi/impl/postgres_block_index.hpp +++ b/irohad/ametsuchi/impl/postgres_block_index.hpp @@ -21,7 +21,7 @@ #include #include "ametsuchi/impl/block_index.hpp" -#include "ametsuchi/impl/postgres_wsv_common.hpp" +#include "ametsuchi/impl/soci_utils.hpp" #include "interfaces/transaction.hpp" #include "logger/logger.hpp" diff --git a/irohad/ametsuchi/impl/postgres_block_query.hpp b/irohad/ametsuchi/impl/postgres_block_query.hpp index cf43616657..1d6e1cd6ca 100644 --- a/irohad/ametsuchi/impl/postgres_block_query.hpp +++ b/irohad/ametsuchi/impl/postgres_block_query.hpp @@ -23,7 +23,7 @@ #include "ametsuchi/block_query.hpp" #include "ametsuchi/impl/flat_file/flat_file.hpp" #include "logger/logger.hpp" -#include "postgres_wsv_common.hpp" +#include "ametsuchi/impl/soci_utils.hpp" namespace iroha { namespace ametsuchi { diff --git a/irohad/ametsuchi/impl/postgres_ordering_service_persistent_state.hpp b/irohad/ametsuchi/impl/postgres_ordering_service_persistent_state.hpp index af2013d92f..5c7dc45106 100644 --- a/irohad/ametsuchi/impl/postgres_ordering_service_persistent_state.hpp +++ b/irohad/ametsuchi/impl/postgres_ordering_service_persistent_state.hpp @@ -18,7 +18,7 @@ #ifndef IROHA_POSTGRES_ORDERING_SERVICE_PERSISTENT_STATE_HPP #define IROHA_POSTGRES_ORDERING_SERVICE_PERSISTENT_STATE_HPP -#include "ametsuchi/impl/postgres_wsv_common.hpp" +#include "ametsuchi/impl/soci_utils.hpp" #include "ametsuchi/ordering_service_persistent_state.hpp" #include "common/result.hpp" #include "logger/logger.hpp" diff --git a/irohad/ametsuchi/impl/postgres_wsv_command.cpp b/irohad/ametsuchi/impl/postgres_wsv_command.cpp index 0cbdd8c4a0..d5b77038af 100644 --- a/irohad/ametsuchi/impl/postgres_wsv_command.cpp +++ b/irohad/ametsuchi/impl/postgres_wsv_command.cpp @@ -17,8 +17,16 @@ #include "ametsuchi/impl/postgres_wsv_command.hpp" +#include + #include + #include "backend/protobuf/permissions.hpp" +#include "interfaces/common_objects/account.hpp" +#include "interfaces/common_objects/account_asset.hpp" +#include "interfaces/common_objects/asset.hpp" +#include "interfaces/common_objects/domain.hpp" +#include "interfaces/common_objects/peer.hpp" namespace iroha { namespace ametsuchi { diff --git a/irohad/ametsuchi/impl/postgres_wsv_command.hpp b/irohad/ametsuchi/impl/postgres_wsv_command.hpp index 9785cb98f1..9f1cbe7ae1 100644 --- a/irohad/ametsuchi/impl/postgres_wsv_command.hpp +++ b/irohad/ametsuchi/impl/postgres_wsv_command.hpp @@ -20,7 +20,7 @@ #include "ametsuchi/wsv_command.hpp" -#include "ametsuchi/impl/postgres_wsv_common.hpp" +#include "ametsuchi/impl/soci_utils.hpp" namespace iroha { namespace ametsuchi { diff --git a/irohad/ametsuchi/impl/postgres_wsv_common.hpp b/irohad/ametsuchi/impl/postgres_wsv_common.hpp deleted file mode 100644 index acb4080105..0000000000 --- a/irohad/ametsuchi/impl/postgres_wsv_common.hpp +++ /dev/null @@ -1,176 +0,0 @@ -/** - * Copyright Soramitsu Co., Ltd. 2018 All Rights Reserved. - * http://soramitsu.co.jp - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef IROHA_POSTGRES_WSV_COMMON_HPP -#define IROHA_POSTGRES_WSV_COMMON_HPP - -#include - -#define SOCI_USE_BOOST -#define HAVE_BOOST -#include -#include - -#include "builders/default_builders.hpp" -#include "common/result.hpp" -#include "logger/logger.hpp" - -namespace iroha { - namespace ametsuchi { - - /** - * Transforms soci::rowset to vector of Ts by applying - * transform_func - * @tparam T - type to transform to - * @tparam Operator - type of transformation function, must return T - * @param result - soci::rowset which contains several rows from - * the database - * @param transform_func - function which transforms result row to T - * @return vector of target type - */ - template - std::vector transform(const soci::rowset &result, - Operator &&transform_func) noexcept { - std::vector values; - std::transform(result.begin(), - result.end(), - std::back_inserter(values), - transform_func); - - return values; - } - - /** - * Execute build function and return error in case it throws - * @tparam T - result value type - * @param f - function which returns BuilderResult - * @return whatever f returns, or error in case exception has been thrown - */ - template - static inline auto tryBuild(BuildFunc &&f) noexcept -> decltype(f()) { - try { - return f(); - } catch (std::exception &e) { - return expected::makeError(std::make_shared(e.what())); - } - } - - template - void processSoci(soci::statement &st, - soci::indicator &ind, - ParamType &row, - Function f) { - while (st.fetch()) { - switch (ind) { - case soci::i_ok: - f(row); - case soci::i_null: - case soci::i_truncated: - break; - } - } - } - - static inline shared_model::builder::BuilderResult< - shared_model::interface::Account> - makeAccount(const std::string &account_id, - const std::string &domain_id, - const shared_model::interface::types::QuorumType &quorum, - const std::string &data) noexcept { - return tryBuild([&] { - return shared_model::builder::DefaultAccountBuilder() - .accountId(account_id) - .domainId(domain_id) - .quorum(quorum) - .jsonData(data) - .build(); - }); - } - - static inline shared_model::builder::BuilderResult< - shared_model::interface::Asset> - makeAsset(const std::string &asset_id, - const std::string &domain_id, - const int32_t precision) noexcept { - return tryBuild([&] { - return shared_model::builder::DefaultAssetBuilder() - .assetId(asset_id) - .domainId(domain_id) - .precision(precision) - .build(); - }); - } - - static inline shared_model::builder::BuilderResult< - shared_model::interface::AccountAsset> - makeAccountAsset(const std::string &account_id, - const std::string &asset_id, - const std::string &amount) noexcept { - return tryBuild([&] { - return shared_model::builder::DefaultAccountAssetBuilder() - .accountId(account_id) - .assetId(asset_id) - .balance(shared_model::interface::Amount(amount)) - .build(); - }); - } - - static inline shared_model::builder::BuilderResult< - shared_model::interface::Peer> - makePeer(const soci::row &row) noexcept { - return tryBuild([&row] { - return shared_model::builder::DefaultPeerBuilder() - .pubkey(shared_model::crypto::PublicKey( - shared_model::crypto::Blob::fromHexString( - row.get(0)))) - .address(row.get(1)) - .build(); - }); - } - - static inline shared_model::builder::BuilderResult< - shared_model::interface::Domain> - makeDomain(const std::string &domain_id, const std::string &role) noexcept { - return tryBuild([&domain_id, &role] { - return shared_model::builder::DefaultDomainBuilder() - .domainId(domain_id) - .defaultRole(role) - .build(); - }); - } - - /** - * Transforms result to optional - * value -> optional - * error -> nullopt - * @tparam T type of object inside - * @param result BuilderResult - * @return optional - */ - template - static inline boost::optional> fromResult( - const shared_model::builder::BuilderResult &result) { - return result.match( - [](const expected::Value> &v) { - return boost::make_optional(v.value); - }, - [](const expected::Error> &e) - -> boost::optional> { return boost::none; }); - } - } // namespace ametsuchi -} // namespace iroha -#endif // IROHA_POSTGRES_WSV_COMMON_HPP diff --git a/irohad/ametsuchi/impl/postgres_wsv_query.cpp b/irohad/ametsuchi/impl/postgres_wsv_query.cpp index b88ed5f42c..224da8775f 100644 --- a/irohad/ametsuchi/impl/postgres_wsv_query.cpp +++ b/irohad/ametsuchi/impl/postgres_wsv_query.cpp @@ -16,7 +16,34 @@ */ #include "ametsuchi/impl/postgres_wsv_query.hpp" + +#include + +#include "ametsuchi/impl/soci_utils.hpp" #include "backend/protobuf/permissions.hpp" +#include "common/result.hpp" + +namespace { + /** + * Transforms result to optional + * value -> optional + * error -> nullopt + * @tparam T type of object inside + * @param result BuilderResult + * @return optional + */ + template + boost::optional> fromResult( + shared_model::interface::CommonObjectsFactory::FactoryResult< + std::unique_ptr> &&result) { + return result.match( + [](iroha::expected::Value> &v) { + return boost::make_optional(std::shared_ptr(std::move(v.value))); + }, + [](iroha::expected::Error) + -> boost::optional> { return boost::none; }); + } +} // namespace namespace iroha { namespace ametsuchi { @@ -36,12 +63,17 @@ namespace iroha { const std::string kAccountId = "account_id"; const std::string kDomainId = "domain_id"; - PostgresWsvQuery::PostgresWsvQuery(soci::session &sql) - : sql_(sql), log_(logger::log("PostgresWsvQuery")) {} + PostgresWsvQuery::PostgresWsvQuery( + soci::session &sql, + std::shared_ptr factory) + : sql_(sql), factory_(factory), log_(logger::log("PostgresWsvQuery")) {} - PostgresWsvQuery::PostgresWsvQuery(std::unique_ptr sql_ptr) + PostgresWsvQuery::PostgresWsvQuery( + std::unique_ptr sql_ptr, + std::shared_ptr factory) : sql_ptr_(std::move(sql_ptr)), sql_(*sql_ptr_), + factory_(factory), log_(logger::log("PostgresWsvQuery")) {} bool PostgresWsvQuery::hasAccountGrantablePermission( @@ -133,8 +165,8 @@ namespace iroha { return boost::none; } - return fromResult( - makeAccount(account_id, domain_id.get(), quorum.get(), data.get())); + return fromResult(factory_->createAccount( + account_id, domain_id.get(), quorum.get(), data.get())); } boost::optional PostgresWsvQuery::getAccountDetail( @@ -221,7 +253,8 @@ namespace iroha { return boost::none; } - return fromResult(makeAsset(asset_id, domain_id.get(), precision.get())); + return fromResult( + factory_->createAsset(asset_id, domain_id.get(), precision.get())); } boost::optional< @@ -234,8 +267,11 @@ namespace iroha { std::vector> assets; for (auto &t : st) { - fromResult(makeAccountAsset(account_id, t.get<1>(), t.get<2>())) | - [&assets](const auto &asset) { assets.push_back(asset); }; + fromResult(factory_->createAccountAsset( + account_id, + t.get<1>(), + shared_model::interface::Amount(t.get<2>()))) + | [&assets](const auto &asset) { assets.push_back(asset); }; } return boost::make_optional(assets); @@ -258,7 +294,8 @@ namespace iroha { return boost::none; } - return fromResult(makeAccountAsset(account_id, asset_id, amount.get())); + return fromResult(factory_->createAccountAsset( + account_id, asset_id, shared_model::interface::Amount(amount.get()))); } boost::optional> @@ -275,7 +312,7 @@ namespace iroha { return boost::none; } - return fromResult(makeDomain(domain_id, role.get())); + return fromResult(factory_->createDomain(domain_id, role.get())); } boost::optional>> @@ -284,16 +321,16 @@ namespace iroha { (sql_.prepare << "SELECT public_key, address FROM peer"); std::vector> peers; - auto results = transform< - shared_model::builder::BuilderResult>( - rows, makePeer); - for (auto &r : results) { - r.match( - [&](expected::Value> - &v) { peers.push_back(v.value); }, - [&](expected::Error> &e) { - log_->info(*e.error); - }); + for (auto &row : rows) { + auto address = row.get(1); + auto key = shared_model::crypto::PublicKey( + shared_model::crypto::Blob::fromHexString(row.get(0))); + + auto peer = factory_->createPeer(address, key); + peer.match( + [&](expected::Value> + &v) { peers.push_back(std::move(v.value)); }, + [&](expected::Error &e) { log_->info(e.error); }); } return peers; } diff --git a/irohad/ametsuchi/impl/postgres_wsv_query.hpp b/irohad/ametsuchi/impl/postgres_wsv_query.hpp index 3fd0c64acd..49795a08dd 100644 --- a/irohad/ametsuchi/impl/postgres_wsv_query.hpp +++ b/irohad/ametsuchi/impl/postgres_wsv_query.hpp @@ -20,14 +20,24 @@ #include "ametsuchi/wsv_query.hpp" -#include "postgres_wsv_common.hpp" +#include + +#include "interfaces/common_objects/common_objects_factory.hpp" +#include "logger/logger.hpp" namespace iroha { namespace ametsuchi { class PostgresWsvQuery : public WsvQuery { public: - explicit PostgresWsvQuery(soci::session &sql); - explicit PostgresWsvQuery(std::unique_ptr sql_ptr); + PostgresWsvQuery( + soci::session &sql, + std::shared_ptr + factory); + + PostgresWsvQuery( + std::unique_ptr sql_ptr, + std::shared_ptr + factory); boost::optional> getAccountRoles(const shared_model::interface::types::AccountIdType @@ -84,6 +94,7 @@ namespace iroha { private: std::unique_ptr sql_ptr_; soci::session &sql_; + std::shared_ptr factory_; logger::Logger log_; }; } // namespace ametsuchi diff --git a/irohad/ametsuchi/impl/soci_utils.hpp b/irohad/ametsuchi/impl/soci_utils.hpp new file mode 100644 index 0000000000..6efcab4a87 --- /dev/null +++ b/irohad/ametsuchi/impl/soci_utils.hpp @@ -0,0 +1,42 @@ +/** + * Copyright Soramitsu Co., Ltd. 2018 All Rights Reserved. + * http://soramitsu.co.jp + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef IROHA_POSTGRES_WSV_COMMON_HPP +#define IROHA_POSTGRES_WSV_COMMON_HPP + +#include + +namespace iroha { + namespace ametsuchi { + template + inline void processSoci(soci::statement &st, + soci::indicator &ind, + ParamType &row, + Function f) { + while (st.fetch()) { + switch (ind) { + case soci::i_ok: + f(row); + case soci::i_null: + case soci::i_truncated: + break; + } + } + } + } // namespace ametsuchi +} // namespace iroha +#endif // IROHA_POSTGRES_WSV_COMMON_HPP diff --git a/irohad/ametsuchi/impl/storage_impl.cpp b/irohad/ametsuchi/impl/storage_impl.cpp index 7acf98dd6f..32c6fd4afa 100644 --- a/irohad/ametsuchi/impl/storage_impl.cpp +++ b/irohad/ametsuchi/impl/storage_impl.cpp @@ -43,11 +43,13 @@ namespace iroha { StorageImpl::StorageImpl(std::string block_store_dir, PostgresOptions postgres_options, std::unique_ptr block_store, - std::shared_ptr connection) + std::shared_ptr connection, + std::shared_ptr factory) : block_store_dir_(std::move(block_store_dir)), postgres_options_(std::move(postgres_options)), block_store_(std::move(block_store)), connection_(connection), + factory_(factory), log_(logger::log("StorageImpl")) {} expected::Result, std::string> @@ -55,7 +57,7 @@ namespace iroha { auto sql = std::make_unique(*connection_); return expected::makeValue>( - std::make_unique(std::move(sql))); + std::make_unique(std::move(sql), factory_)); } expected::Result, std::string> @@ -74,7 +76,8 @@ namespace iroha { [](expected::Error &) { return shared_model::interface::types::HashType(""); }), - std::move(sql))); + std::move(sql), + factory_)); } bool StorageImpl::insertBlock(const shared_model::interface::Block &block) { @@ -212,8 +215,11 @@ DROP TABLE IF EXISTS index_by_id_height_asset; }; expected::Result, std::string> - StorageImpl::create(std::string block_store_dir, - std::string postgres_options) { + StorageImpl::create( + std::string block_store_dir, + std::string postgres_options, + std::shared_ptr + factory) { boost::optional string_res = boost::none; PostgresOptions options(postgres_options); @@ -243,7 +249,8 @@ DROP TABLE IF EXISTS index_by_id_height_asset; new StorageImpl(block_store_dir, options, std::move(ctx.value.block_store), - connection.value))); + connection.value, + factory))); }, [&](expected::Error &error) { storage = error; }); }, @@ -270,7 +277,7 @@ DROP TABLE IF EXISTS index_by_id_height_asset; std::shared_ptr StorageImpl::getWsvQuery() const { auto sql = std::make_unique(*connection_); - return std::make_shared(std::move(sql)); + return std::make_shared(std::move(sql), factory_); } std::shared_ptr StorageImpl::getBlockQuery() const { diff --git a/irohad/ametsuchi/impl/storage_impl.hpp b/irohad/ametsuchi/impl/storage_impl.hpp index 32788dbbde..9a07060aed 100644 --- a/irohad/ametsuchi/impl/storage_impl.hpp +++ b/irohad/ametsuchi/impl/storage_impl.hpp @@ -20,12 +20,15 @@ #include "ametsuchi/storage.hpp" -#include -#include #include #include + +#include +#include + #include "ametsuchi/impl/postgres_options.hpp" #include "ametsuchi/key_value_storage.hpp" +#include "interfaces/common_objects/common_objects_factory.hpp" #include "logger/logger.hpp" namespace iroha { @@ -54,7 +57,10 @@ namespace iroha { public: static expected::Result, std::string> create( - std::string block_store_dir, std::string postgres_connection); + std::string block_store_dir, + std::string postgres_connection, + std::shared_ptr + factory_); expected::Result, std::string> createTemporaryWsv() override; @@ -94,7 +100,9 @@ namespace iroha { StorageImpl(std::string block_store_dir, PostgresOptions postgres_options, std::unique_ptr block_store, - std::shared_ptr connection); + std::shared_ptr connection, + std::shared_ptr + factory); /** * Folder with raw blocks @@ -112,11 +120,13 @@ namespace iroha { // Allows multiple readers and a single writer std::shared_timed_mutex rw_lock_; - logger::Logger log_; + std::shared_ptr factory_; rxcpp::subjects::subject> notifier_; + logger::Logger log_; + protected: static const std::string &init_; }; diff --git a/irohad/ametsuchi/impl/temporary_wsv_impl.cpp b/irohad/ametsuchi/impl/temporary_wsv_impl.cpp index d8cf25b059..bbc5fa72bd 100644 --- a/irohad/ametsuchi/impl/temporary_wsv_impl.cpp +++ b/irohad/ametsuchi/impl/temporary_wsv_impl.cpp @@ -22,9 +22,11 @@ namespace iroha { namespace ametsuchi { - TemporaryWsvImpl::TemporaryWsvImpl(std::unique_ptr sql) + TemporaryWsvImpl::TemporaryWsvImpl(std::unique_ptr sql, + std::shared_ptr + factory) : sql_(std::move(sql)), - wsv_(std::make_shared(*sql_)), + wsv_(std::make_shared(*sql_, factory)), executor_(std::make_shared(*sql_)), command_executor_(std::make_shared(wsv_, executor_)), command_validator_(std::make_shared(wsv_)), diff --git a/irohad/ametsuchi/impl/temporary_wsv_impl.hpp b/irohad/ametsuchi/impl/temporary_wsv_impl.hpp index 1ed34f2c3c..2d81360c85 100644 --- a/irohad/ametsuchi/impl/temporary_wsv_impl.hpp +++ b/irohad/ametsuchi/impl/temporary_wsv_impl.hpp @@ -22,6 +22,7 @@ #include "ametsuchi/temporary_wsv.hpp" #include "execution/command_executor.hpp" +#include "interfaces/common_objects/common_objects_factory.hpp" #include "logger/logger.hpp" namespace iroha { @@ -43,7 +44,9 @@ namespace iroha { bool is_released_; }; - explicit TemporaryWsvImpl(std::unique_ptr sql); + explicit TemporaryWsvImpl(std::unique_ptr sql, + std::shared_ptr + factory); expected::Result apply( const shared_model::interface::Transaction &, diff --git a/irohad/main/application.cpp b/irohad/main/application.cpp index b24fa545fc..7fffca37cc 100644 --- a/irohad/main/application.cpp +++ b/irohad/main/application.cpp @@ -18,6 +18,7 @@ #include "main/application.hpp" #include "ametsuchi/impl/postgres_ordering_service_persistent_state.hpp" #include "ametsuchi/impl/wsv_restorer_impl.hpp" +#include "backend/protobuf/common_objects/proto_common_objects_factory.hpp" #include "consensus/yac/impl/supermajority_checker_impl.hpp" #include "multi_sig_transactions/gossip_propagation_strategy.hpp" #include "multi_sig_transactions/mst_processor_impl.hpp" @@ -25,6 +26,7 @@ #include "multi_sig_transactions/mst_time_provider_impl.hpp" #include "multi_sig_transactions/storage/mst_storage_impl.hpp" #include "multi_sig_transactions/transport/mst_transport_grpc.hpp" +#include "validators/field_validator.hpp" using namespace iroha; using namespace iroha::ametsuchi; @@ -103,7 +105,10 @@ void Irohad::dropStorage() { * Initializing iroha daemon storage */ void Irohad::initStorage() { - auto storageResult = StorageImpl::create(block_store_dir_, pg_conn_); + auto factory = + std::make_shared>(); + auto storageResult = StorageImpl::create(block_store_dir_, pg_conn_, factory); storageResult.match( [&](expected::Value> &_storage) { storage = _storage.value; diff --git a/shared_model/interfaces/common_objects/common_objects_factory.hpp b/shared_model/interfaces/common_objects/common_objects_factory.hpp index 0381a71915..64161c4361 100644 --- a/shared_model/interfaces/common_objects/common_objects_factory.hpp +++ b/shared_model/interfaces/common_objects/common_objects_factory.hpp @@ -10,9 +10,12 @@ #include "common/result.hpp" #include "interfaces/common_objects/account.hpp" +#include "interfaces/common_objects/account_asset.hpp" +#include "interfaces/common_objects/asset.hpp" +#include "interfaces/common_objects/domain.hpp" #include "interfaces/common_objects/peer.hpp" +#include "interfaces/common_objects/signature.hpp" #include "interfaces/common_objects/types.hpp" -#include "interfaces/common_objects/domain.hpp" namespace shared_model { namespace interface { diff --git a/test/module/irohad/ametsuchi/CMakeLists.txt b/test/module/irohad/ametsuchi/CMakeLists.txt index 2e71ca7c54..f4045a1acb 100644 --- a/test/module/irohad/ametsuchi/CMakeLists.txt +++ b/test/module/irohad/ametsuchi/CMakeLists.txt @@ -58,6 +58,7 @@ target_link_libraries(postgres_options_test add_library(ametsuchi_fixture INTERFACE) target_link_libraries(ametsuchi_fixture INTERFACE integration_framework_config_helper + shared_model_proto_backend SOCI::core SOCI::postgresql ) diff --git a/test/module/irohad/ametsuchi/ametsuchi_fixture.hpp b/test/module/irohad/ametsuchi/ametsuchi_fixture.hpp index 6614f68333..0ee855250f 100644 --- a/test/module/irohad/ametsuchi/ametsuchi_fixture.hpp +++ b/test/module/irohad/ametsuchi/ametsuchi_fixture.hpp @@ -25,9 +25,11 @@ #include #include #include "ametsuchi/impl/storage_impl.hpp" +#include "backend/protobuf/common_objects/proto_common_objects_factory.hpp" #include "common/files.hpp" #include "framework/config_helper.hpp" #include "logger/logger.hpp" +#include "validators/field_validator.hpp" namespace iroha { namespace ametsuchi { @@ -56,7 +58,7 @@ namespace iroha { } virtual void connect() { - StorageImpl::create(block_store_path, pgopt_) + StorageImpl::create(block_store_path, pgopt_, factory) .match([&](iroha::expected::Value> &_storage) { storage = _storage.value; }, [](iroha::expected::Error &error) { @@ -78,6 +80,12 @@ namespace iroha { std::shared_ptr sql; + std::shared_ptr> + factory = + std::make_shared>(); + std::shared_ptr storage; // generate random valid dbname diff --git a/test/module/irohad/ametsuchi/ametsuchi_test.cpp b/test/module/irohad/ametsuchi/ametsuchi_test.cpp index d772265637..2296b9577e 100644 --- a/test/module/irohad/ametsuchi/ametsuchi_test.cpp +++ b/test/module/irohad/ametsuchi/ametsuchi_test.cpp @@ -671,7 +671,7 @@ TEST_F(AmetsuchiTest, TestingStorageWhenDropAll) { "=> insert block " "=> assert that inserted"); std::shared_ptr storage; - auto storageResult = StorageImpl::create(block_store_path, pgopt_); + auto storageResult = StorageImpl::create(block_store_path, pgopt_, factory); storageResult.match( [&](iroha::expected::Value> &_storage) { storage = _storage.value; @@ -698,8 +698,9 @@ TEST_F(AmetsuchiTest, TestingStorageWhenDropAll) { ASSERT_EQ(0, wsv->getPeers().value().size()); std::shared_ptr new_storage; - auto new_storageResult = StorageImpl::create(block_store_path, pgopt_); - storageResult.match( + auto new_storage_result = + StorageImpl::create(block_store_path, pgopt_, factory); + new_storage_result.match( [&](iroha::expected::Value> &_storage) { new_storage = _storage.value; }, diff --git a/test/module/irohad/ametsuchi/kv_storage_test.cpp b/test/module/irohad/ametsuchi/kv_storage_test.cpp index 9a52a1240f..b85e071dea 100644 --- a/test/module/irohad/ametsuchi/kv_storage_test.cpp +++ b/test/module/irohad/ametsuchi/kv_storage_test.cpp @@ -38,7 +38,7 @@ class KVTest : public AmetsuchiTest { protected: void SetUp() override { AmetsuchiTest::SetUp(); - auto storageResult = StorageImpl::create(block_store_path, pgopt_); + auto storageResult = StorageImpl::create(block_store_path, pgopt_, factory); storageResult.match( [&](iroha::expected::Value> &_storage) { storage = _storage.value; diff --git a/test/module/irohad/ametsuchi/storage_init_test.cpp b/test/module/irohad/ametsuchi/storage_init_test.cpp index 576f785fdb..a978bc0c3f 100644 --- a/test/module/irohad/ametsuchi/storage_init_test.cpp +++ b/test/module/irohad/ametsuchi/storage_init_test.cpp @@ -10,7 +10,9 @@ #include #include #include "ametsuchi/impl/storage_impl.hpp" +#include "backend/protobuf/common_objects/proto_common_objects_factory.hpp" #include "framework/config_helper.hpp" +#include "validators/field_validator.hpp" using namespace iroha::ametsuchi; using namespace iroha::expected; @@ -34,6 +36,11 @@ class StorageInitTest : public ::testing::Test { std::string pg_opt_without_dbname_; std::string pgopt_; + std::shared_ptr> + factory = std::make_shared>(); + void TearDown() override { soci::session sql(soci::postgresql, pg_opt_without_dbname_); std::string query = "DROP DATABASE IF EXISTS " + dbname_; @@ -47,7 +54,7 @@ class StorageInitTest : public ::testing::Test { * @then Database is created */ TEST_F(StorageInitTest, CreateStorageWithDatabase) { - StorageImpl::create(block_store_path, pgopt_) + StorageImpl::create(block_store_path, pgopt_, factory) .match([](const Value> &) { SUCCEED(); }, [](const Error &error) { FAIL() << error.error; }); soci::session sql(soci::postgresql, pg_opt_without_dbname_); @@ -66,7 +73,7 @@ TEST_F(StorageInitTest, CreateStorageWithDatabase) { TEST_F(StorageInitTest, CreateStorageWithInvalidPgOpt) { std::string pg_opt = "host=localhost port=5432 users=nonexistinguser dbname=test"; - StorageImpl::create(block_store_path, pg_opt) + StorageImpl::create(block_store_path, pg_opt, factory) .match( [](const Value> &) { FAIL() << "storage created, but should not"; diff --git a/test/module/irohad/ametsuchi/wsv_query_command_test.cpp b/test/module/irohad/ametsuchi/wsv_query_command_test.cpp index aea5b3f907..8c6278f1cd 100644 --- a/test/module/irohad/ametsuchi/wsv_query_command_test.cpp +++ b/test/module/irohad/ametsuchi/wsv_query_command_test.cpp @@ -51,7 +51,7 @@ namespace iroha { sql = std::make_unique(soci::postgresql, pgopt_); command = std::make_unique(*sql); - query = std::make_unique(*sql); + query = std::make_unique(*sql, factory); *sql << init_; } @@ -548,7 +548,7 @@ namespace iroha { sql = std::make_unique(soci::postgresql, pgopt_); command = std::make_unique(*sql); - query = std::make_unique(*sql); + query = std::make_unique(*sql, factory); } }; From 4c36088ffad9d63a1b7e8f00a202d2c9bdc25c93 Mon Sep 17 00:00:00 2001 From: Kitsu Date: Fri, 20 Jul 2018 15:17:57 +0300 Subject: [PATCH 60/97] Fix sm build (#1576) - remove add_subdirectory for removed folder libs/amount - fix -package parameter issue in bindings - fix schema folder path for python proto generation (& docs & jenkins) Signed-off-by: Kitsu --- .jenkinsci/bindings.groovy | 18 ++++++++---------- CMakeLists.txt | 3 ++- cmake/functions.cmake | 1 - docs/source/guides/libraries/python.rst | 4 ++-- example/python/prepare.sh | 4 ++-- shared_model/CMakeLists.txt | 2 +- shared_model/bindings/CMakeLists.txt | 14 +++++++------- 7 files changed, 22 insertions(+), 24 deletions(-) diff --git a/.jenkinsci/bindings.groovy b/.jenkinsci/bindings.groovy index 26212c96d7..c5f3896f45 100644 --- a/.jenkinsci/bindings.groovy +++ b/.jenkinsci/bindings.groovy @@ -69,27 +69,25 @@ def doPythonBindings(os, buildType=Release) { sh "cd build; ctest -R python --output-on-failure" if (os == 'linux') { sh """ - protoc --proto_path=schema \ - --python_out=build/bindings \ - block.proto primitive.proto commands.proto queries.proto responses.proto endpoint.proto + protoc --proto_path=shared_model/schema \ + --python_out=build/bindings shared_model/schema/*.proto """ sh """ - ${env.PBVersion} -m grpc_tools.protoc --proto_path=schema --python_out=build/bindings \ - --grpc_python_out=build/bindings endpoint.proto yac.proto ordering.proto loader.proto + ${env.PBVersion} -m grpc_tools.protoc --proto_path=shared_model/schema --python_out=build/bindings \ + --grpc_python_out=build/bindings shared_model/schema/endpoint.proto """ } else if (os == 'windows') { sh """ - protoc --proto_path=schema \ + protoc --proto_path=shared_model/schema \ --proto_path=/c/Users/Administrator/Downloads/vcpkg-master/vcpkg-master/buildtrees/protobuf/src/protobuf-3.5.1-win32/include \ - --python_out=build/bindings \ - block.proto primitive.proto commands.proto queries.proto responses.proto endpoint.proto + --python_out=build/bindings shared_model/schema/*.proto """ sh """ ${env.PBVersion} -m grpc_tools.protoc \ --proto_path=/c/Users/Administrator/Downloads/vcpkg-master/vcpkg-master/buildtrees/protobuf/src/protobuf-3.5.1-win32/include \ - --proto_path=schema --python_out=build/bindings --grpc_python_out=build/bindings \ - endpoint.proto yac.proto ordering.proto loader.proto + --proto_path=shared_model/schema --python_out=build/bindings --grpc_python_out=build/bindings \ + shared_model/schema/endpoint.proto """ } sh """ diff --git a/CMakeLists.txt b/CMakeLists.txt index b3236b7b34..56497fa95d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -103,7 +103,8 @@ message(STATUS "-DSUPPORT_PYTHON2=${SUPPORT_PYTHON2}") message(STATUS "-DSWIG_CSHARP=${SWIG_CSHARP}") message(STATUS "-DSWIG_NODE=${SWIG_NODE}") -SET(IROHA_SCHEMA_DIR "${PROJECT_SOURCE_DIR}/schema") +set(IROHA_SCHEMA_DIR "${CMAKE_CURRENT_SOURCE_DIR}/schema") +set(SM_SCHEMA_DIR "${PROJECT_SOURCE_DIR}/shared_model/schema") set(SCHEMA_OUT_DIR ${CMAKE_BINARY_DIR}/schema) include_directories( ${PROJECT_SOURCE_DIR}/irohad diff --git a/cmake/functions.cmake b/cmake/functions.cmake index f077186254..93f93523a4 100644 --- a/cmake/functions.cmake +++ b/cmake/functions.cmake @@ -98,7 +98,6 @@ endfunction() function(compile_proto_to_python PROTO) string(REGEX REPLACE "\\.proto$" "_pb2.py" PY_PB ${PROTO}) - set(SM_SCHEMA_DIR "${PROJECT_SOURCE_DIR}/shared_model/schema") if (MSVC) set(GEN_COMMAND "${Protobuf_PROTOC_EXECUTABLE}") set(GEN_ARGS ${Protobuf_INCLUDE_DIR}) diff --git a/docs/source/guides/libraries/python.rst b/docs/source/guides/libraries/python.rst index d5caf4b7c7..685183fbd2 100644 --- a/docs/source/guides/libraries/python.rst +++ b/docs/source/guides/libraries/python.rst @@ -74,8 +74,8 @@ Building Protobuf Files echo "schema" >> iroha-schema/.git/info/sparse-checkout git -C iroha-schema pull schema develop cd iroha-schema - protoc --proto_path=schema --python_out=. block.proto primitive.proto commands.proto queries.proto responses.proto endpoint.proto - python -m grpc_tools.protoc --proto_path=schema --python_out=. --grpc_python_out=. endpoint.proto yac.proto ordering.proto loader.proto + protoc --proto_path=shared_model/schema --python_out=. shared_model/schema/*.proto + python -m grpc_tools.protoc --proto_path=shared_model/schema --python_out=. --grpc_python_out=. shared_model/schema/endpoint.proto Protobuf files can be found in **iroha-schema** folder ('\*_pb2\*.py' files) diff --git a/example/python/prepare.sh b/example/python/prepare.sh index cd6bb3bea3..6af9714c0f 100755 --- a/example/python/prepare.sh +++ b/example/python/prepare.sh @@ -9,5 +9,5 @@ cmake -H$IROHA_HOME -Bbuild -DSWIG_PYTHON=ON -DSUPPORT_PYTHON2=ON; cmake --build build/ --target irohapy -- -j"$(getconf _NPROCESSORS_ONLN)" # generate proto files in current dir -protoc --proto_path=../../shared_model/schema --python_out=. block.proto transaction.proto primitive.proto commands.proto queries.proto qry_responses.proto endpoint.proto -python -m grpc_tools.protoc --proto_path=../../schema --proto_path=../../shared_model/schema --python_out=. --grpc_python_out=. endpoint.proto yac.proto ordering.proto loader.proto +protoc --proto_path=../../shared_model/schema --python_out=. ../../shared_model/schema/*.proto +python -m grpc_tools.protoc --proto_path=../../shared_model/schema --python_out=. --grpc_python_out=. ../../shared_model/schema/endpoint.proto diff --git a/shared_model/CMakeLists.txt b/shared_model/CMakeLists.txt index 8e42c8ff17..b07a33ba7b 100644 --- a/shared_model/CMakeLists.txt +++ b/shared_model/CMakeLists.txt @@ -6,6 +6,7 @@ cmake_minimum_required(VERSION 3.5.1) project(shared_model C CXX) set(IROHA_SCHEMA_DIR "${CMAKE_CURRENT_SOURCE_DIR}/schema") +set(SM_SCHEMA_DIR "${IROHA_SCHEMA_DIR}") include_directories( ${CMAKE_CURRENT_SOURCE_DIR}/../libs ${CMAKE_CURRENT_SOURCE_DIR} @@ -35,7 +36,6 @@ if (NOT IROHA_ROOT_PROJECT) set(SCHEMA_OUT_DIR ${CMAKE_BINARY_DIR}/schema) add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../libs/generator generator) - add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../libs/amount amount) endif () add_subdirectory(backend) diff --git a/shared_model/bindings/CMakeLists.txt b/shared_model/bindings/CMakeLists.txt index 8337f6923a..f1a8d46ee8 100644 --- a/shared_model/bindings/CMakeLists.txt +++ b/shared_model/bindings/CMakeLists.txt @@ -34,13 +34,6 @@ if (SWIG_PYTHON OR SWIG_JAVA OR SWIG_CSHARP OR SWIG_NODE) find_package(swig REQUIRED) include(${SWIG_USE_FILE}) - if (SWIG_JAVA AND SWIG_JAVA_PKG) - set(CMAKE_SWIG_FLAGS -package ${SWIG_JAVA_PKG}) - string(REPLACE "." "/" CMAKE_SWIG_OUTDIR ${SWIG_JAVA_PKG}) - else() - set(CMAKE_SWIG_FLAGS "") - endif() - set_source_files_properties(bindings.i PROPERTIES CPLUSPLUS ON) set_property(GLOBAL PROPERTY SWIG_BUILD_DIR ${CMAKE_CURRENT_BINARY_DIR}) @@ -89,7 +82,14 @@ endif() if (SWIG_JAVA) find_package(JNI REQUIRED) + if (SWIG_JAVA_PKG) + set(CMAKE_SWIG_FLAGS -package ${SWIG_JAVA_PKG}) + string(REPLACE "." "/" CMAKE_SWIG_OUTDIR ${SWIG_JAVA_PKG}) + else() + set(CMAKE_SWIG_FLAGS "") + endif() myswig_add_library(irohajava LANGUAGE java SOURCES bindings.i) + unset(CMAKE_SWIG_FLAGS) swig_link_libraries(irohajava ${Java_LIBRARIES} bindings) # the include path to jni.h and jni_md.h target_include_directories(${SWIG_MODULE_irohajava_REAL_NAME} PUBLIC From 3ae48d5c60132939525604a9899bfa710694e102 Mon Sep 17 00:00:00 2001 From: Kitsu Date: Fri, 20 Jul 2018 17:01:48 +0300 Subject: [PATCH 61/97] Mock builder introduction (#1563) Signed-off-by: Kitsu --- irohad/validation/utils.hpp | 9 +- test/module/irohad/validation/CMakeLists.txt | 27 ++---- .../validation/chain_validation_test.cpp | 91 ++++++------------- .../validation/stateful_validator_test.cpp | 50 +++++----- test/module/shared_model/interface_mocks.hpp | 50 ++++++++++ 5 files changed, 116 insertions(+), 111 deletions(-) create mode 100644 test/module/shared_model/interface_mocks.hpp diff --git a/irohad/validation/utils.hpp b/irohad/validation/utils.hpp index d080f9cd20..7ee6bca64b 100644 --- a/irohad/validation/utils.hpp +++ b/irohad/validation/utils.hpp @@ -3,9 +3,14 @@ * SPDX-License-Identifier: Apache-2.0 */ -#include +#ifndef IROHA_VALIDATION_UTILS +#define IROHA_VALIDATION_UTILS + #include #include + +#include + #include "cryptography/public_key.hpp" #include "interfaces/common_objects/types.hpp" @@ -37,3 +42,5 @@ namespace iroha { } // namespace validation } // namespace iroha + +#endif // IROHA_VALIDATION_UTILS diff --git a/test/module/irohad/validation/CMakeLists.txt b/test/module/irohad/validation/CMakeLists.txt index ae60cb471a..e556b73057 100644 --- a/test/module/irohad/validation/CMakeLists.txt +++ b/test/module/irohad/validation/CMakeLists.txt @@ -1,29 +1,14 @@ -# -# Copyright Soramitsu Co., Ltd. 2017 All Rights Reserved. -# http://soramitsu.co.jp -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# +# Copyright Soramitsu Co., Ltd. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 addtest(chain_validation_test chain_validation_test.cpp) target_link_libraries(chain_validation_test chain_validator - shared_model_stateless_validation - shared_model_proto_backend + shared_model_default_builders ) addtest(stateful_validator_test stateful_validator_test.cpp) target_link_libraries(stateful_validator_test - stateful_validator - shared_model_default_builders - ) + stateful_validator + shared_model_default_builders + ) diff --git a/test/module/irohad/validation/chain_validation_test.cpp b/test/module/irohad/validation/chain_validation_test.cpp index 7ecacb8733..42369cfeb0 100644 --- a/test/module/irohad/validation/chain_validation_test.cpp +++ b/test/module/irohad/validation/chain_validation_test.cpp @@ -1,40 +1,22 @@ /** - * Copyright Soramitsu Co., Ltd. 2018 All Rights Reserved. - * http://soramitsu.co.jp - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 */ -#include "builders/common_objects/peer_builder.hpp" -#include "builders/protobuf/common_objects/proto_peer_builder.hpp" #include "module/irohad/ametsuchi/ametsuchi_mocks.hpp" #include "module/irohad/consensus/yac/yac_mocks.hpp" -#include "module/shared_model/builders/protobuf/test_block_builder.hpp" +#include "module/shared_model/interface_mocks.hpp" #include "validation/impl/chain_validator_impl.hpp" -#include "validators/field_validator.hpp" using namespace iroha; using namespace iroha::validation; using namespace iroha::ametsuchi; +using ::testing::_; using ::testing::A; using ::testing::ByRef; using ::testing::InvokeArgument; using ::testing::Return; -using ::testing::_; - -auto zero_string = std::string(32, '\0'); -auto fake_hash = shared_model::crypto::Hash(zero_string); class ChainValidationTest : public ::testing::Test { public: @@ -43,19 +25,17 @@ class ChainValidationTest : public ::testing::Test { storage = std::make_shared(); query = std::make_shared(); peers = std::vector>(); - } - /** - * Get block builder to build blocks for tests - * @return block builder - */ - auto getBlockBuilder() const { - return TestBlockBuilder() - .transactions(std::vector{}) - .height(1) - .prevHash(hash); + EXPECT_CALL(*block, height()).WillRepeatedly(Return(1)); + EXPECT_CALL(*block, prevHash()).WillRepeatedly(testing::ReturnRef(hash)); + EXPECT_CALL(*block, signatures()) + .WillRepeatedly( + testing::Return(std::initializer_list{})); + EXPECT_CALL(*block, payload()).WillRepeatedly(testing::ReturnRef(payload)); } + shared_model::crypto::Blob payload{"blob"}; + std::vector> peers; std::shared_ptr @@ -65,6 +45,7 @@ class ChainValidationTest : public ::testing::Test { std::shared_ptr validator; std::shared_ptr storage; std::shared_ptr query; + std::shared_ptr block = std::make_shared(); }; /** @@ -74,17 +55,15 @@ class ChainValidationTest : public ::testing::Test { */ TEST_F(ChainValidationTest, ValidCase) { // Valid previous hash, has supermajority, correct peers subset => valid - auto block = getBlockBuilder().build(); - - EXPECT_CALL(*supermajority_checker, hasSupermajority(block.signatures(), _)) + EXPECT_CALL(*supermajority_checker, hasSupermajority(block->signatures(), _)) .WillOnce(Return(true)); EXPECT_CALL(*query, getPeers()).WillOnce(Return(peers)); - EXPECT_CALL(*storage, apply(testing::Ref(block), _)) - .WillOnce(InvokeArgument<1>(ByRef(block), ByRef(*query), ByRef(hash))); + EXPECT_CALL(*storage, apply(testing::Ref(*block), _)) + .WillOnce(InvokeArgument<1>(ByRef(*block), ByRef(*query), ByRef(hash))); - ASSERT_TRUE(validator->validateBlock(block, *storage)); + ASSERT_TRUE(validator->validateBlock(*block, *storage)); } /** @@ -94,18 +73,16 @@ TEST_F(ChainValidationTest, ValidCase) { */ TEST_F(ChainValidationTest, FailWhenDifferentPrevHash) { // Invalid previous hash, has supermajority, correct peers subset => invalid - auto block = getBlockBuilder().build(); - shared_model::crypto::Hash another_hash = shared_model::crypto::Hash(std::string(32, '1')); EXPECT_CALL(*query, getPeers()).WillOnce(Return(peers)); - EXPECT_CALL(*storage, apply(testing::Ref(block), _)) + EXPECT_CALL(*storage, apply(testing::Ref(*block), _)) .WillOnce( - InvokeArgument<1>(ByRef(block), ByRef(*query), ByRef(another_hash))); + InvokeArgument<1>(ByRef(*block), ByRef(*query), ByRef(another_hash))); - ASSERT_FALSE(validator->validateBlock(block, *storage)); + ASSERT_FALSE(validator->validateBlock(*block, *storage)); } /** @@ -115,17 +92,15 @@ TEST_F(ChainValidationTest, FailWhenDifferentPrevHash) { */ TEST_F(ChainValidationTest, FailWhenNoSupermajority) { // Valid previous hash, no supermajority, correct peers subset => invalid - auto block = getBlockBuilder().build(); - - EXPECT_CALL(*supermajority_checker, hasSupermajority(block.signatures(), _)) + EXPECT_CALL(*supermajority_checker, hasSupermajority(block->signatures(), _)) .WillOnce(Return(false)); EXPECT_CALL(*query, getPeers()).WillOnce(Return(peers)); - EXPECT_CALL(*storage, apply(testing::Ref(block), _)) - .WillOnce(InvokeArgument<1>(ByRef(block), ByRef(*query), ByRef(hash))); + EXPECT_CALL(*storage, apply(testing::Ref(*block), _)) + .WillOnce(InvokeArgument<1>(ByRef(*block), ByRef(*query), ByRef(hash))); - ASSERT_FALSE(validator->validateBlock(block, *storage)); + ASSERT_FALSE(validator->validateBlock(*block, *storage)); } /** @@ -135,24 +110,16 @@ TEST_F(ChainValidationTest, FailWhenNoSupermajority) { */ TEST_F(ChainValidationTest, ValidWhenValidateChainFromOnePeer) { // Valid previous hash, has supermajority, correct peers subset => valid - auto block = std::make_shared(getBlockBuilder().build()); - rxcpp::observable> - block_observable = rxcpp::observable<>::just(block).map([](auto &&x) { - return std::shared_ptr(x); - }); - EXPECT_CALL(*supermajority_checker, hasSupermajority(_, _)) .WillOnce(Return(true)); EXPECT_CALL(*query, getPeers()).WillOnce(Return(peers)); - EXPECT_CALL( - *storage, - apply(testing::Truly([&](const shared_model::interface::Block &rhs) { - return rhs == *block; - }), - _)) + EXPECT_CALL(*storage, apply(testing::Ref(*block), _)) .WillOnce(InvokeArgument<1>(ByRef(*block), ByRef(*query), ByRef(hash))); - ASSERT_TRUE(validator->validateChain(block_observable, *storage)); + ASSERT_TRUE(validator->validateChain( + rxcpp::observable<>::just( + std::static_pointer_cast(block)), + *storage)); } diff --git a/test/module/irohad/validation/stateful_validator_test.cpp b/test/module/irohad/validation/stateful_validator_test.cpp index 5d48328e36..2f0cf995a1 100644 --- a/test/module/irohad/validation/stateful_validator_test.cpp +++ b/test/module/irohad/validation/stateful_validator_test.cpp @@ -7,7 +7,6 @@ #include #include -#include "builders/protobuf/common_objects/proto_signature_builder.hpp" #include "common/result.hpp" #include "cryptography/crypto_provider/crypto_defaults.hpp" #include "interfaces/iroha_internal/batch_meta.hpp" @@ -15,6 +14,7 @@ #include "module/irohad/ametsuchi/ametsuchi_mocks.hpp" #include "module/shared_model/builders/protobuf/test_proposal_builder.hpp" #include "module/shared_model/builders/protobuf/test_transaction_builder.hpp" +#include "module/shared_model/interface_mocks.hpp" #include "validation/impl/stateful_validator_impl.hpp" #include "validation/stateful_validator.hpp" #include "validation/utils.hpp" @@ -32,12 +32,7 @@ using ::testing::ReturnArg; class SignaturesSubset : public testing::Test { public: - auto makeSignature(PublicKey key, std::string sign) { - return shared_model::proto::SignatureBuilder() - .publicKey(key) - .signedData(Signed(sign)) - .build(); - } + std::vector keys{PublicKey("a"), PublicKey("b"), PublicKey("c")}; }; /** @@ -46,10 +41,10 @@ class SignaturesSubset : public testing::Test { * @then returned true */ TEST_F(SignaturesSubset, Equal) { - std::vector keys{PublicKey("a"), PublicKey("b"), PublicKey("c")}; - std::vector signatures; - for (const auto &k : keys) { - signatures.push_back(makeSignature(k, "")); + std::array signatures; + for (size_t i = 0; i < signatures.size(); ++i) { + EXPECT_CALL(signatures[i], publicKey()) + .WillRepeatedly(testing::ReturnRef(keys[i])); } ASSERT_TRUE(signaturesSubset(signatures, keys)); } @@ -61,13 +56,13 @@ TEST_F(SignaturesSubset, Equal) { * @then returned false */ TEST_F(SignaturesSubset, Lesser) { - std::vector keys{PublicKey("a"), PublicKey("b")}; - std::vector signatures; - for (const auto &k : keys) { - signatures.push_back(makeSignature(k, "")); + std::vector subkeys{keys.begin(), keys.end() - 1}; + std::array signatures; + for (size_t i = 0; i < signatures.size(); ++i) { + EXPECT_CALL(signatures[i], publicKey()) + .WillRepeatedly(testing::ReturnRef(keys[i])); } - signatures.push_back(makeSignature(PublicKey("c"), "")); - ASSERT_FALSE(signaturesSubset(signatures, keys)); + ASSERT_FALSE(signaturesSubset(signatures, subkeys)); } /** @@ -76,12 +71,11 @@ TEST_F(SignaturesSubset, Lesser) { * @then returned true */ TEST_F(SignaturesSubset, StrictSubset) { - std::vector keys{PublicKey("a"), PublicKey("b")}; - std::vector signatures; - for (const auto &k : keys) { - signatures.push_back(makeSignature(k, "")); + std::array signatures; + for (size_t i = 0; i < signatures.size(); ++i) { + EXPECT_CALL(signatures[i], publicKey()) + .WillRepeatedly(testing::ReturnRef(keys[i])); } - keys.push_back(PublicKey("c")); ASSERT_TRUE(signaturesSubset(signatures, keys)); } @@ -91,11 +85,13 @@ TEST_F(SignaturesSubset, StrictSubset) { * @then returned false */ TEST_F(SignaturesSubset, PublickeyUniqueness) { - std::vector keys{PublicKey("a"), PublicKey("a")}; - std::vector signatures; - signatures.push_back(makeSignature(PublicKey("a"), "")); - signatures.push_back(makeSignature(PublicKey("c"), "")); - ASSERT_FALSE(signaturesSubset(signatures, keys)); + std::vector repeated_keys{2, keys[0]}; + std::array signatures; + for (size_t i = 0; i < signatures.size(); ++i) { + EXPECT_CALL(signatures[i], publicKey()) + .WillRepeatedly(testing::ReturnRef(keys[i])); + } + ASSERT_FALSE(signaturesSubset(signatures, repeated_keys)); } class Validator : public testing::Test { diff --git a/test/module/shared_model/interface_mocks.hpp b/test/module/shared_model/interface_mocks.hpp new file mode 100644 index 0000000000..fb8b3789a7 --- /dev/null +++ b/test/module/shared_model/interface_mocks.hpp @@ -0,0 +1,50 @@ +/** + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include "interfaces/iroha_internal/block.hpp" +#include "interfaces/transaction.hpp" + +namespace iface = shared_model::interface; + +struct BlockMock : public iface::Block { + MOCK_CONST_METHOD0(txsNumber, iface::types::TransactionsNumberType()); + MOCK_CONST_METHOD0(transactions, iface::types::TransactionsCollectionType()); + MOCK_CONST_METHOD0(height, iface::types::HeightType()); + MOCK_CONST_METHOD0(prevHash, const iface::types::HashType &()); + MOCK_CONST_METHOD0(signatures, iface::types::SignatureRangeType()); + MOCK_CONST_METHOD0(createdTime, iface::types::TimestampType()); + MOCK_CONST_METHOD0(payload, const iface::types::BlobType &()); + MOCK_CONST_METHOD0(blob, const iface::types::BlobType &()); + MOCK_METHOD2(addSignature, + bool(const shared_model::crypto::Signed &, + const shared_model::crypto::PublicKey &)); + MOCK_CONST_METHOD0(clone, BlockMock *()); +}; + +struct TransactionMock : public iface::Transaction { + MOCK_CONST_METHOD0(creatorAccountId, const iface::types::AccountIdType &()); + MOCK_CONST_METHOD0(quorum, iface::types::QuorumType()); + MOCK_CONST_METHOD0(commands, CommandsType()); + MOCK_CONST_METHOD0(reduced_payload, const iface::types::BlobType &()); + MOCK_CONST_METHOD0(batch_meta, + boost::optional>()); + MOCK_CONST_METHOD0(signatures, iface::types::SignatureRangeType()); + MOCK_CONST_METHOD0(createdTime, iface::types::TimestampType()); + MOCK_CONST_METHOD0(payload, const iface::types::BlobType &()); + MOCK_CONST_METHOD0(blob, const iface::types::BlobType &()); + MOCK_METHOD2(addSignature, + bool(const shared_model::crypto::Signed &, + const shared_model::crypto::PublicKey &)); + MOCK_CONST_METHOD0(clone, TransactionMock *()); +}; + +struct SignatureMock : public iface::Signature { + MOCK_CONST_METHOD0(publicKey, const PublicKeyType &()); + MOCK_CONST_METHOD0(signedData, const SignedType &()); + MOCK_CONST_METHOD0(clone, SignatureMock *()); +}; From 6aab2f34c97c602a1a62efbfb90dd735a7cacf00 Mon Sep 17 00:00:00 2001 From: Kitsu Date: Mon, 23 Jul 2018 12:55:19 +0300 Subject: [PATCH 62/97] Replace QPF with QueryExecution mockable module (#1579) Signed-off-by: Kitsu --- irohad/execution/CMakeLists.txt | 15 +- ...execution.cpp => query_execution_impl.cpp} | 219 +++++++----- irohad/execution/query_execution.hpp | 115 +------ irohad/execution/query_execution_impl.hpp | 143 ++++++++ irohad/main/application.cpp | 20 +- .../processor/impl/query_processor_impl.cpp | 57 ++-- irohad/torii/processor/query_processor.hpp | 30 +- .../torii/processor/query_processor_impl.hpp | 22 +- .../integration_test_framework.hpp | 17 +- test/fuzzing/find_fuzz.cpp | 8 +- test/fuzzing/status_fuzz.cpp | 4 +- test/fuzzing/torii_fuzz.cpp | 4 +- test/module/iroha-cli/client_test.cpp | 23 +- .../irohad/execution/execution_mocks.hpp | 20 ++ .../irohad/execution/query_execution_test.cpp | 28 +- test/module/irohad/model/model_mocks.hpp | 54 --- .../torii/processor/query_processor_test.cpp | 316 ++++-------------- .../irohad/torii/torii_queries_test.cpp | 26 +- 18 files changed, 454 insertions(+), 667 deletions(-) rename irohad/execution/impl/{query_execution.cpp => query_execution_impl.cpp} (68%) create mode 100644 irohad/execution/query_execution_impl.hpp create mode 100644 test/module/irohad/execution/execution_mocks.hpp delete mode 100644 test/module/irohad/model/model_mocks.hpp diff --git a/irohad/execution/CMakeLists.txt b/irohad/execution/CMakeLists.txt index 6d8db100de..2f2f45ed28 100644 --- a/irohad/execution/CMakeLists.txt +++ b/irohad/execution/CMakeLists.txt @@ -1,16 +1,7 @@ -# Copyright 2018 Soramitsu Co., Ltd. # -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at +#Copyright Soramitsu Co., Ltd. All Rights Reserved. +#SPDX-License-Identifier: Apache-2.0 # -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. add_library(common_execution impl/common_executor.cpp @@ -34,7 +25,7 @@ target_link_libraries(command_execution ) add_library(query_execution - impl/query_execution.cpp + impl/query_execution_impl.cpp ) target_link_libraries(query_execution diff --git a/irohad/execution/impl/query_execution.cpp b/irohad/execution/impl/query_execution_impl.cpp similarity index 68% rename from irohad/execution/impl/query_execution.cpp rename to irohad/execution/impl/query_execution_impl.cpp index fa45ab2855..ef0991e81e 100644 --- a/irohad/execution/impl/query_execution.cpp +++ b/irohad/execution/impl/query_execution_impl.cpp @@ -3,22 +3,24 @@ * SPDX-License-Identifier: Apache-2.0 */ -#include "execution/query_execution.hpp" +#include "execution/query_execution_impl.hpp" #include -#include "backend/protobuf/permissions.hpp" +#include "builders/protobuf/builder_templates/blocks_query_template.hpp" #include "execution/common_executor.hpp" +#include "interfaces/permissions.hpp" +#include "interfaces/queries/blocks_query.hpp" +#include "interfaces/queries/query.hpp" +#include "interfaces/query_responses/query_response.hpp" using namespace shared_model::interface::permissions; -using namespace shared_model::proto::permissions; using namespace iroha; using namespace iroha::ametsuchi; -QueryProcessingFactory::QueryProcessingFactory( - std::shared_ptr wsvQuery, - std::shared_ptr blockQuery) - : _wsvQuery(std::move(wsvQuery)), _blockQuery(std::move(blockQuery)) {} +QueryExecutionImpl::QueryExecutionImpl( + std::shared_ptr storage) + : storage_(storage) {} std::string getDomainFromName(const std::string &account_id) { std::vector res; @@ -69,113 +71,125 @@ static bool hasQueryPermission(const std::string &creator, and set.test(domain_permission_id)); } -bool QueryProcessingFactory::validate( +bool QueryExecutionImpl::validate( const shared_model::interface::BlocksQuery &query) { return checkAccountRolePermission( - query.creatorAccountId(), *_wsvQuery, Role::kGetBlocks); + query.creatorAccountId(), *storage_->getWsvQuery(), Role::kGetBlocks); } -bool QueryProcessingFactory::validate( +bool QueryExecutionImpl::validate( + ametsuchi::WsvQuery &wq, const shared_model::interface::Query &query, const shared_model::interface::GetAssetInfo &get_asset_info) { return checkAccountRolePermission( - query.creatorAccountId(), *_wsvQuery, Role::kReadAssets); + query.creatorAccountId(), wq, Role::kReadAssets); } -bool QueryProcessingFactory::validate( +bool QueryExecutionImpl::validate( + ametsuchi::WsvQuery &wq, const shared_model::interface::Query &query, const shared_model::interface::GetRoles &get_roles) { return checkAccountRolePermission( - query.creatorAccountId(), *_wsvQuery, Role::kGetRoles); + query.creatorAccountId(), wq, Role::kGetRoles); } -bool QueryProcessingFactory::validate( +bool QueryExecutionImpl::validate( + ametsuchi::WsvQuery &wq, const shared_model::interface::Query &query, const shared_model::interface::GetRolePermissions &get_role_permissions) { return checkAccountRolePermission( - query.creatorAccountId(), *_wsvQuery, Role::kGetRoles); + query.creatorAccountId(), wq, Role::kGetRoles); } -bool QueryProcessingFactory::validate( +bool QueryExecutionImpl::validate( + ametsuchi::WsvQuery &wq, const shared_model::interface::Query &query, const shared_model::interface::GetAccount &get_account) { return hasQueryPermission(query.creatorAccountId(), get_account.accountId(), - *_wsvQuery, + wq, Role::kGetMyAccount, Role::kGetAllAccounts, Role::kGetDomainAccounts); } -bool QueryProcessingFactory::validate( +bool QueryExecutionImpl::validate( + ametsuchi::WsvQuery &wq, const shared_model::interface::Query &query, const shared_model::interface::GetSignatories &get_signatories) { return hasQueryPermission(query.creatorAccountId(), get_signatories.accountId(), - *_wsvQuery, + wq, Role::kGetMySignatories, Role::kGetAllSignatories, Role::kGetDomainSignatories); } -bool QueryProcessingFactory::validate( +bool QueryExecutionImpl::validate( + ametsuchi::WsvQuery &wq, const shared_model::interface::Query &query, const shared_model::interface::GetAccountAssets &get_account_assets) { return hasQueryPermission(query.creatorAccountId(), get_account_assets.accountId(), - *_wsvQuery, + wq, Role::kGetMyAccAst, Role::kGetAllAccAst, Role::kGetDomainAccAst); } -bool QueryProcessingFactory::validate( +bool QueryExecutionImpl::validate( + ametsuchi::WsvQuery &wq, const shared_model::interface::Query &query, const shared_model::interface::GetAccountDetail &get_account_detail) { return hasQueryPermission(query.creatorAccountId(), get_account_detail.accountId(), - *_wsvQuery, + wq, Role::kGetMyAccDetail, Role::kGetAllAccDetail, Role::kGetDomainAccDetail); } -bool QueryProcessingFactory::validate( +bool QueryExecutionImpl::validate( + ametsuchi::WsvQuery &wq, const shared_model::interface::Query &query, const shared_model::interface::GetAccountTransactions &get_account_transactions) { return hasQueryPermission(query.creatorAccountId(), get_account_transactions.accountId(), - *_wsvQuery, + wq, Role::kGetMyAccTxs, Role::kGetAllAccTxs, Role::kGetDomainAccTxs); } -bool QueryProcessingFactory::validate( +bool QueryExecutionImpl::validate( + ametsuchi::WsvQuery &wq, const shared_model::interface::Query &query, const shared_model::interface::GetAccountAssetTransactions &get_account_asset_transactions) { return hasQueryPermission(query.creatorAccountId(), get_account_asset_transactions.accountId(), - *_wsvQuery, + wq, Role::kGetMyAccAstTxs, Role::kGetAllAccAstTxs, Role::kGetDomainAccAstTxs); } -bool QueryProcessingFactory::validate( +bool QueryExecutionImpl::validate( + ametsuchi::WsvQuery &wq, const shared_model::interface::Query &query, const shared_model::interface::GetTransactions &get_transactions) { return checkAccountRolePermission( - query.creatorAccountId(), *_wsvQuery, Role::kGetMyTxs) + query.creatorAccountId(), wq, Role::kGetMyTxs) or checkAccountRolePermission( - query.creatorAccountId(), *_wsvQuery, Role::kGetAllTxs); + query.creatorAccountId(), wq, Role::kGetAllTxs); } -QueryProcessingFactory::QueryResponseBuilderDone -QueryProcessingFactory::executeGetAssetInfo( +QueryExecutionImpl::QueryResponseBuilderDone +QueryExecutionImpl::executeGetAssetInfo( + ametsuchi::WsvQuery &wq, + ametsuchi::BlockQuery &, const shared_model::interface::GetAssetInfo &query) { - auto ast = _wsvQuery->getAsset(query.assetId()); + auto ast = wq.getAsset(query.assetId()); if (not ast) { return buildError(); @@ -187,10 +201,12 @@ QueryProcessingFactory::executeGetAssetInfo( return response; } -QueryProcessingFactory::QueryResponseBuilderDone -QueryProcessingFactory::executeGetRoles( +QueryExecutionImpl::QueryResponseBuilderDone +QueryExecutionImpl::executeGetRoles( + ametsuchi::WsvQuery &wq, + ametsuchi::BlockQuery &, const shared_model::interface::GetRoles &queryQueryResponseBuilder) { - auto roles = _wsvQuery->getRoles(); + auto roles = wq.getRoles(); if (not roles) { return buildError(); } @@ -198,10 +214,12 @@ QueryProcessingFactory::executeGetRoles( return response; } -QueryProcessingFactory::QueryResponseBuilderDone -QueryProcessingFactory::executeGetRolePermissions( +QueryExecutionImpl::QueryResponseBuilderDone +QueryExecutionImpl::executeGetRolePermissions( + ametsuchi::WsvQuery &wq, + ametsuchi::BlockQuery &, const shared_model::interface::GetRolePermissions &query) { - auto perm = _wsvQuery->getRolePermissions(query.roleId()); + auto perm = wq.getRolePermissions(query.roleId()); if (not perm) { return buildError(); } @@ -210,12 +228,14 @@ QueryProcessingFactory::executeGetRolePermissions( return response; } -QueryProcessingFactory::QueryResponseBuilderDone -QueryProcessingFactory::executeGetAccount( +QueryExecutionImpl::QueryResponseBuilderDone +QueryExecutionImpl::executeGetAccount( + ametsuchi::WsvQuery &wq, + ametsuchi::BlockQuery &, const shared_model::interface::GetAccount &query) { - auto acc = _wsvQuery->getAccount(query.accountId()); + auto acc = wq.getAccount(query.accountId()); - auto roles = _wsvQuery->getAccountRoles(query.accountId()); + auto roles = wq.getAccountRoles(query.accountId()); if (not acc or not roles) { return buildError(); } @@ -225,10 +245,12 @@ QueryProcessingFactory::executeGetAccount( return response; } -QueryProcessingFactory::QueryResponseBuilderDone -QueryProcessingFactory::executeGetAccountAssets( +QueryExecutionImpl::QueryResponseBuilderDone +QueryExecutionImpl::executeGetAccountAssets( + ametsuchi::WsvQuery &wq, + ametsuchi::BlockQuery &, const shared_model::interface::GetAccountAssets &query) { - auto acct_assets = _wsvQuery->getAccountAssets(query.accountId()); + auto acct_assets = wq.getAccountAssets(query.accountId()); if (not acct_assets) { return buildError(); @@ -244,13 +266,14 @@ QueryProcessingFactory::executeGetAccountAssets( return response; } -QueryProcessingFactory::QueryResponseBuilderDone -QueryProcessingFactory::executeGetAccountDetail( +QueryExecutionImpl::QueryResponseBuilderDone +QueryExecutionImpl::executeGetAccountDetail( + ametsuchi::WsvQuery &wq, + ametsuchi::BlockQuery &, const shared_model::interface::GetAccountDetail &query) { - auto acct_detail = - _wsvQuery->getAccountDetail(query.accountId(), - query.key() ? *query.key() : "", - query.writer() ? *query.writer() : ""); + auto acct_detail = wq.getAccountDetail(query.accountId(), + query.key() ? *query.key() : "", + query.writer() ? *query.writer() : ""); if (not acct_detail) { return buildError(); } @@ -258,11 +281,13 @@ QueryProcessingFactory::executeGetAccountDetail( return response; } -QueryProcessingFactory::QueryResponseBuilderDone -QueryProcessingFactory::executeGetAccountAssetTransactions( +QueryExecutionImpl::QueryResponseBuilderDone +QueryExecutionImpl::executeGetAccountAssetTransactions( + ametsuchi::WsvQuery &, + ametsuchi::BlockQuery &bq, const shared_model::interface::GetAccountAssetTransactions &query) { - auto acc_asset_tx = _blockQuery->getAccountAssetTransactions( - query.accountId(), query.assetId()); + auto acc_asset_tx = + bq.getAccountAssetTransactions(query.accountId(), query.assetId()); std::vector txs; acc_asset_tx.subscribe([&](const auto &tx) { @@ -274,10 +299,12 @@ QueryProcessingFactory::executeGetAccountAssetTransactions( return response; } -QueryProcessingFactory::QueryResponseBuilderDone -QueryProcessingFactory::executeGetAccountTransactions( +QueryExecutionImpl::QueryResponseBuilderDone +QueryExecutionImpl::executeGetAccountTransactions( + ametsuchi::WsvQuery &, + ametsuchi::BlockQuery &bq, const shared_model::interface::GetAccountTransactions &query) { - auto acc_tx = _blockQuery->getAccountTransactions(query.accountId()); + auto acc_tx = bq.getAccountTransactions(query.accountId()); std::vector txs; acc_tx.subscribe([&](const auto &tx) { @@ -289,17 +316,19 @@ QueryProcessingFactory::executeGetAccountTransactions( return response; } -QueryProcessingFactory::QueryResponseBuilderDone -QueryProcessingFactory::executeGetTransactions( +QueryExecutionImpl::QueryResponseBuilderDone +QueryExecutionImpl::executeGetTransactions( + ametsuchi::WsvQuery &wq, + ametsuchi::BlockQuery &bq, const shared_model::interface::GetTransactions &q, const shared_model::interface::types::AccountIdType &accountId) { const std::vector &hashes = q.transactionHashes(); - auto transactions = _blockQuery->getTransactions(hashes); + auto transactions = bq.getTransactions(hashes); std::vector txs; bool can_get_all = - checkAccountRolePermission(accountId, *_wsvQuery, Role::kGetAllTxs); + checkAccountRolePermission(accountId, wq, Role::kGetAllTxs); transactions.subscribe([&](const auto &tx) { if (tx) { auto proto_tx = @@ -313,10 +342,12 @@ QueryProcessingFactory::executeGetTransactions( return response; } -QueryProcessingFactory::QueryResponseBuilderDone -QueryProcessingFactory::executeGetSignatories( +QueryExecutionImpl::QueryResponseBuilderDone +QueryExecutionImpl::executeGetSignatories( + ametsuchi::WsvQuery &wq, + ametsuchi::BlockQuery &, const shared_model::interface::GetSignatories &query) { - auto signs = _wsvQuery->getSignatories(query.accountId()); + auto signs = wq.getSignatories(query.accountId()); if (not signs) { return buildError(); } @@ -324,8 +355,10 @@ QueryProcessingFactory::executeGetSignatories( return response; } -QueryProcessingFactory::QueryResponseBuilderDone -QueryProcessingFactory::executeGetPendingTransactions( +QueryExecutionImpl::QueryResponseBuilderDone +QueryExecutionImpl::executeGetPendingTransactions( + ametsuchi::WsvQuery &, + ametsuchi::BlockQuery &, const shared_model::interface::GetPendingTransactions &query, const shared_model::interface::types::AccountIdType &query_creator) { std::vector txs; @@ -334,97 +367,101 @@ QueryProcessingFactory::executeGetPendingTransactions( return response; } -std::shared_ptr -QueryProcessingFactory::validateAndExecute( +std::unique_ptr +QueryExecutionImpl::validateAndExecute( const shared_model::interface::Query &query) { const auto &query_hash = query.hash(); QueryResponseBuilderDone builder; + auto wq = storage_->getWsvQuery(); + auto bq = storage_->getBlockQuery(); // TODO: 29/04/2018 x3medima18, Add visitor class, IR-1185 return visit_in_place( query.get(), [&](const shared_model::interface::GetAccount &q) { - if (not validate(query, q)) { + if (not validate(*wq, query, q)) { builder = statefulFailed(); } else { - builder = executeGetAccount(q); + builder = executeGetAccount(*wq, *bq, q); } return clone(builder.queryHash(query_hash).build()); }, [&](const shared_model::interface::GetSignatories &q) { - if (not validate(query, q)) { + if (not validate(*wq, query, q)) { builder = statefulFailed(); } else { - builder = executeGetSignatories(q); + builder = executeGetSignatories(*wq, *bq, q); } return clone(builder.queryHash(query_hash).build()); }, [&](const shared_model::interface::GetAccountTransactions &q) { - if (not validate(query, q)) { + if (not validate(*wq, query, q)) { builder = statefulFailed(); } else { - builder = executeGetAccountTransactions(q); + builder = executeGetAccountTransactions(*wq, *bq, q); } return clone(builder.queryHash(query_hash).build()); }, [&](const shared_model::interface::GetTransactions &q) { - if (not validate(query, q)) { + if (not validate(*wq, query, q)) { builder = statefulFailed(); } else { - builder = executeGetTransactions(q, query.creatorAccountId()); + builder = + executeGetTransactions(*wq, *bq, q, query.creatorAccountId()); } return clone(builder.queryHash(query_hash).build()); }, [&](const shared_model::interface::GetAccountAssetTransactions &q) { - if (not validate(query, q)) { + if (not validate(*wq, query, q)) { builder = statefulFailed(); } else { - builder = executeGetAccountAssetTransactions(q); + builder = executeGetAccountAssetTransactions(*wq, *bq, q); } return clone(builder.queryHash(query_hash).build()); }, [&](const shared_model::interface::GetAccountAssets &q) { - if (not validate(query, q)) { + if (not validate(*wq, query, q)) { builder = statefulFailed(); } else { - builder = executeGetAccountAssets(q); + builder = executeGetAccountAssets(*wq, *bq, q); } return clone(builder.queryHash(query_hash).build()); }, [&](const shared_model::interface::GetAccountDetail &q) { - if (not validate(query, q)) { + if (not validate(*wq, query, q)) { builder = statefulFailed(); } else { - builder = executeGetAccountDetail(q); + builder = executeGetAccountDetail(*wq, *bq, q); } return clone(builder.queryHash(query_hash).build()); }, [&](const shared_model::interface::GetRoles &q) { - if (not validate(query, q)) { + if (not validate(*wq, query, q)) { builder = statefulFailed(); } else { - builder = executeGetRoles(q); + builder = executeGetRoles(*wq, *bq, q); } return clone(builder.queryHash(query_hash).build()); }, [&](const shared_model::interface::GetRolePermissions &q) { - if (not validate(query, q)) { + if (not validate(*wq, query, q)) { builder = statefulFailed(); } else { - builder = executeGetRolePermissions(q); + builder = executeGetRolePermissions(*wq, *bq, q); } return clone(builder.queryHash(query_hash).build()); }, [&](const shared_model::interface::GetAssetInfo &q) { - if (not validate(query, q)) { + if (not validate(*wq, query, q)) { builder = statefulFailed(); } else { - builder = executeGetAssetInfo(q); + builder = executeGetAssetInfo(*wq, *bq, q); } return clone(builder.queryHash(query_hash).build()); }, [&](const shared_model::interface::GetPendingTransactions &q) { // the query does not require validation - builder = executeGetPendingTransactions(q, query.creatorAccountId()); + builder = executeGetPendingTransactions( + *wq, *bq, q, query.creatorAccountId()); return clone(builder.queryHash(query_hash).build()); } diff --git a/irohad/execution/query_execution.hpp b/irohad/execution/query_execution.hpp index 8da803ef92..62921be970 100644 --- a/irohad/execution/query_execution.hpp +++ b/irohad/execution/query_execution.hpp @@ -6,127 +6,38 @@ #ifndef IROHA_QUERY_EXECUTION_HPP #define IROHA_QUERY_EXECUTION_HPP -#include "ametsuchi/block_query.hpp" -#include "ametsuchi/wsv_query.hpp" -#include "builders/protobuf/builder_templates/blocks_query_template.hpp" -#include "builders/protobuf/builder_templates/query_response_template.hpp" -#include "interfaces/common_objects/types.hpp" +#include namespace shared_model { namespace interface { - class QueryResponse; class Query; + class BlocksQuery; + class QueryResponse; } // namespace interface } // namespace shared_model namespace iroha { - /** - * Converting model objects to protobuf and vice versa - */ - class QueryProcessingFactory { - using QueryResponseBuilder = - shared_model::proto::TemplateQueryResponseBuilder<0>; - - using QueryResponseBuilderDone = - shared_model::proto::TemplateQueryResponseBuilder<1>; - + class QueryExecution { public: + virtual ~QueryExecution() = default; /** * Execute and validate query. * * @param query - * @return shared pointer to query response + * @return query response */ - std::shared_ptr validateAndExecute( - const shared_model::interface::Query &query); + virtual std::unique_ptr + validateAndExecute(const shared_model::interface::Query &query) = 0; /** - * - * @param wsvQuery - * @param blockQuery + * Perform BlocksQuery validation + * @param query to validate + * @return true if valid, false otherwise */ - QueryProcessingFactory(std::shared_ptr wsvQuery, - std::shared_ptr blockQuery); - bool validate(const shared_model::interface::BlocksQuery &query); - - private: - bool validate(const shared_model::interface::Query &query, - const shared_model::interface::GetAssetInfo &get_asset_info); - - bool validate(const shared_model::interface::Query &query, - const shared_model::interface::GetRoles &get_roles); - - bool validate(const shared_model::interface::Query &query, - const shared_model::interface::GetRolePermissions - &get_role_permissions); - - bool validate( - const shared_model::interface::Query &query, - const shared_model::interface::GetAccountAssets &get_account_assets); - - bool validate(const shared_model::interface::Query &query, - const shared_model::interface::GetAccount &get_account); - - bool validate( - const shared_model::interface::Query &query, - const shared_model::interface::GetSignatories &get_signatories); - - bool validate(const shared_model::interface::Query &query, - const shared_model::interface::GetAccountTransactions - &get_account_transactions); - - bool validate(const shared_model::interface::Query &query, - const shared_model::interface::GetAccountAssetTransactions - &get_account_asset_transactions); - - bool validate( - const shared_model::interface::Query &query, - const shared_model::interface::GetAccountDetail &get_account_detail); - - bool validate( - const shared_model::interface::Query &query, - const shared_model::interface::GetTransactions &get_transactions); - - QueryResponseBuilderDone executeGetAssetInfo( - const shared_model::interface::GetAssetInfo &get_asset_info); - - QueryResponseBuilderDone executeGetRoles( - const shared_model::interface::GetRoles &query); - - QueryResponseBuilderDone executeGetRolePermissions( - const shared_model::interface::GetRolePermissions &query); - - QueryResponseBuilderDone executeGetAccountAssets( - const shared_model::interface::GetAccountAssets &query); - - QueryResponseBuilderDone executeGetAccountDetail( - const shared_model::interface::GetAccountDetail &query); - - QueryResponseBuilderDone executeGetAccount( - const shared_model::interface::GetAccount &query); - - QueryResponseBuilderDone executeGetSignatories( - const shared_model::interface::GetSignatories &query); - - QueryResponseBuilderDone executeGetAccountAssetTransactions( - const shared_model::interface::GetAccountAssetTransactions &query); - - QueryResponseBuilderDone executeGetAccountTransactions( - const shared_model::interface::GetAccountTransactions &query); - - QueryResponseBuilderDone executeGetTransactions( - const shared_model::interface::GetTransactions &query, - const shared_model::interface::types::AccountIdType &accountId); - - QueryResponseBuilderDone executeGetPendingTransactions( - const shared_model::interface::GetPendingTransactions &query, - const shared_model::interface::types::AccountIdType &query_creator); - - std::shared_ptr _wsvQuery; - std::shared_ptr _blockQuery; + virtual bool validate( + const shared_model::interface::BlocksQuery &query) = 0; }; - } // namespace iroha #endif // IROHA_QUERY_EXECUTION_HPP diff --git a/irohad/execution/query_execution_impl.hpp b/irohad/execution/query_execution_impl.hpp new file mode 100644 index 0000000000..e556a2811a --- /dev/null +++ b/irohad/execution/query_execution_impl.hpp @@ -0,0 +1,143 @@ +/** + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef IROHA_QUERY_EXECUTION_IMPL_HPP +#define IROHA_QUERY_EXECUTION_IMPL_HPP + +#include "execution/query_execution.hpp" + +#include "ametsuchi/block_query.hpp" +#include "ametsuchi/storage.hpp" +#include "ametsuchi/wsv_query.hpp" +#include "builders/protobuf/builder_templates/query_response_template.hpp" +#include "interfaces/common_objects/types.hpp" + +namespace iroha { + + class QueryExecutionImpl : public QueryExecution { + using QueryResponseBuilder = + shared_model::proto::TemplateQueryResponseBuilder<0>; + + using QueryResponseBuilderDone = + shared_model::proto::TemplateQueryResponseBuilder<1>; + + public: + explicit QueryExecutionImpl(std::shared_ptr storage); + + std::unique_ptr validateAndExecute( + const shared_model::interface::Query &query) override; + bool validate(const shared_model::interface::BlocksQuery &query) override; + + private: + bool validate(ametsuchi::WsvQuery &wq, + const shared_model::interface::Query &query, + const shared_model::interface::GetAssetInfo &get_asset_info); + + bool validate(ametsuchi::WsvQuery &wq, + const shared_model::interface::Query &query, + const shared_model::interface::GetRoles &get_roles); + + bool validate(ametsuchi::WsvQuery &wq, + const shared_model::interface::Query &query, + const shared_model::interface::GetRolePermissions + &get_role_permissions); + + bool validate( + ametsuchi::WsvQuery &wq, + const shared_model::interface::Query &query, + const shared_model::interface::GetAccountAssets &get_account_assets); + + bool validate(ametsuchi::WsvQuery &wq, + const shared_model::interface::Query &query, + const shared_model::interface::GetAccount &get_account); + + bool validate( + ametsuchi::WsvQuery &wq, + const shared_model::interface::Query &query, + const shared_model::interface::GetSignatories &get_signatories); + + bool validate(ametsuchi::WsvQuery &wq, + const shared_model::interface::Query &query, + const shared_model::interface::GetAccountTransactions + &get_account_transactions); + + bool validate(ametsuchi::WsvQuery &wq, + const shared_model::interface::Query &query, + const shared_model::interface::GetAccountAssetTransactions + &get_account_asset_transactions); + + bool validate( + ametsuchi::WsvQuery &wq, + const shared_model::interface::Query &query, + const shared_model::interface::GetAccountDetail &get_account_detail); + + bool validate( + ametsuchi::WsvQuery &wq, + const shared_model::interface::Query &query, + const shared_model::interface::GetTransactions &get_transactions); + + QueryResponseBuilderDone executeGetAssetInfo( + ametsuchi::WsvQuery &wq, + ametsuchi::BlockQuery &bq, + const shared_model::interface::GetAssetInfo &get_asset_info); + + QueryResponseBuilderDone executeGetRoles( + ametsuchi::WsvQuery &wq, + ametsuchi::BlockQuery &bq, + const shared_model::interface::GetRoles &query); + + QueryResponseBuilderDone executeGetRolePermissions( + ametsuchi::WsvQuery &wq, + ametsuchi::BlockQuery &bq, + const shared_model::interface::GetRolePermissions &query); + + QueryResponseBuilderDone executeGetAccountAssets( + ametsuchi::WsvQuery &wq, + ametsuchi::BlockQuery &bq, + const shared_model::interface::GetAccountAssets &query); + + QueryResponseBuilderDone executeGetAccountDetail( + ametsuchi::WsvQuery &wq, + ametsuchi::BlockQuery &bq, + const shared_model::interface::GetAccountDetail &query); + + QueryResponseBuilderDone executeGetAccount( + ametsuchi::WsvQuery &wq, + ametsuchi::BlockQuery &bq, + const shared_model::interface::GetAccount &query); + + QueryResponseBuilderDone executeGetSignatories( + ametsuchi::WsvQuery &wq, + ametsuchi::BlockQuery &bq, + const shared_model::interface::GetSignatories &query); + + QueryResponseBuilderDone executeGetAccountAssetTransactions( + ametsuchi::WsvQuery &wq, + ametsuchi::BlockQuery &bq, + const shared_model::interface::GetAccountAssetTransactions &query); + + QueryResponseBuilderDone executeGetAccountTransactions( + ametsuchi::WsvQuery &wq, + ametsuchi::BlockQuery &bq, + const shared_model::interface::GetAccountTransactions &query); + + QueryResponseBuilderDone executeGetTransactions( + ametsuchi::WsvQuery &wq, + ametsuchi::BlockQuery &bq, + const shared_model::interface::GetTransactions &query, + const shared_model::interface::types::AccountIdType &accountId); + + QueryResponseBuilderDone executeGetPendingTransactions( + ametsuchi::WsvQuery &wq, + ametsuchi::BlockQuery &bq, + const shared_model::interface::GetPendingTransactions &query, + const shared_model::interface::types::AccountIdType &query_creator); + + std::shared_ptr storage_; + }; + +} // namespace iroha + +#endif // IROHA_QUERY_EXECUTION_IMPL_HPP diff --git a/irohad/main/application.cpp b/irohad/main/application.cpp index 7fffca37cc..25cab42a8c 100644 --- a/irohad/main/application.cpp +++ b/irohad/main/application.cpp @@ -1,18 +1,6 @@ /** - * Copyright Soramitsu Co., Ltd. 2017 All Rights Reserved. - * http://soramitsu.co.jp - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 */ #include "main/application.hpp" @@ -20,6 +8,7 @@ #include "ametsuchi/impl/wsv_restorer_impl.hpp" #include "backend/protobuf/common_objects/proto_common_objects_factory.hpp" #include "consensus/yac/impl/supermajority_checker_impl.hpp" +#include "execution/query_execution_impl.hpp" #include "multi_sig_transactions/gossip_propagation_strategy.hpp" #include "multi_sig_transactions/mst_processor_impl.hpp" #include "multi_sig_transactions/mst_processor_stub.hpp" @@ -282,7 +271,8 @@ void Irohad::initTransactionCommandService() { * Initializing query command service */ void Irohad::initQueryService() { - auto query_processor = std::make_shared(storage); + auto query_processor = std::make_shared( + storage, std::make_unique(storage)); query_service = std::make_shared<::torii::QueryService>(query_processor); diff --git a/irohad/torii/processor/impl/query_processor_impl.cpp b/irohad/torii/processor/impl/query_processor_impl.cpp index 9ff16695c2..419afadcab 100644 --- a/irohad/torii/processor/impl/query_processor_impl.cpp +++ b/irohad/torii/processor/impl/query_processor_impl.cpp @@ -1,23 +1,19 @@ /** - * Copyright Soramitsu Co., Ltd. 2017 All Rights Reserved. - * http://soramitsu.co.jp - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 */ #include "torii/processor/query_processor_impl.hpp" -#include "boost/range/size.hpp" + +#include + +#include "ametsuchi/wsv_query.hpp" +#include "builders/protobuf/builder_templates/query_response_template.hpp" #include "builders/protobuf/query_responses/proto_block_query_response_builder.hpp" +#include "interfaces/queries/blocks_query.hpp" +#include "interfaces/queries/query.hpp" +#include "interfaces/query_responses/block_query_response.hpp" +#include "interfaces/query_responses/query_response.hpp" #include "validation/utils.hpp" namespace iroha { @@ -51,12 +47,13 @@ namespace iroha { } QueryProcessorImpl::QueryProcessorImpl( - std::shared_ptr storage) - : storage_(storage) { + std::shared_ptr storage, + std::shared_ptr qry_exec) + : storage_(storage), qry_exec_(qry_exec) { storage_->on_commit().subscribe( [this](std::shared_ptr block) { auto response = buildBlocksQueryBlock(*block); - blocksQuerySubject_.get_subscriber().on_next(response); + blocks_query_subject_.get_subscriber().on_next(response); }); } template @@ -84,14 +81,7 @@ namespace iroha { return buildStatefulError(qry.hash()); } - const auto &wsv_query = storage_->getWsvQuery(); - auto qpf = QueryProcessingFactory(wsv_query, storage_->getBlockQuery()); - auto qpf_response = qpf.validateAndExecute(qry); - auto qry_resp = - std::static_pointer_cast( - qpf_response); - return std::make_unique( - qry_resp->getTransport()); + return qry_exec_->validateAndExecute(qry); } rxcpp::observable< @@ -100,19 +90,14 @@ namespace iroha { const shared_model::interface::BlocksQuery &qry) { if (not checkSignatories(qry)) { auto response = buildBlocksQueryError("Wrong signatories"); - return rxcpp::observable<>::just( - std::shared_ptr( - response)); + return rxcpp::observable<>::just(response); } - const auto &wsv_query = storage_->getWsvQuery(); - auto qpf = QueryProcessingFactory(wsv_query, storage_->getBlockQuery()); - if (not qpf.validate(qry)) { + + if (not qry_exec_->validate(qry)) { auto response = buildBlocksQueryError("Stateful invalid"); - return rxcpp::observable<>::just( - std::shared_ptr( - response)); + return rxcpp::observable<>::just(response); } - return blocksQuerySubject_.get_observable(); + return blocks_query_subject_.get_observable(); } } // namespace torii diff --git a/irohad/torii/processor/query_processor.hpp b/irohad/torii/processor/query_processor.hpp index fabcab71d7..a19cf56288 100644 --- a/irohad/torii/processor/query_processor.hpp +++ b/irohad/torii/processor/query_processor.hpp @@ -1,18 +1,6 @@ /** - * Copyright Soramitsu Co., Ltd. 2017 All Rights Reserved. - * http://soramitsu.co.jp - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 */ #ifndef IROHA_QUERY_PROCESSOR_HPP @@ -20,10 +8,16 @@ #include -#include "interfaces/queries/blocks_query.hpp" -#include "interfaces/queries/query.hpp" -#include "interfaces/query_responses/block_query_response.hpp" -#include "interfaces/query_responses/query_response.hpp" +#include + +namespace shared_model { + namespace interface { + class Query; + class BlocksQuery; + class QueryResponse; + class BlockQueryResponse; + } // namespace interface +} // namespace shared_model namespace iroha { namespace torii { diff --git a/irohad/torii/processor/query_processor_impl.hpp b/irohad/torii/processor/query_processor_impl.hpp index ba290b7520..d98fe7791d 100644 --- a/irohad/torii/processor/query_processor_impl.hpp +++ b/irohad/torii/processor/query_processor_impl.hpp @@ -1,18 +1,6 @@ /** - * Copyright Soramitsu Co., Ltd. 2017 All Rights Reserved. - * http://soramitsu.co.jp - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 */ #ifndef IROHA_QUERY_PROCESSOR_IMPL_HPP @@ -30,7 +18,8 @@ namespace iroha { */ class QueryProcessorImpl : public QueryProcessor { public: - explicit QueryProcessorImpl(std::shared_ptr storage); + QueryProcessorImpl(std::shared_ptr storage, + std::shared_ptr qry_exec); /** * Checks if query has needed signatures @@ -51,8 +40,9 @@ namespace iroha { private: rxcpp::subjects::subject< std::shared_ptr> - blocksQuerySubject_; + blocks_query_subject_; std::shared_ptr storage_; + std::shared_ptr qry_exec_; }; } // namespace torii } // namespace iroha diff --git a/test/framework/integration_framework/integration_test_framework.hpp b/test/framework/integration_framework/integration_test_framework.hpp index 6f230b37ac..7bd8abac3e 100644 --- a/test/framework/integration_framework/integration_test_framework.hpp +++ b/test/framework/integration_framework/integration_test_framework.hpp @@ -1,18 +1,6 @@ /** - * Copyright Soramitsu Co., Ltd. 2018 All Rights Reserved. - * http://soramitsu.co.jp - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 */ #ifndef IROHA_INTEGRATION_FRAMEWORK_HPP @@ -29,6 +17,7 @@ #include #include +#include "backend/protobuf/query_responses/proto_query_response.hpp" #include "framework/integration_framework/iroha_instance.hpp" #include "framework/integration_framework/test_irohad.hpp" #include "logger/logger.hpp" diff --git a/test/fuzzing/find_fuzz.cpp b/test/fuzzing/find_fuzz.cpp index b4933733c3..3c2c71ce63 100644 --- a/test/fuzzing/find_fuzz.cpp +++ b/test/fuzzing/find_fuzz.cpp @@ -7,6 +7,7 @@ #include #include "libfuzzer/libfuzzer_macro.h" #include "module/irohad/ametsuchi/ametsuchi_mocks.hpp" +#include "module/irohad/execution/execution_mocks.hpp" #include "module/irohad/network/network_mocks.hpp" #include "torii/processor/query_processor_impl.hpp" #include "torii/query_service.hpp" @@ -18,6 +19,7 @@ using testing::Return; struct QueryFixture { std::shared_ptr service_; std::shared_ptr qry_processor_; + std::shared_ptr qry_exec_; std::shared_ptr storage_; std::shared_ptr bq_; std::shared_ptr wq_; @@ -28,8 +30,9 @@ struct QueryFixture { wq_ = std::make_shared(); EXPECT_CALL(*storage_, getBlockQuery()).WillRepeatedly(Return(bq_)); EXPECT_CALL(*storage_, getWsvQuery()).WillRepeatedly(Return(wq_)); + qry_exec_ = std::make_shared(); qry_processor_ = - std::make_shared(storage_); + std::make_shared(storage_, qry_exec_); service_ = std::make_shared(qry_processor_); } }; @@ -39,6 +42,9 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, std::size_t size) { if (size < 1) { return 0; } + + EXPECT_CALL(*handler.qry_exec_, validateAndExecute(_)) + .WillRepeatedly(testing::Invoke([](auto &) { return nullptr; })); iroha::protocol::Query qry; if (protobuf_mutator::libfuzzer::LoadProtoInput(true, data, size, &qry)) { iroha::protocol::QueryResponse resp; diff --git a/test/fuzzing/status_fuzz.cpp b/test/fuzzing/status_fuzz.cpp index 5251e90304..3feec01488 100644 --- a/test/fuzzing/status_fuzz.cpp +++ b/test/fuzzing/status_fuzz.cpp @@ -52,8 +52,8 @@ struct CommandFixture { EXPECT_CALL(*storage_, getBlockQuery()).WillRepeatedly(Return(bq_)); tx_processor_ = std::make_shared( pcs_, mst_processor_); - service_ = - std::make_shared(tx_processor_, storage_, 15s); + service_ = std::make_shared( + tx_processor_, storage_, 15s, 15s); } }; diff --git a/test/fuzzing/torii_fuzz.cpp b/test/fuzzing/torii_fuzz.cpp index 2cfb67b537..e6e86b3539 100644 --- a/test/fuzzing/torii_fuzz.cpp +++ b/test/fuzzing/torii_fuzz.cpp @@ -46,8 +46,8 @@ struct CommandFixture { tx_processor_ = std::make_shared( pcs_, mst_processor_); - service_ = - std::make_shared(tx_processor_, nullptr, 15s); + service_ = std::make_shared( + tx_processor_, nullptr, 15s, 15s); } }; diff --git a/test/module/iroha-cli/client_test.cpp b/test/module/iroha-cli/client_test.cpp index 90d289c247..fe522e5902 100644 --- a/test/module/iroha-cli/client_test.cpp +++ b/test/module/iroha-cli/client_test.cpp @@ -1,18 +1,6 @@ /** - * Copyright Soramitsu Co., Ltd. 2017 All Rights Reserved. - * http://soramitsu.co.jp - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 */ #include "builders/protobuf/common_objects/proto_account_builder.hpp" @@ -28,6 +16,7 @@ #include "client.hpp" +#include "execution/query_execution_impl.hpp" #include "main/server_runner.hpp" #include "torii/command_service.hpp" #include "torii/processor/query_processor_impl.hpp" @@ -92,12 +81,12 @@ class ClientServerTest : public testing::Test { std::make_shared(); //----------- Query Service ---------- - - auto qpi = std::make_shared(storage); - EXPECT_CALL(*storage, getWsvQuery()).WillRepeatedly(Return(wsv_query)); EXPECT_CALL(*storage, getBlockQuery()).WillRepeatedly(Return(block_query)); + auto qpi = std::make_shared( + storage, std::make_shared(storage)); + //----------- Server run ---------------- runner ->append(std::make_unique( diff --git a/test/module/irohad/execution/execution_mocks.hpp b/test/module/irohad/execution/execution_mocks.hpp new file mode 100644 index 0000000000..480e3f4dfd --- /dev/null +++ b/test/module/irohad/execution/execution_mocks.hpp @@ -0,0 +1,20 @@ +/** + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef IROHA_EXECUTION_MOCKS +#define IROHA_EXECUTION_MOCKS + +#include +#include "execution/query_execution.hpp" + +class MockQueryExecution : public iroha::QueryExecution { + public: + MOCK_METHOD1(validateAndExecute, + std::unique_ptr( + const shared_model::interface::Query &)); + MOCK_METHOD1(validate, bool(const shared_model::interface::BlocksQuery &)); +}; + +#endif // IROHA_EXECUTION_MOCKS diff --git a/test/module/irohad/execution/query_execution_test.cpp b/test/module/irohad/execution/query_execution_test.cpp index 920123880b..f7c9fc19bc 100644 --- a/test/module/irohad/execution/query_execution_test.cpp +++ b/test/module/irohad/execution/query_execution_test.cpp @@ -1,18 +1,6 @@ /** - * Copyright Soramitsu Co., Ltd. 2017 All Rights Reserved. - * http://soramitsu.co.jp - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this/her file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 */ #include "module/irohad/ametsuchi/ametsuchi_mocks.hpp" @@ -25,7 +13,7 @@ #include "builders/protobuf/common_objects/proto_asset_builder.hpp" #include "builders/protobuf/queries.hpp" #include "builders/query_responses/block_query_response_builder.hpp" -#include "execution/query_execution.hpp" +#include "execution/query_execution_impl.hpp" #include "framework/specified_visitor.hpp" #include "framework/test_subscriber.hpp" #include "module/shared_model/builders/protobuf/test_query_builder.hpp" @@ -51,7 +39,10 @@ class QueryValidateExecuteTest : public ::testing::Test { void SetUp() override { wsv_query = std::make_shared>(); block_query = std::make_shared>(); - factory = std::make_shared(wsv_query, block_query); + storage = std::make_shared(); + EXPECT_CALL(*storage, getWsvQuery()).WillRepeatedly(Return(wsv_query)); + EXPECT_CALL(*storage, getBlockQuery()).WillRepeatedly(Return(block_query)); + qry_exec = std::make_shared(storage); creator = clone(shared_model::proto::AccountBuilder() .accountId(admin_id) @@ -69,7 +60,7 @@ class QueryValidateExecuteTest : public ::testing::Test { } std::shared_ptr validateAndExecute( const shared_model::interface::Query &query) { - return factory->validateAndExecute(query); + return qry_exec->validateAndExecute(query); } /** @@ -108,8 +99,9 @@ class QueryValidateExecuteTest : public ::testing::Test { std::shared_ptr creator, account; std::shared_ptr wsv_query; std::shared_ptr block_query; + std::shared_ptr storage; - std::shared_ptr factory; + std::shared_ptr qry_exec; }; class GetAccountTest : public QueryValidateExecuteTest { diff --git a/test/module/irohad/model/model_mocks.hpp b/test/module/irohad/model/model_mocks.hpp deleted file mode 100644 index 094fb6db20..0000000000 --- a/test/module/irohad/model/model_mocks.hpp +++ /dev/null @@ -1,54 +0,0 @@ -/** - * Copyright Soramitsu Co., Ltd. 2017 All Rights Reserved. - * http://soramitsu.co.jp - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef IROHA_MODEL_MOCKS_HPP -#define IROHA_MODEL_MOCKS_HPP - -#include -#include "ametsuchi/wsv_command.hpp" -#include "ametsuchi/wsv_query.hpp" -#include "execution/query_execution.hpp" -#include "model/command.hpp" - -namespace iroha { - namespace model { - - class MockCommand : public Command { - public: - MOCK_METHOD2(validate, bool(ametsuchi::WsvQuery &, const Account &)); - MOCK_METHOD2(execute, - bool(ametsuchi::WsvQuery &, ametsuchi::WsvCommand &)); - - MOCK_CONST_METHOD1(Equals, bool(const Command &)); - bool operator==(const Command &rhs) const override { - return Equals(rhs); - } - - MOCK_CONST_METHOD1(NotEquals, bool(const Command &)); - bool operator!=(const Command &rhs) const override { - return NotEquals(rhs); - } - }; - - class MockQueryProcessingFactory : public QueryProcessingFactory { - public: - MOCK_METHOD1(execute, std::shared_ptr(const Query &query)); - }; - } // namespace model -} // namespace iroha - -#endif // IROHA_MODEL_MOCKS_HPP diff --git a/test/module/irohad/torii/processor/query_processor_test.cpp b/test/module/irohad/torii/processor/query_processor_test.cpp index a38b5d7956..0b531f02e5 100644 --- a/test/module/irohad/torii/processor/query_processor_test.cpp +++ b/test/module/irohad/torii/processor/query_processor_test.cpp @@ -1,18 +1,6 @@ /** - * Copyright Soramitsu Co., Ltd. 2017 All Rights Reserved. - * http://soramitsu.co.jp - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 */ #include "backend/protobuf/block.hpp" @@ -23,7 +11,9 @@ #include "execution/query_execution.hpp" #include "framework/specified_visitor.hpp" #include "framework/test_subscriber.hpp" +#include "interfaces/query_responses/block_query_response.hpp" #include "module/irohad/ametsuchi/ametsuchi_mocks.hpp" +#include "module/irohad/execution/execution_mocks.hpp" #include "module/irohad/validation/validation_mocks.hpp" #include "module/shared_model/builders/protobuf/test_block_builder.hpp" #include "module/shared_model/builders/protobuf/test_query_builder.hpp" @@ -47,118 +37,89 @@ using ::testing::Return; class QueryProcessorTest : public ::testing::Test { public: void SetUp() override { - created_time = iroha::time::now(); - account_id = "account@domain"; - counter = 1048576; + qry_exec = std::make_shared(); + storage = std::make_shared(); + qpi = std::make_shared(storage, qry_exec); + wsv_queries = std::make_shared(); + EXPECT_CALL(*storage, getWsvQuery()).WillRepeatedly(Return(wsv_queries)); + EXPECT_CALL(*storage, getBlockQuery()) + .WillRepeatedly(Return(block_queries)); } auto getBlocksQuery(const std::string &creator_account_id) { return TestUnsignedBlocksQueryBuilder() - .createdTime(created_time) + .createdTime(kCreatedTime) .creatorAccountId(creator_account_id) - .queryCounter(counter) + .queryCounter(kCounter) .build() .signAndAddSignature(keypair) .finish(); } - decltype(iroha::time::now()) created_time; - std::string account_id; - uint64_t counter; + const decltype(iroha::time::now()) kCreatedTime = iroha::time::now(); + const std::string kAccountId = "account@domain"; + const uint64_t kCounter = 1048576; shared_model::crypto::Keypair keypair = shared_model::crypto::DefaultCryptoAlgorithmType::generateKeypair(); std::vector signatories = { keypair.publicKey()}; - shared_model::interface::RolePermissionSet perms; - std::vector roles; + std::shared_ptr qry_exec; + std::shared_ptr wsv_queries; + std::shared_ptr block_queries; + std::shared_ptr storage; + std::shared_ptr qpi; }; /** - * @given account, ametsuchi queries and query processing factory - * @when stateless validation error - * @then Query Processor should return ErrorQueryResponse + * @given QueryProcessorImpl and GetAccountDetail query + * @when queryHandle called at normal flow + * @then the mocked value of validateAndExecute is returned */ TEST_F(QueryProcessorTest, QueryProcessorWhereInvokeInvalidQuery) { - auto wsv_queries = std::make_shared(); - auto block_queries = std::make_shared(); - auto storage = std::make_shared(); - auto qpf = - std::make_unique(wsv_queries, block_queries); - - iroha::torii::QueryProcessorImpl qpi(storage); - - auto query = TestUnsignedQueryBuilder() - .createdTime(created_time) - .creatorAccountId(account_id) - .getAccount(account_id) - .queryCounter(counter) - .build() - .signAndAddSignature(keypair) - .finish(); - - std::shared_ptr shared_account = clone( - shared_model::proto::AccountBuilder().accountId(account_id).build()); - - auto role = "admin"; - roles = {role}; - perms = {shared_model::interface::permissions::Role::kGetMyAccount}; - - EXPECT_CALL(*storage, getWsvQuery()).WillRepeatedly(Return(wsv_queries)); - EXPECT_CALL(*storage, getBlockQuery()).WillRepeatedly(Return(block_queries)); - EXPECT_CALL(*wsv_queries, getAccount(account_id)) - .WillOnce(Return(shared_account)); - EXPECT_CALL(*wsv_queries, getAccountRoles(account_id)) - .Times(2) - .WillRepeatedly(Return(roles)); - EXPECT_CALL(*wsv_queries, getRolePermissions(role)).WillOnce(Return(perms)); - EXPECT_CALL(*wsv_queries, getSignatories(account_id)) + auto qry = TestUnsignedQueryBuilder() + .creatorAccountId(kAccountId) + .getAccountDetail(kAccountId) + .build() + .signAndAddSignature(keypair) + .finish(); + auto qry_resp = + clone(TestQueryResponseBuilder().accountDetailResponse("").build()); + + EXPECT_CALL(*wsv_queries, getSignatories(kAccountId)) .WillRepeatedly(Return(signatories)); + EXPECT_CALL(*qry_exec, validateAndExecute(_)) + .WillOnce(Invoke([&qry_resp](auto &query) { return clone(*qry_resp); })); - auto response = qpi.queryHandle(query); + auto response = qpi->queryHandle(qry); ASSERT_TRUE(response); ASSERT_NO_THROW(boost::apply_visitor( - framework::SpecifiedVisitor(), + framework::SpecifiedVisitor< + shared_model::interface::AccountDetailResponse>(), response->get())); } /** - * @given account, ametsuchi queries and query processing factory - * @when signed with wrong key - * @then Query Processor should return StatefulFailed + * @given QueryProcessorImpl and GetAccountDetail query with wrong signature + * @when queryHandle called at normal flow + * @then Query Processor returns StatefulFailed response */ TEST_F(QueryProcessorTest, QueryProcessorWithWrongKey) { - auto wsv_queries = std::make_shared(); - auto block_queries = std::make_shared(); - auto storage = std::make_shared(); - auto qpf = - std::make_unique(wsv_queries, block_queries); - - iroha::torii::QueryProcessorImpl qpi(storage); - auto query = TestUnsignedQueryBuilder() - .createdTime(created_time) - .creatorAccountId(account_id) - .getAccount(account_id) - .queryCounter(counter) + .creatorAccountId(kAccountId) + .getAccountDetail(kAccountId) .build() .signAndAddSignature( shared_model::crypto::DefaultCryptoAlgorithmType:: generateKeypair()) .finish(); + auto qry_resp = + clone(TestQueryResponseBuilder().accountDetailResponse("").build()); - std::shared_ptr shared_account = clone( - shared_model::proto::AccountBuilder().accountId(account_id).build()); - auto role = "admin"; - roles = {role}; - perms = {shared_model::interface::permissions::Role::kGetMyAccount}; - - EXPECT_CALL(*storage, getWsvQuery()).WillRepeatedly(Return(wsv_queries)); - EXPECT_CALL(*storage, getBlockQuery()).WillRepeatedly(Return(block_queries)); - EXPECT_CALL(*wsv_queries, getSignatories(account_id)) + EXPECT_CALL(*wsv_queries, getSignatories(kAccountId)) .WillRepeatedly(Return(signatories)); - auto response = qpi.queryHandle(query); + auto response = qpi->queryHandle(query); ASSERT_TRUE(response); ASSERT_NO_THROW(boost::apply_visitor( shared_model::interface::QueryErrorResponseChecker< @@ -167,46 +128,21 @@ TEST_F(QueryProcessorTest, QueryProcessorWithWrongKey) { } /** - * @given account, ametsuchi queries and query processing factory + * @given account, ametsuchi queries * @when valid block query is send * @then Query Processor should start emitting BlockQueryRespones to the * observable */ TEST_F(QueryProcessorTest, GetBlocksQuery) { - auto wsv_queries = std::make_shared(); - auto block_queries = std::make_shared(); - auto storage = std::make_shared(); - auto blockNumber = 5; - auto qpf = - std::make_unique(wsv_queries, block_queries); - - iroha::torii::QueryProcessorImpl qpi(storage); - - auto blockQuery = TestUnsignedBlocksQueryBuilder() - .createdTime(created_time) - .creatorAccountId(account_id) - .queryCounter(counter) - .build() - .signAndAddSignature(keypair) - .finish(); + auto block_number = 5; + auto block_query = getBlocksQuery(kAccountId); - std::shared_ptr shared_account = clone( - shared_model::proto::AccountBuilder().accountId(account_id).build()); - - auto role = "admin"; - std::vector roles = {role}; - perms = {shared_model::interface::permissions::Role::kGetMyAccount, - shared_model::interface::permissions::Role::kGetBlocks}; - EXPECT_CALL(*storage, getWsvQuery()).WillRepeatedly(Return(wsv_queries)); - EXPECT_CALL(*storage, getBlockQuery()).WillRepeatedly(Return(block_queries)); - EXPECT_CALL(*wsv_queries, getAccountRoles(account_id)) - .WillOnce(Return(roles)); - EXPECT_CALL(*wsv_queries, getRolePermissions(role)).WillOnce(Return(perms)); - EXPECT_CALL(*wsv_queries, getSignatories(account_id)) - .WillRepeatedly(Return(signatories)); + EXPECT_CALL(*wsv_queries, getSignatories(kAccountId)) + .WillOnce(Return(signatories)); + EXPECT_CALL(*qry_exec, validate(_)).WillOnce(Return(true)); auto wrapper = make_test_subscriber( - qpi.blocksQueryHandle(blockQuery), blockNumber); + qpi->blocksQueryHandle(block_query), block_number); wrapper.subscribe([](auto response) { ASSERT_NO_THROW({ boost::apply_visitor( @@ -214,49 +150,28 @@ TEST_F(QueryProcessorTest, GetBlocksQuery) { response->get()); }); }); - for (int i = 0; i < blockNumber; i++) { + for (int i = 0; i < block_number; i++) { storage->notifier.get_subscriber().on_next( - clone(TestBlockBuilder() - .height(1) - .prevHash(shared_model::crypto::Hash(std::string(32, '0'))) - .build())); + clone(TestBlockBuilder().build())); } ASSERT_TRUE(wrapper.validate()); } /** - * @given account, ametsuchi queries and query processing factory + * @given account, ametsuchi queries * @when valid block query is invalid (no can_get_blocks permission) * @then Query Processor should return an observable with blockError */ TEST_F(QueryProcessorTest, GetBlocksQueryNoPerms) { - auto wsv_queries = std::make_shared(); - auto block_queries = std::make_shared(); - auto storage = std::make_shared(); - auto blockNumber = 5; - auto qpf = - std::make_unique(wsv_queries, block_queries); + auto block_number = 5; + auto block_query = getBlocksQuery(kAccountId); - iroha::torii::QueryProcessorImpl qpi(storage); - - auto blockQuery = getBlocksQuery(account_id); - - std::shared_ptr shared_account = clone( - shared_model::proto::AccountBuilder().accountId(account_id).build()); - - auto role = "admin"; - std::vector roles = {role}; - perms = {shared_model::interface::permissions::Role::kGetMyAccount}; - EXPECT_CALL(*storage, getWsvQuery()).WillRepeatedly(Return(wsv_queries)); - EXPECT_CALL(*storage, getBlockQuery()).WillRepeatedly(Return(block_queries)); - EXPECT_CALL(*wsv_queries, getAccountRoles(account_id)) - .WillOnce(Return(roles)); - EXPECT_CALL(*wsv_queries, getRolePermissions(role)).WillOnce(Return(perms)); - EXPECT_CALL(*wsv_queries, getSignatories(account_id)) + EXPECT_CALL(*wsv_queries, getSignatories(kAccountId)) .WillRepeatedly(Return(signatories)); + EXPECT_CALL(*qry_exec, validate(_)).WillOnce(Return(false)); auto wrapper = - make_test_subscriber(qpi.blocksQueryHandle(blockQuery), 1); + make_test_subscriber(qpi->blocksQueryHandle(block_query), 1); wrapper.subscribe([](auto response) { ASSERT_NO_THROW({ boost::apply_visitor(framework::SpecifiedVisitor< @@ -264,7 +179,7 @@ TEST_F(QueryProcessorTest, GetBlocksQueryNoPerms) { response->get()); }); }); - for (int i = 0; i < blockNumber; i++) { + for (int i = 0; i < block_number; i++) { storage->notifier.get_subscriber().on_next( clone(TestBlockBuilder() .height(1) @@ -273,106 +188,3 @@ TEST_F(QueryProcessorTest, GetBlocksQueryNoPerms) { } ASSERT_TRUE(wrapper.validate()); } - -/** - * @given admin with permisisons to get blocks - * @and user with no permissions to get blocks - * @when admin sends blocks query - * @and user sends blocks query - * @then admin will get only block response - * @and user will get only block error response with stateful invalid message - */ -TEST_F(QueryProcessorTest, NoOneSeesStatefulInvalidButCaller) { - auto wsv_queries = std::make_shared(); - auto block_queries = std::make_shared(); - auto storage = std::make_shared(); - - auto qpf = - std::make_unique(wsv_queries, block_queries); - - iroha::torii::QueryProcessorImpl qpi(storage); - - std::shared_ptr account_with_perms = clone( - shared_model::proto::AccountBuilder().accountId(account_id).build()); - - std::shared_ptr account_without_perms = - clone( - shared_model::proto::AccountBuilder().accountId(account_id).build()); - - auto admin_account_id = "admin@test"; - auto user_account_id = "user@test"; - - auto admin_role = "admin"; - auto user_role = "user"; - - shared_model::interface::RolePermissionSet admin_perms = { - shared_model::interface::permissions::Role::kGetMyAccount, - shared_model::interface::permissions::Role::kGetBlocks}; - shared_model::interface::RolePermissionSet user_perms = { - shared_model::interface::permissions::Role::kGetMyAccount}; - - EXPECT_CALL(*storage, getWsvQuery()).WillRepeatedly(Return(wsv_queries)); - EXPECT_CALL(*storage, getBlockQuery()).WillRepeatedly(Return(block_queries)); - - EXPECT_CALL(*wsv_queries, getSignatories(admin_account_id)) - .WillRepeatedly(Return(signatories)); - EXPECT_CALL(*wsv_queries, getSignatories(user_account_id)) - .WillRepeatedly(Return(signatories)); - - EXPECT_CALL(*wsv_queries, getAccountRoles(admin_account_id)) - .WillOnce(Return(std::vector{admin_role})); - EXPECT_CALL(*wsv_queries, getAccountRoles(user_account_id)) - .WillOnce(Return(std::vector{user_role})); - - EXPECT_CALL(*wsv_queries, getRolePermissions(admin_role)) - .WillOnce(Return(admin_perms)); - EXPECT_CALL(*wsv_queries, getRolePermissions(user_role)) - .WillOnce(Return(user_perms)); - - auto expected_block = - TestBlockBuilder() - .height(1) - .prevHash(shared_model::crypto::Hash(std::string(32, '0'))) - .build(); - - auto admin_block_query = getBlocksQuery(admin_account_id); - - // check that admin can get block and do not get anything but block responses - auto admin_wrapper = make_test_subscriber( - qpi.blocksQueryHandle(admin_block_query), 1); - admin_wrapper.subscribe([&expected_block]( - const std::shared_ptr< - shared_model::interface::BlockQueryResponse> - &block) { - ASSERT_NO_THROW({ - auto &block_response = boost::apply_visitor( - framework::SpecifiedVisitor(), - block->get()); - ASSERT_EQ(block_response.block(), expected_block); - }); - }); - - auto user_block_query = getBlocksQuery(user_account_id); - - // check that user without can_get_blocks permission will not get anything but - // block error response - auto user_wrapper = make_test_subscriber( - qpi.blocksQueryHandle(user_block_query), 1); - user_wrapper.subscribe( - [](const std::shared_ptr - &block) { - ASSERT_NO_THROW({ - auto &block_response = boost::apply_visitor( - framework::SpecifiedVisitor< - shared_model::interface::BlockErrorResponse>(), - block->get()); - ASSERT_EQ(block_response.message(), "Stateful invalid"); - }); - }); - - // apply expected block to the ledger - storage->notifier.get_subscriber().on_next(clone(expected_block)); - - ASSERT_TRUE(admin_wrapper.validate()); - ASSERT_TRUE(user_wrapper.validate()); -} diff --git a/test/module/irohad/torii/torii_queries_test.cpp b/test/module/irohad/torii/torii_queries_test.cpp index 71aafdb22b..fc0b845d77 100644 --- a/test/module/irohad/torii/torii_queries_test.cpp +++ b/test/module/irohad/torii/torii_queries_test.cpp @@ -1,24 +1,14 @@ -/* -Copyright Soramitsu Co., Ltd. 2016 All Rights Reserved. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ +/** + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ #include "module/irohad/ametsuchi/ametsuchi_mocks.hpp" #include "module/irohad/network/network_mocks.hpp" #include "module/irohad/torii/torii_mocks.hpp" #include "module/irohad/validation/validation_mocks.hpp" +#include "backend/protobuf/query_responses/proto_query_response.hpp" #include "builders/protobuf/common_objects/proto_account_asset_builder.hpp" #include "builders/protobuf/common_objects/proto_account_builder.hpp" #include "builders/protobuf/common_objects/proto_asset_builder.hpp" @@ -26,6 +16,7 @@ limitations under the License. #include "module/shared_model/builders/protobuf/test_query_builder.hpp" #include "module/shared_model/builders/protobuf/test_transaction_builder.hpp" +#include "execution/query_execution_impl.hpp" #include "framework/specified_visitor.hpp" #include "main/server_runner.hpp" #include "torii/processor/query_processor_impl.hpp" @@ -58,11 +49,12 @@ class ToriiQueriesTest : public testing::Test { //----------- Query Service ---------- - auto qpi = std::make_shared(storage); - EXPECT_CALL(*storage, getWsvQuery()).WillRepeatedly(Return(wsv_query)); EXPECT_CALL(*storage, getBlockQuery()).WillRepeatedly(Return(block_query)); + auto qpi = std::make_shared( + storage, std::make_shared(storage)); + //----------- Server run ---------------- runner->append(std::make_unique(qpi)) .run() From adb842be8360c5786feadd183b46c81a2cf35e2a Mon Sep 17 00:00:00 2001 From: Akvinikym Date: Mon, 23 Jul 2018 16:32:35 +0300 Subject: [PATCH 63/97] Ordering Gate Batch Support (#1582) Signed-off-by: Akvinikym --- irohad/network/ordering_gate.hpp | 11 ++++++++++- irohad/network/ordering_gate_transport.hpp | 8 ++++++++ irohad/ordering/impl/ordering_gate_impl.cpp | 16 ++++++++++++++-- irohad/ordering/impl/ordering_gate_impl.hpp | 6 +++++- .../impl/ordering_gate_transport_grpc.cpp | 18 ++++++++++++++++++ .../impl/ordering_gate_transport_grpc.hpp | 4 ++++ schema/ordering.proto | 2 ++ test/module/irohad/network/network_mocks.hpp | 11 ++++++++--- .../irohad/ordering/ordering_gate_test.cpp | 2 ++ 9 files changed, 71 insertions(+), 7 deletions(-) diff --git a/irohad/network/ordering_gate.hpp b/irohad/network/ordering_gate.hpp index 35b6ca8806..fc28f7eb51 100644 --- a/irohad/network/ordering_gate.hpp +++ b/irohad/network/ordering_gate.hpp @@ -19,6 +19,8 @@ #define IROHA_ORDERING_SERVICE_HPP #include + +#include "interfaces/iroha_internal/transaction_batch.hpp" #include "network/peer_communication_service.hpp" namespace shared_model { @@ -42,7 +44,14 @@ namespace iroha { */ virtual void propagateTransaction( std::shared_ptr - transaction) = 0; + transaction) const = 0; + + /** + * Propagate a transaction batch for further processing + * @param batch + */ + virtual void propagateBatch( + const shared_model::interface::TransactionBatch &batch) const = 0; /** * Return observable of all proposals in the consensus diff --git a/irohad/network/ordering_gate_transport.hpp b/irohad/network/ordering_gate_transport.hpp index 34a9bcfc5b..d1bb21bfb4 100644 --- a/irohad/network/ordering_gate_transport.hpp +++ b/irohad/network/ordering_gate_transport.hpp @@ -22,6 +22,7 @@ namespace shared_model { namespace interface { class Transaction; + class TransactionBatch; class Proposal; } // namespace interface } // namespace shared_model @@ -68,6 +69,13 @@ namespace iroha { std::shared_ptr transaction) = 0; + /** + * Propagates transaction batch over network + * @param batch to be propagated + */ + virtual void propagateBatch( + const shared_model::interface::TransactionBatch &batch) = 0; + virtual ~OrderingGateTransport() = default; }; diff --git a/irohad/ordering/impl/ordering_gate_impl.cpp b/irohad/ordering/impl/ordering_gate_impl.cpp index 1daf800f9d..3f4b78f649 100644 --- a/irohad/ordering/impl/ordering_gate_impl.cpp +++ b/irohad/ordering/impl/ordering_gate_impl.cpp @@ -43,14 +43,26 @@ namespace iroha { run_async_(run_async) {} void OrderingGateImpl::propagateTransaction( - std::shared_ptr - transaction) { + std::shared_ptr transaction) + const { log_->info("propagate tx, account_id: {}", " account_id: " + transaction->creatorAccountId()); transport_->propagateTransaction(transaction); } + void OrderingGateImpl::propagateBatch( + const shared_model::interface::TransactionBatch &batch) const { + if (batch.transactions().empty()) { + log_->warn("trying to propagate empty batch"); + return; + } + log_->info("propagate batch, account_id: {}", + batch.transactions().front()->creatorAccountId()); + + transport_->propagateBatch(batch); + } + rxcpp::observable> OrderingGateImpl::on_proposal() { return proposals_.get_observable(); diff --git a/irohad/ordering/impl/ordering_gate_impl.hpp b/irohad/ordering/impl/ordering_gate_impl.hpp index b00b9a770d..96509ce6fb 100644 --- a/irohad/ordering/impl/ordering_gate_impl.hpp +++ b/irohad/ordering/impl/ordering_gate_impl.hpp @@ -70,7 +70,11 @@ namespace iroha { void propagateTransaction( std::shared_ptr - transaction) override; + transaction) const override; + + void propagateBatch( + const shared_model::interface::TransactionBatch &batch) + const override; rxcpp::observable> on_proposal() override; diff --git a/irohad/ordering/impl/ordering_gate_transport_grpc.cpp b/irohad/ordering/impl/ordering_gate_transport_grpc.cpp index 15364dc778..a961f22b0f 100644 --- a/irohad/ordering/impl/ordering_gate_transport_grpc.cpp +++ b/irohad/ordering/impl/ordering_gate_transport_grpc.cpp @@ -18,6 +18,7 @@ #include "backend/protobuf/transaction.hpp" #include "builders/protobuf/proposal.hpp" +#include "endpoint.pb.h" #include "interfaces/common_objects/types.hpp" #include "network/impl/grpc_channel_builder.hpp" @@ -73,6 +74,23 @@ void OrderingGateTransportGrpc::propagateTransaction( call->response_reader->Finish(&call->reply, &call->status, call); } +void OrderingGateTransportGrpc::propagateBatch( + const shared_model::interface::TransactionBatch &batch) { + log_->info("Propagate transaction batch (on transport)"); + auto call = new AsyncClientCall; + + iroha::protocol::TxList batch_transport; + for (const auto tx : batch.transactions()) { + new (batch_transport.add_transactions()) iroha::protocol::Transaction( + std::static_pointer_cast(tx) + ->getTransport()); + } + call->response_reader = + client_->AsynconBatch(&call->context, batch_transport, &cq_); + + call->response_reader->Finish(&call->reply, &call->status, call); +} + void OrderingGateTransportGrpc::subscribe( std::shared_ptr subscriber) { log_->info("Subscribe"); diff --git a/irohad/ordering/impl/ordering_gate_transport_grpc.hpp b/irohad/ordering/impl/ordering_gate_transport_grpc.hpp index 39292b42f8..2f10045484 100644 --- a/irohad/ordering/impl/ordering_gate_transport_grpc.hpp +++ b/irohad/ordering/impl/ordering_gate_transport_grpc.hpp @@ -19,6 +19,7 @@ #include +#include "interfaces/iroha_internal/transaction_batch.hpp" #include "logger/logger.hpp" #include "network/impl/async_grpc_client.hpp" #include "network/ordering_gate_transport.hpp" @@ -47,6 +48,9 @@ namespace iroha { std::shared_ptr transaction) override; + void propagateBatch( + const shared_model::interface::TransactionBatch &batch) override; + void subscribe(std::shared_ptr subscriber) override; diff --git a/schema/ordering.proto b/schema/ordering.proto index 40b97571d4..c8a3aac100 100644 --- a/schema/ordering.proto +++ b/schema/ordering.proto @@ -3,6 +3,7 @@ package iroha.ordering.proto; import "transaction.proto"; import "proposal.proto"; +import "endpoint.proto"; import "google/protobuf/empty.proto"; service OrderingGateTransportGrpc { @@ -11,4 +12,5 @@ service OrderingGateTransportGrpc { service OrderingServiceTransportGrpc { rpc onTransaction (iroha.protocol.Transaction) returns (google.protobuf.Empty); + rpc onBatch (iroha.protocol.TxList) returns (google.protobuf.Empty); } diff --git a/test/module/irohad/network/network_mocks.hpp b/test/module/irohad/network/network_mocks.hpp index b37eac58a2..7801639dd7 100644 --- a/test/module/irohad/network/network_mocks.hpp +++ b/test/module/irohad/network/network_mocks.hpp @@ -39,8 +39,9 @@ namespace iroha { propagate_transaction, void(std::shared_ptr)); - MOCK_CONST_METHOD1(propagate_batch, - void(const shared_model::interface::TransactionBatch &)); + MOCK_CONST_METHOD1( + propagate_batch, + void(const shared_model::interface::TransactionBatch &)); MOCK_CONST_METHOD0( on_proposal, @@ -70,11 +71,15 @@ namespace iroha { class MockOrderingGate : public OrderingGate { public: - MOCK_METHOD1( + MOCK_CONST_METHOD1( propagateTransaction, void(std::shared_ptr transaction)); + MOCK_CONST_METHOD1( + propagateBatch, + void(const shared_model::interface::TransactionBatch &)); + MOCK_METHOD0(on_proposal, rxcpp::observable< std::shared_ptr>()); diff --git a/test/module/irohad/ordering/ordering_gate_test.cpp b/test/module/irohad/ordering/ordering_gate_test.cpp index 1f158b039a..b758fb4316 100644 --- a/test/module/irohad/ordering/ordering_gate_test.cpp +++ b/test/module/irohad/ordering/ordering_gate_test.cpp @@ -54,6 +54,8 @@ class MockOrderingGateTransport : public OrderingGateTransport { MOCK_METHOD1( propagateTransaction, void(std::shared_ptr)); + MOCK_METHOD1(propagateBatch, + void(const shared_model::interface::TransactionBatch &)); }; class OrderingGateTest : public ::testing::Test { From d04a283438a983a0e498a9d1cd9cdb90280f1523 Mon Sep 17 00:00:00 2001 From: victordrobny Date: Mon, 23 Jul 2018 16:47:47 +0300 Subject: [PATCH 64/97] Feature/sql executors (#1557) sql executor for AddAssetQuantity command Signed-off-by: victordrobny --- irohad/ametsuchi/CMakeLists.txt | 1 + irohad/ametsuchi/command_executor.hpp | 114 +++ .../ametsuchi/impl/mutable_storage_impl.cpp | 3 +- .../impl/postgres_command_executor.cpp | 799 ++++++++++++++++ .../impl/postgres_command_executor.hpp | 81 ++ irohad/ametsuchi/impl/temporary_wsv_impl.cpp | 3 +- irohad/ametsuchi/wsv_command.hpp | 1 + irohad/execution/command_executor.hpp | 78 +- irohad/execution/impl/command_executor.cpp | 420 -------- shared_model/utils/amount_utils.cpp | 7 - .../acceptance/add_asset_qty_test.cpp | 4 +- .../acceptance/transfer_asset_test.cpp | 6 +- test/module/irohad/ametsuchi/CMakeLists.txt | 7 + .../irohad/ametsuchi/ametsuchi_mocks.hpp | 1 + .../ametsuchi/postgres_executor_test.cpp | 864 +++++++++++++++++ .../ametsuchi/wsv_query_command_test.cpp | 1 + .../command_validate_execute_test.cpp | 905 ++---------------- .../irohad/simulator/simulator_test.cpp | 4 +- .../protobuf/test_account_asset_builder.hpp | 2 +- .../builders/protobuf/test_asset_builder.hpp | 29 + 20 files changed, 1969 insertions(+), 1361 deletions(-) create mode 100644 irohad/ametsuchi/command_executor.hpp create mode 100644 irohad/ametsuchi/impl/postgres_command_executor.cpp create mode 100644 irohad/ametsuchi/impl/postgres_command_executor.hpp create mode 100644 test/module/irohad/ametsuchi/postgres_executor_test.cpp create mode 100644 test/module/shared_model/builders/protobuf/test_asset_builder.hpp diff --git a/irohad/ametsuchi/CMakeLists.txt b/irohad/ametsuchi/CMakeLists.txt index cf95605824..154fe30c3f 100644 --- a/irohad/ametsuchi/CMakeLists.txt +++ b/irohad/ametsuchi/CMakeLists.txt @@ -7,6 +7,7 @@ add_library(ametsuchi impl/postgres_wsv_command.cpp impl/peer_query_wsv.cpp impl/postgres_block_query.cpp + impl/postgres_command_executor.cpp impl/postgres_block_index.cpp impl/postgres_ordering_service_persistent_state.cpp impl/wsv_restorer_impl.cpp diff --git a/irohad/ametsuchi/command_executor.hpp b/irohad/ametsuchi/command_executor.hpp new file mode 100644 index 0000000000..6beb339896 --- /dev/null +++ b/irohad/ametsuchi/command_executor.hpp @@ -0,0 +1,114 @@ +/** + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef IROHA_AMETSUCHI_COMMAND_EXECUTOR_HPP +#define IROHA_AMETSUCHI_COMMAND_EXECUTOR_HPP + +#include "common/result.hpp" +#include "interfaces/common_objects/types.hpp" + +namespace shared_model { + namespace interface { + class AddAssetQuantity; + class AddPeer; + class AddSignatory; + class AppendRole; + class CreateAccount; + class CreateAsset; + class CreateDomain; + class CreateRole; + class DetachRole; + class GrantPermission; + class RemoveSignatory; + class RevokePermission; + class SetAccountDetail; + class SetQuorum; + class SubtractAssetQuantity; + class TransferAsset; + } // namespace interface +} // namespace shared_model + +namespace iroha { + namespace ametsuchi { + + /** + * Error for command execution or validation + * Contains command name, as well as an error message + */ + struct CommandError { + std::string command_name; + std::string error_message; + + std::string toString() const; + }; + + /** + * If command is successful, we assume changes are made, + * and do not need anything + * If something goes wrong, Result will contain Error + * with additional information + */ + using CommandResult = expected::Result; + + class CommandExecutor : public boost::static_visitor { + public: + virtual ~CommandExecutor() = default; + + virtual void setCreatorAccountId( + const shared_model::interface::types::AccountIdType + &creator_account_id) = 0; + + virtual CommandResult operator()( + const shared_model::interface::AddAssetQuantity &command) = 0; + + virtual CommandResult operator()( + const shared_model::interface::AddPeer &command) = 0; + + virtual CommandResult operator()( + const shared_model::interface::AddSignatory &command) = 0; + + virtual CommandResult operator()( + const shared_model::interface::AppendRole &command) = 0; + + virtual CommandResult operator()( + const shared_model::interface::CreateAccount &command) = 0; + + virtual CommandResult operator()( + const shared_model::interface::CreateAsset &command) = 0; + + virtual CommandResult operator()( + const shared_model::interface::CreateDomain &command) = 0; + + virtual CommandResult operator()( + const shared_model::interface::CreateRole &command) = 0; + + virtual CommandResult operator()( + const shared_model::interface::DetachRole &command) = 0; + + virtual CommandResult operator()( + const shared_model::interface::GrantPermission &command) = 0; + + virtual CommandResult operator()( + const shared_model::interface::RemoveSignatory &command) = 0; + + virtual CommandResult operator()( + const shared_model::interface::RevokePermission &command) = 0; + + virtual CommandResult operator()( + const shared_model::interface::SetAccountDetail &command) = 0; + + virtual CommandResult operator()( + const shared_model::interface::SetQuorum &command) = 0; + + virtual CommandResult operator()( + const shared_model::interface::SubtractAssetQuantity &command) = 0; + + virtual CommandResult operator()( + const shared_model::interface::TransferAsset &command) = 0; + }; + } // namespace ametsuchi +} // namespace iroha + +#endif // IROHA_AMETSUCHI_COMMAND_EXECUTOR_HPP diff --git a/irohad/ametsuchi/impl/mutable_storage_impl.cpp b/irohad/ametsuchi/impl/mutable_storage_impl.cpp index c934aa99a7..e32905d006 100644 --- a/irohad/ametsuchi/impl/mutable_storage_impl.cpp +++ b/irohad/ametsuchi/impl/mutable_storage_impl.cpp @@ -19,6 +19,7 @@ #include +#include "ametsuchi/impl/postgres_command_executor.hpp" #include "ametsuchi/impl/postgres_block_index.hpp" #include "ametsuchi/impl/postgres_wsv_command.hpp" #include "ametsuchi/impl/postgres_wsv_query.hpp" @@ -37,7 +38,7 @@ namespace iroha { wsv_(std::make_shared(*sql_, factory)), executor_(std::make_shared(*sql_)), block_index_(std::make_unique(*sql_)), - command_executor_(std::make_shared(wsv_, executor_)), + command_executor_(std::make_shared(*sql_)), committed(false), log_(logger::log("MutableStorage")) { *sql_ << "BEGIN"; diff --git a/irohad/ametsuchi/impl/postgres_command_executor.cpp b/irohad/ametsuchi/impl/postgres_command_executor.cpp new file mode 100644 index 0000000000..8f093b23b4 --- /dev/null +++ b/irohad/ametsuchi/impl/postgres_command_executor.cpp @@ -0,0 +1,799 @@ +/** + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "ametsuchi/impl/postgres_command_executor.hpp" + +#include + +#include "ametsuchi/impl/postgres_wsv_common.hpp" +#include "backend/protobuf/permissions.hpp" +#include "interfaces/commands/add_asset_quantity.hpp" +#include "interfaces/commands/add_peer.hpp" +#include "interfaces/commands/add_signatory.hpp" +#include "interfaces/commands/append_role.hpp" +#include "interfaces/commands/create_account.hpp" +#include "interfaces/commands/create_asset.hpp" +#include "interfaces/commands/create_domain.hpp" +#include "interfaces/commands/create_role.hpp" +#include "interfaces/commands/detach_role.hpp" +#include "interfaces/commands/grant_permission.hpp" +#include "interfaces/commands/remove_signatory.hpp" +#include "interfaces/commands/revoke_permission.hpp" +#include "interfaces/commands/set_account_detail.hpp" +#include "interfaces/commands/set_quorum.hpp" +#include "interfaces/commands/subtract_asset_quantity.hpp" +#include "interfaces/commands/transfer_asset.hpp" +#include "interfaces/common_objects/types.hpp" + +namespace { + iroha::expected::Error makeCommandError( + const std::string &error_message, + const std::string &command_name) noexcept { + return iroha::expected::makeError( + iroha::ametsuchi::CommandError{command_name, error_message}); + } + + /** + * Transforms soci statement to CommandResult, + * which will have error message generated by error_generator + * appended to error received from given result + * @param result which can be received by calling execute_ + * @param error_generator function which must generate error message + * to be used as a return error. + * Function is passed instead of string to avoid overhead of string + * construction in successful case. + * @return CommandResult with combined error message + * in case of result contains error + */ + template + iroha::ametsuchi::CommandResult makeCommandResult( + soci::statement &st, + const std::string &command_name, + Function &&error_generator) noexcept { + st.define_and_bind(); + try { + st.execute(true); + } catch (std::exception &e) { + return makeCommandError(error_generator() + "\n" + e.what(), + command_name); + } + return {}; + } + + /** + * Transforms soci statement to CommandResult, + * which will have error message generated exception + * Assums that statement query returns 0 in case of success or error code + * @param result which can be received by calling execute_ + * @param error_generator functions which must generate error message + * to be used as a return error. + * Functions are passed instead of string to avoid overhead of string + * construction in successful case. + * @return CommandResult with combined error message + * in case of result contains error + */ + iroha::ametsuchi::CommandResult makeCommandResultByReturnedValue( + soci::statement &st, + const std::string &command_name, + std::vector> &error_generator) noexcept { + uint32_t result; + st.exchange(soci::into(result)); + st.define_and_bind(); + try { + st.execute(true); + if (result != 0) { + return makeCommandError(error_generator[result - 1](), command_name); + } + return {}; + } catch (std::exception &e) { + return makeCommandError(e.what(), command_name); + } + } +} // namespace + +namespace iroha { + namespace ametsuchi { + + std::string CommandError::toString() const { + return (boost::format("%s: %s") % command_name % error_message).str(); + } + + PostgresCommandExecutor::PostgresCommandExecutor(soci::session &sql) + : sql_(sql) {} + + void PostgresCommandExecutor::setCreatorAccountId( + const shared_model::interface::types::AccountIdType + &creator_account_id) { + creator_account_id_ = creator_account_id; + } + + CommandResult PostgresCommandExecutor::operator()( + const shared_model::interface::AddAssetQuantity &command) { + auto &account_id = creator_account_id_; + auto &asset_id = command.assetId(); + auto amount = command.amount().toStringRepr(); + auto precision = command.amount().precision(); + soci::statement st = sql_.prepare << + // clang-format off + (R"( + WITH has_account AS (SELECT account_id FROM account + WHERE account_id = :account_id LIMIT 1), + has_asset AS (SELECT asset_id FROM asset + WHERE asset_id = :asset_id AND + precision >= :precision LIMIT 1), + amount AS (SELECT amount FROM account_has_asset + WHERE asset_id = :asset_id AND + account_id = :account_id LIMIT 1), + new_value AS (SELECT :new_value::decimal + + (SELECT + CASE WHEN EXISTS + (SELECT amount FROM amount LIMIT 1) THEN + (SELECT amount FROM amount LIMIT 1) + ELSE 0::decimal + END) AS value + ), + inserted AS + ( + INSERT INTO account_has_asset(account_id, asset_id, amount) + ( + SELECT :account_id, :asset_id, value FROM new_value + WHERE EXISTS (SELECT * FROM has_account LIMIT 1) AND + EXISTS (SELECT * FROM has_asset LIMIT 1) AND + EXISTS (SELECT value FROM new_value + WHERE value < 2::decimal ^ (256 - :precision) + LIMIT 1) + ) + ON CONFLICT (account_id, asset_id) DO UPDATE + SET amount = EXCLUDED.amount + RETURNING (1) + ) + SELECT CASE + WHEN EXISTS (SELECT * FROM inserted LIMIT 1) THEN 0 + WHEN NOT EXISTS (SELECT * FROM has_account LIMIT 1) THEN 1 + WHEN NOT EXISTS (SELECT * FROM has_asset LIMIT 1) THEN 2 + WHEN NOT EXISTS (SELECT value FROM new_value + WHERE value < 2::decimal ^ (256 - :precision) + LIMIT 1) THEN 3 + ELSE 4 + END AS result;)"); + // clang-format on + + st.exchange(soci::use(account_id, "account_id")); + st.exchange(soci::use(asset_id, "asset_id")); + st.exchange(soci::use(amount, "new_value")); + st.exchange(soci::use(precision, "precision")); + + std::vector> message_gen = { + [] { return std::string("Account does not exist"); }, + [] { + return std::string("Asset with given precision does not exist"); + }, + [] { return std::string("Summation overflows uint256"); }, + }; + return makeCommandResultByReturnedValue( + st, "AddAssetQuantity", message_gen); + } + + CommandResult PostgresCommandExecutor::operator()( + const shared_model::interface::AddPeer &command) { + auto &peer = command.peer(); + soci::statement st = sql_.prepare + << "INSERT INTO peer(public_key, address) VALUES (:pk, :address)"; + st.exchange(soci::use(peer.pubkey().hex())); + st.exchange(soci::use(peer.address())); + auto message_gen = [&] { + return (boost::format( + "failed to insert peer, public key: '%s', address: '%s'") + % peer.pubkey().hex() % peer.address()) + .str(); + }; + return makeCommandResult(st, "AddPeer", message_gen); + } + + CommandResult PostgresCommandExecutor::operator()( + const shared_model::interface::AddSignatory &command) { + auto &account_id = command.accountId(); + auto pubkey = command.pubkey().hex(); + soci::statement st = sql_.prepare << + R"( + WITH insert_signatory AS + ( + INSERT INTO signatory(public_key) VALUES (:pk) + ON CONFLICT DO NOTHING RETURNING (1) + ), + has_signatory AS (SELECT * FROM signatory WHERE public_key = :pk), + insert_account_signatory AS + ( + INSERT INTO account_has_signatory(account_id, public_key) + ( + SELECT :account_id, :pk WHERE EXISTS + (SELECT * FROM insert_signatory) OR + EXISTS (SELECT * FROM has_signatory) + ) + RETURNING (1) + ) + SELECT CASE + WHEN EXISTS (SELECT * FROM insert_account_signatory) THEN 0 + WHEN EXISTS (SELECT * FROM insert_signatory) THEN 1 + ELSE 2 + END AS RESULT;)"; + st.exchange(soci::use(pubkey, "pk")); + st.exchange(soci::use(account_id, "account_id")); + + std::vector> message_gen = { + [&] { + return (boost::format( + "failed to insert account signatory, account id: " + "'%s', signatory hex string: '%s") + % account_id % pubkey) + .str(); + }, + [&] { + return (boost::format("failed to insert signatory, " + "signatory hex string: '%s'") + % pubkey) + .str(); + }, + }; + return makeCommandResultByReturnedValue(st, "AddSignatory", message_gen); + } + + CommandResult PostgresCommandExecutor::operator()( + const shared_model::interface::AppendRole &command) { + auto &account_id = command.accountId(); + auto &role_name = command.roleName(); + soci::statement st = sql_.prepare + << "INSERT INTO account_has_roles(account_id, role_id) VALUES " + "(:account_id, :role_id)"; + st.exchange(soci::use(account_id)); + st.exchange(soci::use(role_name)); + auto message_gen = [&] { + return (boost::format("failed to insert account role, account: '%s', " + "role name: '%s'") + % account_id % role_name) + .str(); + }; + return makeCommandResult(st, "AppendRole", message_gen); + } + + CommandResult PostgresCommandExecutor::operator()( + const shared_model::interface::CreateAccount &command) { + auto &account_name = command.accountName(); + auto &domain_id = command.domainId(); + auto &pubkey = command.pubkey().hex(); + std::string account_id = account_name + "@" + domain_id; + soci::statement st = sql_.prepare << + R"( + WITH get_domain_default_role AS (SELECT default_role FROM domain + WHERE domain_id = :domain_id), + insert_signatory AS + ( + INSERT INTO signatory(public_key) + ( + SELECT :pk WHERE EXISTS + (SELECT * FROM get_domain_default_role) + ) ON CONFLICT DO NOTHING RETURNING (1) + ), + has_signatory AS (SELECT * FROM signatory WHERE public_key = :pk), + insert_account AS + ( + INSERT INTO account(account_id, domain_id, quorum, data) + ( + SELECT :account_id, :domain_id, 1, '{}' WHERE (EXISTS + (SELECT * FROM insert_signatory) OR EXISTS + (SELECT * FROM has_signatory) + ) AND EXISTS (SELECT * FROM get_domain_default_role) + ) RETURNING (1) + ), + insert_account_signatory AS + ( + INSERT INTO account_has_signatory(account_id, public_key) + ( + SELECT :account_id, :pk WHERE + EXISTS (SELECT * FROM insert_account) + ) + RETURNING (1) + ), + insert_account_role AS + ( + INSERT INTO account_has_roles(account_id, role_id) + ( + SELECT :account_id, default_role FROM get_domain_default_role + WHERE EXISTS (SELECT * FROM get_domain_default_role) + AND EXISTS (SELECT * FROM insert_account_signatory) + ) RETURNING (1) + ) + SELECT CASE + WHEN EXISTS (SELECT * FROM insert_account_role) THEN 0 + WHEN NOT EXISTS (SELECT * FROM account + WHERE account_id = :account_id) THEN 1 + WHEN NOT EXISTS (SELECT * FROM account_has_signatory + WHERE account_id = :account_id + AND public_key = :pk) THEN 2 + WHEN NOT EXISTS (SELECT * FROM account_has_roles + WHERE account_id = account_id AND role_id = ( + SELECT default_role FROM get_domain_default_role) + ) THEN 3 + ELSE 4 + END AS result +)"; + st.exchange(soci::use(account_id, "account_id")); + st.exchange(soci::use(domain_id, "domain_id")); + st.exchange(soci::use(pubkey, "pk")); + std::vector> message_gen = { + [&] { + return (boost::format("failed to insert account, " + "account id: '%s', " + "domain id: '%s', " + "quorum: '1', " + "json_data: {}") + % account_id % domain_id) + .str(); + }, + [&] { + return (boost::format("failed to insert account signatory, " + "account id: " + "'%s', signatory hex string: '%s") + % account_id % pubkey) + .str(); + }, + [&] { + return (boost::format( + "failed to insert account role, account: '%s' " + "with default domain role name for domain: " + "'%s'") + % account_id % domain_id) + .str(); + }, + }; + return makeCommandResultByReturnedValue(st, "CreateAccount", message_gen); + } + + CommandResult PostgresCommandExecutor::operator()( + const shared_model::interface::CreateAsset &command) { + auto &domain_id = command.domainId(); + auto asset_id = command.assetName() + "#" + domain_id; + auto precision = command.precision(); + soci::statement st = sql_.prepare + << "INSERT INTO asset(asset_id, domain_id, \"precision\", data) " + "VALUES (:id, :domain_id, :precision, NULL)"; + st.exchange(soci::use(asset_id)); + st.exchange(soci::use(domain_id)); + st.exchange(soci::use(precision)); + auto message_gen = [&] { + return (boost::format("failed to insert asset, asset id: '%s', " + "domain id: '%s', precision: %d") + % asset_id % domain_id % precision) + .str(); + }; + return makeCommandResult(st, "CreateAsset", message_gen); + } + + CommandResult PostgresCommandExecutor::operator()( + const shared_model::interface::CreateDomain &command) { + auto &domain_id = command.domainId(); + auto &default_role = command.userDefaultRole(); + soci::statement st = sql_.prepare + << "INSERT INTO domain(domain_id, default_role) VALUES (:id, " + ":role)"; + st.exchange(soci::use(domain_id)); + st.exchange(soci::use(default_role)); + auto message_gen = [&] { + return (boost::format("failed to insert domain, domain id: '%s', " + "default role: '%s'") + % domain_id % default_role) + .str(); + }; + return makeCommandResult(st, "CreateDomain", message_gen); + } + + CommandResult PostgresCommandExecutor::operator()( + const shared_model::interface::CreateRole &command) { + auto &role_id = command.roleName(); + auto &permissions = command.rolePermissions(); + auto perm_str = permissions.toBitstring(); + soci::statement st = sql_.prepare << + R"( + WITH insert_role AS (INSERT INTO role(role_id) + VALUES (:role_id) RETURNING (1)), + insert_role_permissions AS + ( + INSERT INTO role_has_permissions(role_id, permission) + ( + SELECT :role_id, :perms WHERE EXISTS + (SELECT * FROM insert_role) + ) RETURNING (1) + ) + SELECT CASE + WHEN EXISTS (SELECT * FROM insert_role_permissions) THEN 0 + WHEN EXISTS (SELECT * FROM role WHERE role_id = :role_id) THEN 1 + ELSE 2 + END AS result +)"; + st.exchange(soci::use(role_id, "role_id")); + st.exchange(soci::use(perm_str, "perms")); + + std::vector> message_gen = { + [&] { + // TODO(@l4l) 26/06/18 need to be simplified at IR-1479 + const auto &str = + shared_model::proto::permissions::toString(permissions); + const auto perm_debug_str = + std::accumulate(str.begin(), str.end(), std::string()); + return (boost::format("failed to insert role permissions, role " + "id: '%s', permissions: [%s]") + % role_id % perm_debug_str) + .str(); + }, + [&] { + return (boost::format("failed to insert role: '%s'") % role_id) + .str(); + }, + }; + return makeCommandResultByReturnedValue(st, "CreateRole", message_gen); + } + + CommandResult PostgresCommandExecutor::operator()( + const shared_model::interface::DetachRole &command) { + auto &account_id = command.accountId(); + auto &role_name = command.roleName(); + soci::statement st = sql_.prepare + << "DELETE FROM account_has_roles WHERE account_id=:account_id " + "AND role_id=:role_id"; + st.exchange(soci::use(account_id)); + st.exchange(soci::use(role_name)); + auto message_gen = [&] { + return (boost::format( + "failed to delete account role, account id: '%s', " + "role name: '%s'") + % account_id % role_name) + .str(); + }; + return makeCommandResult(st, "DetachRole", message_gen); + } + + CommandResult PostgresCommandExecutor::operator()( + const shared_model::interface::GrantPermission &command) { + auto &permittee_account_id = command.accountId(); + auto &account_id = creator_account_id_; + auto permission = command.permissionName(); + const auto perm_str = + shared_model::interface::GrantablePermissionSet({permission}) + .toBitstring(); + soci::statement st = sql_.prepare + << "INSERT INTO account_has_grantable_permissions as " + "has_perm(permittee_account_id, account_id, permission) VALUES " + "(:permittee_account_id, :account_id, :perms) ON CONFLICT " + "(permittee_account_id, account_id) " + // SELECT will end up with a error, if the permission exists + "DO UPDATE SET permission=(SELECT has_perm.permission | :perms " + "WHERE (has_perm.permission & :perms) <> :perms);"; + st.exchange(soci::use(permittee_account_id, "permittee_account_id")); + st.exchange(soci::use(account_id, "account_id")); + st.exchange(soci::use(perm_str, "perms")); + auto message_gen = [&] { + return (boost::format("failed to insert account grantable permission, " + "permittee account id: '%s', " + "account id: '%s', " + "permission: '%s'") + % permittee_account_id + % account_id + // TODO(@l4l) 26/06/18 need to be simplified at IR-1479 + % shared_model::proto::permissions::toString(permission)) + .str(); + }; + + return makeCommandResult(st, "GrantPermission", message_gen); + } + + CommandResult PostgresCommandExecutor::operator()( + const shared_model::interface::RemoveSignatory &command) { + auto &account_id = command.accountId(); + auto &pubkey = command.pubkey().hex(); + soci::statement st = sql_.prepare << + R"( + WITH delete_account_signatory AS (DELETE FROM account_has_signatory + WHERE account_id = :account_id + AND public_key = :pk RETURNING (1)), + delete_signatory AS + ( + DELETE FROM signatory WHERE public_key = :pk AND + NOT EXISTS (SELECT 1 FROM account_has_signatory + WHERE public_key = :pk) + AND NOT EXISTS (SELECT 1 FROM peer WHERE public_key = :pk) + RETURNING (1) + ) + SELECT CASE + WHEN EXISTS (SELECT * FROM delete_account_signatory) THEN + CASE + WHEN EXISTS (SELECT * FROM delete_signatory) THEN 0 + WHEN EXISTS (SELECT 1 FROM account_has_signatory + WHERE public_key = :pk) THEN 0 + WHEN EXISTS (SELECT 1 FROM peer + WHERE public_key = :pk) THEN 0 + ELSE 2 + END + ELSE 1 + END AS result +)"; + st.exchange(soci::use(account_id, "account_id")); + st.exchange(soci::use(pubkey, "pk")); + std::vector> message_gen = { + [&] { + return (boost::format( + "failed to delete account signatory, account id: " + "'%s', signatory hex string: '%s'") + % account_id % pubkey) + .str(); + }, + [&] { + return (boost::format("failed to delete signatory, " + "signatory hex string: '%s'") + % pubkey) + .str(); + }, + }; + return makeCommandResultByReturnedValue( + st, "RemoveSignatory", message_gen); + } + + CommandResult PostgresCommandExecutor::operator()( + const shared_model::interface::RevokePermission &command) { + auto &permittee_account_id = command.accountId(); + auto &account_id = creator_account_id_; + auto permission = command.permissionName(); + const auto without_perm_str = + shared_model::interface::GrantablePermissionSet() + .set() + .unset(permission) + .toBitstring(); + const auto perms = shared_model::interface::GrantablePermissionSet() + .set(permission) + .toBitstring(); + soci::statement st = sql_.prepare + << "UPDATE account_has_grantable_permissions as has_perm " + // SELECT will end up with a error, if the permission + // doesn't exists + "SET permission=(SELECT has_perm.permission & :without_perm " + "WHERE has_perm.permission & :perm = :perm AND " + "has_perm.permittee_account_id=:permittee_account_id AND " + "has_perm.account_id=:account_id) WHERE " + "permittee_account_id=:permittee_account_id AND " + "account_id=:account_id"; + st.exchange(soci::use(permittee_account_id, "permittee_account_id")); + st.exchange(soci::use(account_id, "account_id")); + st.exchange(soci::use(without_perm_str, "without_perm")); + st.exchange(soci::use(perms, "perm")); + auto message_gen = [&] { + return (boost::format("failed to delete account grantable permission, " + "permittee account id: '%s', " + "account id: '%s', " + "permission id: '%s'") + % permittee_account_id + % account_id + // TODO(@l4l) 26/06/18 need to be simplified at IR-1479 + % shared_model::proto::permissions::toString(permission)) + .str(); + }; + return makeCommandResult(st, "RevokePermission", message_gen); + } + + CommandResult PostgresCommandExecutor::operator()( + const shared_model::interface::SetAccountDetail &command) { + auto &account_id = command.accountId(); + auto &key = command.key(); + auto &value = command.value(); + if (creator_account_id_.empty()) { + // When creator is not known, it is genesis block + creator_account_id_ = "genesis"; + } + std::string json = "{" + creator_account_id_ + "}"; + std::string empty_json = "{}"; + std::string filled_json = "{" + creator_account_id_ + ", " + key + "}"; + std::string val = "\"" + value + "\""; + soci::statement st = sql_.prepare + << "UPDATE account SET data = jsonb_set(" + "CASE WHEN data ?:creator_account_id THEN data ELSE " + "jsonb_set(data, :json, :empty_json) END, " + " :filled_json, :val) WHERE account_id=:account_id"; + st.exchange(soci::use(creator_account_id_)); + st.exchange(soci::use(json)); + st.exchange(soci::use(empty_json)); + st.exchange(soci::use(filled_json)); + st.exchange(soci::use(val)); + st.exchange(soci::use(account_id)); + auto message_gen = [&] { + return (boost::format( + "failed to set account key-value, account id: '%s', " + "creator account id: '%s',\n key: '%s', value: '%s'") + % account_id % creator_account_id_ % key % value) + .str(); + }; + return makeCommandResult(st, "SetAccountDetail", message_gen); + } + + CommandResult PostgresCommandExecutor::operator()( + const shared_model::interface::SetQuorum &command) { + auto &account_id = command.accountId(); + auto quorum = command.newQuorum(); + soci::statement st = sql_.prepare + << "UPDATE account SET quorum=:quorum WHERE account_id=:account_id"; + st.exchange(soci::use(quorum)); + st.exchange(soci::use(account_id)); + auto message_gen = [&] { + return (boost::format( + "failed to update account, account id: '%s', quorum: '%s'") + % account_id % quorum) + .str(); + }; + return makeCommandResult(st, "SetQuorum", message_gen); + } + + CommandResult PostgresCommandExecutor::operator()( + const shared_model::interface::SubtractAssetQuantity &command) { + auto &account_id = creator_account_id_; + auto &asset_id = command.assetId(); + auto amount = command.amount().toStringRepr(); + uint32_t precision = command.amount().precision(); + soci::statement st = sql_.prepare << + // clang-format off + R"( + WITH has_account AS (SELECT account_id FROM account + WHERE account_id = :account_id LIMIT 1), + has_asset AS (SELECT asset_id FROM asset + WHERE asset_id = :asset_id + AND precision >= :precision LIMIT 1), + amount AS (SELECT amount FROM account_has_asset + WHERE asset_id = :asset_id + AND account_id = :account_id LIMIT 1), + new_value AS (SELECT + (SELECT + CASE WHEN EXISTS + (SELECT amount FROM amount LIMIT 1) + THEN (SELECT amount FROM amount LIMIT 1) + ELSE 0::decimal + END) - :value::decimal AS value + ), + inserted AS + ( + INSERT INTO account_has_asset(account_id, asset_id, amount) + ( + SELECT :account_id, :asset_id, value FROM new_value + WHERE EXISTS (SELECT * FROM has_account LIMIT 1) AND + EXISTS (SELECT * FROM has_asset LIMIT 1) AND + EXISTS (SELECT value FROM new_value WHERE value >= 0 LIMIT 1) + ) + ON CONFLICT (account_id, asset_id) + DO UPDATE SET amount = EXCLUDED.amount + RETURNING (1) + ) + SELECT CASE + WHEN EXISTS (SELECT * FROM inserted LIMIT 1) THEN 0 + WHEN NOT EXISTS (SELECT * FROM has_account LIMIT 1) THEN 1 + WHEN NOT EXISTS (SELECT * FROM has_asset LIMIT 1) THEN 2 + WHEN NOT EXISTS + (SELECT value FROM new_value WHERE value >= 0 LIMIT 1) THEN 3 + ELSE 4 + END AS result;)"; + // clang-format on + st.exchange(soci::use(account_id, "account_id")); + st.exchange(soci::use(asset_id, "asset_id")); + st.exchange(soci::use(amount, "value")); + st.exchange(soci::use(precision, "precision")); + + std::vector> message_gen = { + [&] { return "Account does not exist with given precision"; }, + [&] { return "Asset with given precision does not exist"; }, + [&] { return "Subtracts overdrafts account asset"; }, + }; + return makeCommandResultByReturnedValue( + st, "SubtractAssetQuantity", message_gen); + } + + CommandResult PostgresCommandExecutor::operator()( + const shared_model::interface::TransferAsset &command) { + auto &src_account_id = command.srcAccountId(); + auto &dest_account_id = command.destAccountId(); + auto &asset_id = command.assetId(); + auto amount = command.amount().toStringRepr(); + uint32_t precision = command.amount().precision(); + soci::statement st = sql_.prepare << + // clang-format off + R"( + WITH has_src_account AS (SELECT account_id FROM account + WHERE account_id = :src_account_id LIMIT 1), + has_dest_account AS (SELECT account_id FROM account + WHERE account_id = :dest_account_id + LIMIT 1), + has_asset AS (SELECT asset_id FROM asset + WHERE asset_id = :asset_id AND + precision >= :precision LIMIT 1), + src_amount AS (SELECT amount FROM account_has_asset + WHERE asset_id = :asset_id AND + account_id = :src_account_id LIMIT 1), + dest_amount AS (SELECT amount FROM account_has_asset + WHERE asset_id = :asset_id AND + account_id = :dest_account_id LIMIT 1), + new_src_value AS (SELECT + (SELECT + CASE WHEN EXISTS + (SELECT amount FROM src_amount LIMIT 1) + THEN + (SELECT amount FROM src_amount LIMIT 1) + ELSE 0::decimal + END) - :value::decimal AS value + ), + new_dest_value AS (SELECT + (SELECT :value::decimal + + CASE WHEN EXISTS + (SELECT amount FROM dest_amount LIMIT 1) + THEN + (SELECT amount FROM dest_amount LIMIT 1) + ELSE 0::decimal + END) AS value + ), + insert_src AS + ( + INSERT INTO account_has_asset(account_id, asset_id, amount) + ( + SELECT :src_account_id, :asset_id, value + FROM new_src_value + WHERE EXISTS (SELECT * FROM has_src_account LIMIT 1) AND + EXISTS (SELECT * FROM has_dest_account LIMIT 1) AND + EXISTS (SELECT * FROM has_asset LIMIT 1) AND + EXISTS (SELECT value FROM new_src_value + WHERE value >= 0 LIMIT 1) + ) + ON CONFLICT (account_id, asset_id) + DO UPDATE SET amount = EXCLUDED.amount + RETURNING (1) + ), + insert_dest AS + ( + INSERT INTO account_has_asset(account_id, asset_id, amount) + ( + SELECT :dest_account_id, :asset_id, value + FROM new_dest_value + WHERE EXISTS (SELECT * FROM insert_src) AND + EXISTS (SELECT * FROM has_src_account LIMIT 1) AND + EXISTS (SELECT * FROM has_dest_account LIMIT 1) AND + EXISTS (SELECT * FROM has_asset LIMIT 1) AND + EXISTS (SELECT value FROM new_dest_value + WHERE value < 2::decimal ^ (256 - :precision) + LIMIT 1) + ) + ON CONFLICT (account_id, asset_id) + DO UPDATE SET amount = EXCLUDED.amount + RETURNING (1) + ) + SELECT CASE + WHEN EXISTS (SELECT * FROM insert_dest LIMIT 1) THEN 0 + WHEN NOT EXISTS (SELECT * FROM has_dest_account LIMIT 1) THEN 1 + WHEN NOT EXISTS (SELECT * FROM has_src_account LIMIT 1) THEN 2 + WHEN NOT EXISTS (SELECT * FROM has_asset LIMIT 1) THEN 3 + WHEN NOT EXISTS (SELECT value FROM new_src_value + WHERE value >= 0 LIMIT 1) THEN 4 + WHEN NOT EXISTS (SELECT value FROM new_dest_value + WHERE value < 2::decimal ^ (256 - :precision) + LIMIT 1) THEN 5 + ELSE 6 + END AS result;)"; + // clang-format on + st.exchange(soci::use(src_account_id, "src_account_id")); + st.exchange(soci::use(dest_account_id, "dest_account_id")); + st.exchange(soci::use(asset_id, "asset_id")); + st.exchange(soci::use(amount, "value")); + st.exchange(soci::use(precision, "precision")); + std::vector> message_gen = { + [&] { return "Destination account does not exist"; }, + [&] { return "Source account does not exist"; }, + [&] { return "Asset with given precision does not exist"; }, + [&] { return "Transfer overdrafts source account asset"; }, + [&] { return "Transfer overflows destanation account asset"; }, + }; + return makeCommandResultByReturnedValue(st, "TransferAsset", message_gen); + } + } // namespace ametsuchi +} // namespace iroha diff --git a/irohad/ametsuchi/impl/postgres_command_executor.hpp b/irohad/ametsuchi/impl/postgres_command_executor.hpp new file mode 100644 index 0000000000..79ad1522b1 --- /dev/null +++ b/irohad/ametsuchi/impl/postgres_command_executor.hpp @@ -0,0 +1,81 @@ +/** + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef IROHA_POSTGRES_COMMAND_EXECUTOR_HPP +#define IROHA_POSTGRES_COMMAND_EXECUTOR_HPP + +#include "ametsuchi/command_executor.hpp" + +#include "ametsuchi/impl/postgres_wsv_common.hpp" + +namespace iroha { + namespace ametsuchi { + + class PostgresCommandExecutor : public CommandExecutor { + public: + explicit PostgresCommandExecutor(soci::session &transaction); + + void setCreatorAccountId( + const shared_model::interface::types::AccountIdType + &creator_account_id) override; + + CommandResult operator()( + const shared_model::interface::AddAssetQuantity &command) override; + + CommandResult operator()( + const shared_model::interface::AddPeer &command) override; + + CommandResult operator()( + const shared_model::interface::AddSignatory &command) override; + + CommandResult operator()( + const shared_model::interface::AppendRole &command) override; + + CommandResult operator()( + const shared_model::interface::CreateAccount &command) override; + + CommandResult operator()( + const shared_model::interface::CreateAsset &command) override; + + CommandResult operator()( + const shared_model::interface::CreateDomain &command) override; + + CommandResult operator()( + const shared_model::interface::CreateRole &command) override; + + CommandResult operator()( + const shared_model::interface::DetachRole &command) override; + + CommandResult operator()( + const shared_model::interface::GrantPermission &command) override; + + CommandResult operator()( + const shared_model::interface::RemoveSignatory &command) override; + + CommandResult operator()( + const shared_model::interface::RevokePermission &command) override; + + CommandResult operator()( + const shared_model::interface::SetAccountDetail &command) override; + + CommandResult operator()( + const shared_model::interface::SetQuorum &command) override; + + CommandResult operator()( + const shared_model::interface::SubtractAssetQuantity &command) + override; + + CommandResult operator()( + const shared_model::interface::TransferAsset &command) override; + + private: + soci::session &sql_; + + shared_model::interface::types::AccountIdType creator_account_id_; + }; + } // namespace ametsuchi +} // namespace iroha + +#endif // IROHA_POSTGRES_COMMAND_EXECUTOR_HPP diff --git a/irohad/ametsuchi/impl/temporary_wsv_impl.cpp b/irohad/ametsuchi/impl/temporary_wsv_impl.cpp index bbc5fa72bd..2ccb61b69b 100644 --- a/irohad/ametsuchi/impl/temporary_wsv_impl.cpp +++ b/irohad/ametsuchi/impl/temporary_wsv_impl.cpp @@ -17,6 +17,7 @@ #include "ametsuchi/impl/temporary_wsv_impl.hpp" +#include "ametsuchi/impl/postgres_command_executor.hpp" #include "ametsuchi/impl/postgres_wsv_command.hpp" #include "ametsuchi/impl/postgres_wsv_query.hpp" @@ -28,7 +29,7 @@ namespace iroha { : sql_(std::move(sql)), wsv_(std::make_shared(*sql_, factory)), executor_(std::make_shared(*sql_)), - command_executor_(std::make_shared(wsv_, executor_)), + command_executor_(std::make_shared(*sql_)), command_validator_(std::make_shared(wsv_)), log_(logger::log("TemporaryWSV")) { *sql_ << "BEGIN"; diff --git a/irohad/ametsuchi/wsv_command.hpp b/irohad/ametsuchi/wsv_command.hpp index 8a2afa1103..d34f899db1 100644 --- a/irohad/ametsuchi/wsv_command.hpp +++ b/irohad/ametsuchi/wsv_command.hpp @@ -232,6 +232,7 @@ namespace iroha { */ virtual WsvCommandResult insertDomain( const shared_model::interface::Domain &domain) = 0; + }; } // namespace ametsuchi diff --git a/irohad/execution/command_executor.hpp b/irohad/execution/command_executor.hpp index a8f70dbdf5..e536ea180d 100644 --- a/irohad/execution/command_executor.hpp +++ b/irohad/execution/command_executor.hpp @@ -21,6 +21,7 @@ #include #include +#include "ametsuchi/command_executor.hpp" #include "ametsuchi/wsv_command.hpp" #include "ametsuchi/wsv_query.hpp" #include "builders/default_builders.hpp" @@ -44,18 +45,7 @@ namespace iroha { - /** - * Error for command execution or validation - * Contains command name, as well as an error message - */ - struct CommandError { - std::string command_name; - std::string error_message; - - std::string toString() const { - return (boost::format("%s: %s") % command_name % error_message).str(); - } - }; + using CommandError = ametsuchi::CommandError; /** * CommandResult is a return type of all execute and validate functions. @@ -71,70 +61,6 @@ namespace iroha { */ using CommandResult = iroha::expected::Result; - class CommandExecutor : public boost::static_visitor { - public: - CommandExecutor(std::shared_ptr queries, - std::shared_ptr commands); - - CommandResult operator()( - const shared_model::interface::AddAssetQuantity &command); - - CommandResult operator()(const shared_model::interface::AddPeer &command); - - CommandResult operator()( - const shared_model::interface::AddSignatory &command); - - CommandResult operator()( - const shared_model::interface::AppendRole &command); - - CommandResult operator()( - const shared_model::interface::CreateAccount &command); - - CommandResult operator()( - const shared_model::interface::CreateAsset &command); - - CommandResult operator()( - const shared_model::interface::CreateDomain &command); - - CommandResult operator()( - const shared_model::interface::CreateRole &command); - - CommandResult operator()( - const shared_model::interface::DetachRole &command); - - CommandResult operator()( - const shared_model::interface::GrantPermission &command); - - CommandResult operator()( - const shared_model::interface::RemoveSignatory &command); - - CommandResult operator()( - const shared_model::interface::RevokePermission &command); - - CommandResult operator()( - const shared_model::interface::SetAccountDetail &command); - - CommandResult operator()(const shared_model::interface::SetQuorum &command); - - CommandResult operator()( - const shared_model::interface::SubtractAssetQuantity &command); - - CommandResult operator()( - const shared_model::interface::TransferAsset &command); - - void setCreatorAccountId(const shared_model::interface::types::AccountIdType - &creator_account_id); - - private: - std::shared_ptr queries; - std::shared_ptr commands; - shared_model::interface::types::AccountIdType creator_account_id; - - shared_model::builder::DefaultAccountAssetBuilder account_asset_builder_; - shared_model::builder::DefaultAccountBuilder account_builder_; - shared_model::builder::DefaultAssetBuilder asset_builder_; - shared_model::builder::DefaultDomainBuilder domain_builder_; - }; class CommandValidator : public boost::static_visitor { public: diff --git a/irohad/execution/impl/command_executor.cpp b/irohad/execution/impl/command_executor.cpp index d78ba028f7..3203bcac6a 100644 --- a/irohad/execution/impl/command_executor.cpp +++ b/irohad/execution/impl/command_executor.cpp @@ -50,426 +50,6 @@ namespace iroha { }); } - CommandExecutor::CommandExecutor( - std::shared_ptr queries, - std::shared_ptr commands) - : queries(queries), commands(commands) {} - - void CommandExecutor::setCreatorAccountId( - const shared_model::interface::types::AccountIdType &creator_account_id) { - this->creator_account_id = creator_account_id; - } - - CommandResult CommandExecutor::operator()( - const shared_model::interface::AddAssetQuantity &command) { - std::string command_name = "AddAssetQuantity"; - auto asset = queries->getAsset(command.assetId()); - if (not asset) { - return makeCommandError( - (boost::format("asset %s is absent") % command.assetId()).str(), - command_name); - } - - auto precision = asset.value()->precision(); - if (command.amount().precision() > precision) { - return makeCommandError( - (boost::format("command precision is greater than asset precision: " - "expected %d, but got %d") - % precision % command.amount().precision()) - .str(), - command_name); - } - auto command_amount = - makeAmountWithPrecision(command.amount(), asset.value()->precision()); - if (not queries->getAccount(creator_account_id)) { - return makeCommandError( - (boost::format("account %s is absent") % creator_account_id).str(), - command_name); - } - auto account_asset = - queries->getAccountAsset(creator_account_id, command.assetId()); - - using AccountAssetResult = - expected::Result, - iroha::CommandError>; - auto account_asset_new = command_amount.match( - [this, &account_asset, &command_name, &command]( - const expected::Value< - std::shared_ptr> - &new_balance_val) -> AccountAssetResult { - expected::PolymorphicResult - result; - if (account_asset) { - result = (*new_balance_val.value + account_asset.value()->balance()) - | [this, &command](const auto &balance) { - return account_asset_builder_.balance(*balance) - .accountId(creator_account_id) - .assetId(command.assetId()) - .build(); - }; - } else { - result = account_asset_builder_.balance(*new_balance_val.value) - .accountId(creator_account_id) - .assetId(command.assetId()) - .build(); - } - return result.match( - [](expected::Value< - std::shared_ptr> - &new_account_asset_val) -> AccountAssetResult { - return expected::makeValue(new_account_asset_val.value); - }, - [&command_name](const auto &error) -> AccountAssetResult { - return makeCommandError(*error.error, command_name); - }); - }, - [&command_name](const auto &error) -> AccountAssetResult { - return makeCommandError( - "amount builder failed. reason " + *error.error, command_name); - }); - - return account_asset_new.match( - [&](const expected::Value< - std::shared_ptr> - &account_asset_new_val) -> CommandResult { - return makeCommandResult( - commands->upsertAccountAsset(*account_asset_new_val.value), - command_name); - }, - [&command_name](const auto &account_asset_error) -> CommandResult { - return makeCommandError("account asset builder failed. reason " - + account_asset_error.error.toString(), - command_name); - }); - } - - CommandResult CommandExecutor::operator()( - const shared_model::interface::AddPeer &command) { - return makeCommandResult(commands->insertPeer(command.peer()), "AddPeer"); - } - - CommandResult CommandExecutor::operator()( - const shared_model::interface::AddSignatory &command) { - auto result = commands->insertSignatory(command.pubkey()) | [&] { - return commands->insertAccountSignatory(command.accountId(), - command.pubkey()); - }; - return makeCommandResult(result, "AddSignatory"); - } - - CommandResult CommandExecutor::operator()( - const shared_model::interface::AppendRole &command) { - return makeCommandResult( - commands->insertAccountRole(command.accountId(), command.roleName()), - "AppendRole"); - } - - CommandResult CommandExecutor::operator()( - const shared_model::interface::CreateAccount &command) { - std::string command_name = "CreateAccount"; - auto account = - account_builder_ - .accountId(command.accountName() + "@" + command.domainId()) - .domainId(command.domainId()) - .quorum(1) - .jsonData("{}") - .build(); - return account.match( - [&](const expected::Value< - std::shared_ptr> &account_val) - -> CommandResult { - auto domain = queries->getDomain(command.domainId()); - if (not domain) { - return makeCommandError( - (boost::format("Domain %s not found") % command.domainId()) - .str(), - command_name); - } - std::string domain_default_role = domain.value()->defaultRole(); - // Account must have unique initial pubkey - auto result = commands->insertSignatory(command.pubkey()) | [&] { - return commands->insertAccount(*account_val.value); - } | [&] { - return commands->insertAccountSignatory( - (*account_val.value).accountId(), command.pubkey()); - } | [&] { - return commands->insertAccountRole((*account_val.value).accountId(), - domain_default_role); - }; - return makeCommandResult(result, command_name); - }, - [&command_name](const auto &error) -> CommandResult { - return makeCommandError( - "account builder failed. reason " + *error.error, command_name); - }); - } - - CommandResult CommandExecutor::operator()( - const shared_model::interface::CreateAsset &command) { - std::string command_name = "CreateAsset"; - auto new_asset = - asset_builder_.assetId(command.assetName() + "#" + command.domainId()) - .domainId(command.domainId()) - .precision(command.precision()) - .build(); - return new_asset.match( - [&](const expected::Value< - std::shared_ptr> &new_asset_val) - -> CommandResult { - // The insert will fail if asset already exists - return makeCommandResult(commands->insertAsset(*new_asset_val.value), - command_name); - }, - [&command_name](const auto &error) -> CommandResult { - return makeCommandError( - "asset builder failed. reason " + *error.error, command_name); - }); - } - - CommandResult CommandExecutor::operator()( - const shared_model::interface::CreateDomain &command) { - std::string command_name = "CreateDomain"; - auto new_domain = domain_builder_.domainId(command.domainId()) - .defaultRole(command.userDefaultRole()) - .build(); - return new_domain.match( - [&](const expected::Value< - std::shared_ptr> &new_domain_val) - -> CommandResult { - // The insert will fail if domain already exist - return makeCommandResult( - commands->insertDomain(*new_domain_val.value), command_name); - }, - [&command_name](const auto &error) -> CommandResult { - return makeCommandError( - "domain builder failed. reason " + *error.error, command_name); - }); - } - - CommandResult CommandExecutor::operator()( - const shared_model::interface::CreateRole &command) { - std::string command_name = "CreateRole"; - auto result = commands->insertRole(command.roleName()) | [&] { - return commands->insertRolePermissions(command.roleName(), - command.rolePermissions()); - }; - return makeCommandResult(result, command_name); - } - - CommandResult CommandExecutor::operator()( - const shared_model::interface::DetachRole &command) { - return makeCommandResult( - commands->deleteAccountRole(command.accountId(), command.roleName()), - "DetachRole"); - } - - CommandResult CommandExecutor::operator()( - const shared_model::interface::GrantPermission &command) { - return makeCommandResult( - commands->insertAccountGrantablePermission( - command.accountId(), creator_account_id, command.permissionName()), - "GrantPermission"); - } - - CommandResult CommandExecutor::operator()( - const shared_model::interface::RemoveSignatory &command) { - std::string command_name = "RemoveSignatory"; - - // Delete will fail if account signatory doesn't exist - auto result = - commands->deleteAccountSignatory(command.accountId(), command.pubkey()) - | [&] { return commands->deleteSignatory(command.pubkey()); }; - return makeCommandResult(result, command_name); - } - - CommandResult CommandExecutor::operator()( - const shared_model::interface::RevokePermission &command) { - return makeCommandResult( - commands->deleteAccountGrantablePermission( - command.accountId(), creator_account_id, command.permissionName()), - "RevokePermission"); - } - - CommandResult CommandExecutor::operator()( - const shared_model::interface::SetAccountDetail &command) { - auto creator = creator_account_id; - if (creator_account_id.empty()) { - // When creator is not known, it is genesis block - creator = "genesis"; - } - return makeCommandResult( - commands->setAccountKV( - command.accountId(), creator, command.key(), command.value()), - "SetAccountDetail"); - } - - CommandResult CommandExecutor::operator()( - const shared_model::interface::SetQuorum &command) { - std::string command_name = "SetQuorum"; - - auto account = queries->getAccount(command.accountId()); - if (not account) { - return makeCommandError( - (boost::format("absent account %s") % command.accountId()).str(), - command_name); - } - auto account_new = account_builder_.domainId(account.value()->domainId()) - .accountId(account.value()->accountId()) - .jsonData(account.value()->jsonData()) - .quorum(command.newQuorum()) - .build(); - - return account_new.match( - [&](const expected::Value< - std::shared_ptr> &account_new_val) - -> CommandResult { - return makeCommandResult( - commands->updateAccount(*account_new_val.value), command_name); - }, - [&command_name](const auto &error) -> CommandResult { - return makeCommandError( - "account builder failed. reason " + *error.error, command_name); - }); - } - - CommandResult CommandExecutor::operator()( - const shared_model::interface::SubtractAssetQuantity &command) { - std::string command_name = "SubtractAssetQuantity"; - auto asset = queries->getAsset(command.assetId()); - if (not asset) { - return makeCommandError( - (boost::format("asset %s is absent") % command.assetId()).str(), - command_name); - } - auto precision = asset.value()->precision(); - if (command.amount().precision() > precision) { - return makeCommandError( - (boost::format("command precision is greater than asset precision: " - "expected %d, but got %d") - % precision % command.amount().precision()) - .str(), - command_name); - } - auto command_amount = - makeAmountWithPrecision(command.amount(), asset.value()->precision()); - auto account_asset = - queries->getAccountAsset(creator_account_id, command.assetId()); - if (not account_asset) { - return makeCommandError((boost::format("%s do not have %s") - % creator_account_id % command.assetId()) - .str(), - command_name); - } - auto account_asset_new = command_amount | - [&account_asset](const auto &amount) { - return account_asset.value()->balance() - *amount; - } - | [this, &account_asset](const auto &new_balance) { - return account_asset_builder_.balance(*new_balance) - .accountId(account_asset.value()->accountId()) - .assetId(account_asset.value()->assetId()) - .build(); - }; - - return account_asset_new.match( - [&](const expected::Value< - std::shared_ptr> - &account_asset_new_val) -> CommandResult { - return makeCommandResult( - commands->upsertAccountAsset(*account_asset_new_val.value), - command_name); - }, - [&command_name](const auto &error) -> CommandResult { - return makeCommandError( - "account asset builder failed. reason " + *error.error, - command_name); - }); - } - - CommandResult CommandExecutor::operator()( - const shared_model::interface::TransferAsset &command) { - std::string command_name = "TransferAsset"; - - auto src_account_asset = - queries->getAccountAsset(command.srcAccountId(), command.assetId()); - if (not src_account_asset) { - return makeCommandError((boost::format("asset %s is absent of %s") - % command.assetId() % command.srcAccountId()) - .str(), - command_name); - } - auto dest_account_asset = - queries->getAccountAsset(command.destAccountId(), command.assetId()); - auto asset = queries->getAsset(command.assetId()); - if (not asset) { - return makeCommandError((boost::format("asset %s is absent of %s") - % command.assetId() % command.destAccountId()) - .str(), - command_name); - } - auto precision = asset.value()->precision(); - if (command.amount().precision() > precision) { - return makeCommandError( - (boost::format("command precision is greater than asset precision: " - "expected %d, but got %d") - % precision % command.amount().precision()) - .str(), - command_name); - } - auto command_amount = - makeAmountWithPrecision(command.amount(), asset.value()->precision()); - // Set new balance for source account - auto src_account_asset_new = command_amount | - [&src_account_asset](const auto &amount) { - return src_account_asset.value()->balance() - *amount; - } - | [this, &src_account_asset](const auto &new_src_balance) { - return account_asset_builder_ - .assetId(src_account_asset.value()->assetId()) - .accountId(src_account_asset.value()->accountId()) - .balance(*new_src_balance) - .build(); - }; - - auto dest_account_asset_new = command_amount | [&](const auto &amount) { - static const shared_model::interface::Amount kZero("0"); - auto new_amount = - (dest_account_asset | [](const auto &ast) - -> boost::optional { - return {ast->balance()}; - }) - .get_value_or(kZero) - + *amount; - return new_amount | [this, &command](const auto &new_dest_balance) { - return account_asset_builder_.assetId(command.assetId()) - .accountId(command.destAccountId()) - .balance(*new_dest_balance) - .build(); - }; - }; - - auto map_error = [&command_name](const auto &t) { - return expected::map_error< - CommandError>(t, [&command_name](const auto &error) -> CommandError { - return {"account asset builder failed. reason " + *error, command_name}; - }); - }; - - return (map_error(src_account_asset_new) | - [&](std::shared_ptr - src_amount) -> CommandResult { - return map_error(dest_account_asset_new) | - [&](std::shared_ptr - dst_amount) -> CommandResult { - return makeCommandResult( - commands->upsertAccountAsset(*src_amount) | - [&] { return commands->upsertAccountAsset(*dst_amount); }, - command_name); - }; - }); - } - // ----------------------| Validator |---------------------- CommandValidator::CommandValidator( diff --git a/shared_model/utils/amount_utils.cpp b/shared_model/utils/amount_utils.cpp index 4a908207d3..7e3256ec07 100644 --- a/shared_model/utils/amount_utils.cpp +++ b/shared_model/utils/amount_utils.cpp @@ -55,13 +55,6 @@ namespace shared_model { std::string> operator-(const shared_model::interface::Amount &a, const shared_model::interface::Amount &b) { - // check if a greater than b - if (a.intValue() < b.intValue()) { - return iroha::expected::makeError(std::make_shared( - (boost::format("minuend is smaller than subtrahend (%s - %s)") - % a.intValue().str() % b.intValue().str()) - .str())); - } auto max_precision = std::max(a.precision(), b.precision()); auto val_a = increaseValuePrecision(a.intValue(), max_precision - a.precision()); diff --git a/test/integration/acceptance/add_asset_qty_test.cpp b/test/integration/acceptance/add_asset_qty_test.cpp index d9ed77adbb..f1eace0fff 100644 --- a/test/integration/acceptance/add_asset_qty_test.cpp +++ b/test/integration/acceptance/add_asset_qty_test.cpp @@ -97,8 +97,8 @@ TEST_F(AddAssetQuantity, ZeroAmount) { */ TEST_F(AddAssetQuantity, Uint256DestOverflow) { std::string uint256_halfmax = - "723700557733226221397318656304299424082937404160253525246609900049457060" - "2495.0"; // 2**252 - 1 + "578960446186580977117854925043439539266349923328202820197287920039565648" + "19966.0"; // 2**255 - 2 IntegrationTestFramework(1) .setInitialState(kAdminKeypair) .sendTx(makeUserWithPerms()) diff --git a/test/integration/acceptance/transfer_asset_test.cpp b/test/integration/acceptance/transfer_asset_test.cpp index 22c03bc7c7..38049d6ecb 100644 --- a/test/integration/acceptance/transfer_asset_test.cpp +++ b/test/integration/acceptance/transfer_asset_test.cpp @@ -249,8 +249,8 @@ TEST_F(TransferAsset, MoreThanHas) { */ TEST_F(TransferAsset, Uint256DestOverflow) { std::string uint256_halfmax = - "723700557733226221397318656304299424082937404160253525246609900049457060" - "2495.0"; // 2**252 - 1 + "578960446186580977117854925043439539266349923328202820197287920039565648" + "19966.0"; // 2**255 - 2 IntegrationTestFramework(1) .setInitialState(kAdminKeypair) .sendTxAwait(makeFirstUser(), check(1)) @@ -347,7 +347,7 @@ TEST_F(TransferAsset, BigPrecision) { auto check_balance = [](std::string account_id, std::string val) { return [a = std::move(account_id), - v = val + "." + std::string(kPrecision, '0')](auto &resp) { + v = val](auto &resp) { auto &acc_ast = boost::apply_visitor( framework::SpecifiedVisitor(), resp.get()); diff --git a/test/module/irohad/ametsuchi/CMakeLists.txt b/test/module/irohad/ametsuchi/CMakeLists.txt index f4045a1acb..37c021dac0 100644 --- a/test/module/irohad/ametsuchi/CMakeLists.txt +++ b/test/module/irohad/ametsuchi/CMakeLists.txt @@ -55,6 +55,13 @@ target_link_libraries(postgres_options_test libs_common ) +addtest(postgres_executor_test postgres_executor_test.cpp) +target_link_libraries(postgres_executor_test + integration_framework + ametsuchi + libs_common + ) + add_library(ametsuchi_fixture INTERFACE) target_link_libraries(ametsuchi_fixture INTERFACE integration_framework_config_helper diff --git a/test/module/irohad/ametsuchi/ametsuchi_mocks.hpp b/test/module/irohad/ametsuchi/ametsuchi_mocks.hpp index f757c9499a..691a2a26be 100644 --- a/test/module/irohad/ametsuchi/ametsuchi_mocks.hpp +++ b/test/module/irohad/ametsuchi/ametsuchi_mocks.hpp @@ -21,6 +21,7 @@ #include #include #include "ametsuchi/block_query.hpp" +#include "ametsuchi/command_executor.hpp" #include "ametsuchi/key_value_storage.hpp" #include "ametsuchi/mutable_factory.hpp" #include "ametsuchi/mutable_storage.hpp" diff --git a/test/module/irohad/ametsuchi/postgres_executor_test.cpp b/test/module/irohad/ametsuchi/postgres_executor_test.cpp new file mode 100644 index 0000000000..fe19034dbe --- /dev/null +++ b/test/module/irohad/ametsuchi/postgres_executor_test.cpp @@ -0,0 +1,864 @@ +/** + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "ametsuchi/impl/postgres_command_executor.hpp" +#include "ametsuchi/impl/postgres_wsv_query.hpp" +#include "framework/result_fixture.hpp" +#include "module/irohad/ametsuchi/ametsuchi_fixture.hpp" +#include "module/shared_model/builders/protobuf/test_account_builder.hpp" +#include "module/shared_model/builders/protobuf/test_asset_builder.hpp" +#include "module/shared_model/builders/protobuf/test_domain_builder.hpp" +#include "module/shared_model/builders/protobuf/test_peer_builder.hpp" +#include "module/shared_model/builders/protobuf/test_transaction_builder.hpp" + +namespace iroha { + namespace ametsuchi { + + using namespace framework::expected; + + class CommandExecutorTest : public AmetsuchiTest { + public: + CommandExecutorTest() { + domain = clone( + TestDomainBuilder().domainId("domain").defaultRole(role).build()); + + account = clone(TestAccountBuilder() + .domainId(domain->domainId()) + .accountId("id@" + domain->domainId()) + .quorum(1) + .jsonData(R"({"id@domain": {"key": "value"}})") + .build()); + role_permissions.set( + shared_model::interface::permissions::Role::kAddMySignatory); + grantable_permission = + shared_model::interface::permissions::Grantable::kAddMySignatory; + pubkey = std::make_unique( + std::string('1', 32)); + } + + void SetUp() override { + AmetsuchiTest::SetUp(); + sql = std::make_unique(soci::postgresql, pgopt_); + + query = std::make_unique(*sql); + executor = std::make_unique(*sql); + + *sql << init_; + } + + CommandResult execute( + const std::unique_ptr &command, + const shared_model::interface::types::AccountIdType &creator = + "id@domain") { + executor->setCreatorAccountId(creator); + return boost::apply_visitor(*executor, command->get()); + } + + // TODO 2018-04-20 Alexey Chernyshov - IR-1276 - rework function with + // CommandBuilder + /** + * Hepler function to build command and wrap it into + * std::unique_ptr<> + * @param builder command builder + * @return command + */ + std::unique_ptr buildCommand( + const TestTransactionBuilder &builder) { + return clone(builder.build().commands().front()); + } + + std::string role = "role"; + shared_model::interface::RolePermissionSet role_permissions; + shared_model::interface::permissions::Grantable grantable_permission; + std::unique_ptr account; + std::unique_ptr domain; + std::unique_ptr pubkey; + + std::unique_ptr sql; + + std::unique_ptr command; + + std::unique_ptr query; + std::unique_ptr executor; + }; + + class AddAccountAssetTest : public CommandExecutorTest { + public: + void SetUp() override { + CommandExecutorTest::SetUp(); + + ASSERT_TRUE(val(execute(buildCommand( + TestTransactionBuilder().createRole(role, role_permissions))))); + ASSERT_TRUE(val(execute(buildCommand( + TestTransactionBuilder().createDomain(domain->domainId(), role))))); + ASSERT_TRUE( + val(execute(buildCommand(TestTransactionBuilder().createAccount( + "id", domain->domainId(), *pubkey))))); + } + /** + * Add default asset and check that it is done + */ + void addAsset() { + auto asset = clone(TestAccountAssetBuilder() + .domainId(domain->domainId()) + .assetId(asset_id) + .precision(1) + .build()); + + ASSERT_TRUE( + val(execute(buildCommand(TestTransactionBuilder().createAsset( + "coin", domain->domainId(), 1))))); + } + + shared_model::interface::types::AssetIdType asset_id = + "coin#" + domain->domainId(); + }; + + /** + * @given command + * @when trying to add account asset + * @then account asset is successfully added + */ + TEST_F(AddAccountAssetTest, ValidAddAccountAssetTest) { + addAsset(); + ASSERT_TRUE(val( + execute(buildCommand(TestTransactionBuilder() + .addAssetQuantity(asset_id, "1.0") + .creatorAccountId(account->accountId()))))); + auto account_asset = + query->getAccountAsset(account->accountId(), asset_id); + ASSERT_TRUE(account_asset); + ASSERT_EQ("1.0", account_asset.get()->balance().toStringRepr()); + ASSERT_TRUE(val( + execute(buildCommand(TestTransactionBuilder() + .addAssetQuantity(asset_id, "1.0") + .creatorAccountId(account->accountId()))))); + account_asset = query->getAccountAsset(account->accountId(), asset_id); + ASSERT_TRUE(account_asset); + ASSERT_EQ("2.0", account_asset.get()->balance().toStringRepr()); + } + + /** + * @given command + * @when trying to add account asset with non-existing asset + * @then account asset fails to be added + */ + TEST_F(AddAccountAssetTest, AddAccountAssetTestInvalidAsset) { + ASSERT_TRUE(err( + execute(buildCommand(TestTransactionBuilder() + .addAssetQuantity(asset_id, "1.0") + .creatorAccountId(account->accountId()))))); + } + + /** + * @given command + * @when trying to add account asset with non-existing account + * @then account asset fails to added + */ + TEST_F(AddAccountAssetTest, AddAccountAssetTestInvalidAccount) { + addAsset(); + ASSERT_TRUE( + err(execute(buildCommand(TestTransactionBuilder() + .addAssetQuantity(asset_id, "1.0") + .creatorAccountId("some@domain")), + "some@domain"))); + } + + /** + * @given command + * @when trying to add account asset that overflows + * @then account asset fails to added + */ + TEST_F(AddAccountAssetTest, AddAccountAssetTestUint256Overflow) { + std::string uint256_halfmax = + "578960446186580977117854925043439539266349923328202820197287920039565648" + "19966.0"; // 2**255 - 2tra + addAsset(); + ASSERT_TRUE(val( + execute(buildCommand(TestTransactionBuilder() + .addAssetQuantity(asset_id, uint256_halfmax) + .creatorAccountId(account->accountId()))))); + ASSERT_TRUE(err( + execute(buildCommand(TestTransactionBuilder() + .addAssetQuantity(asset_id, uint256_halfmax) + .creatorAccountId(account->accountId()))))); + } + + class AddPeer : public CommandExecutorTest { + public: + void SetUp() override { + CommandExecutorTest::SetUp(); + peer = clone(TestPeerBuilder().build()); + } + std::unique_ptr peer; + }; + + /** + * @given command + * @when trying to add peer + * @then peer is successfully added + */ + TEST_F(AddPeer, ValidAddPeerTest) { + ASSERT_TRUE(val(execute(buildCommand( + TestTransactionBuilder().addPeer(peer->address(), peer->pubkey()))))); + } + + class AddSignatory : public CommandExecutorTest { + public: + void SetUp() override { + CommandExecutorTest::SetUp(); + ASSERT_TRUE(val(execute(buildCommand( + TestTransactionBuilder().createRole(role, role_permissions))))); + ASSERT_TRUE(val(execute(buildCommand( + TestTransactionBuilder().createDomain(domain->domainId(), role))))); + ASSERT_TRUE( + val(execute(buildCommand(TestTransactionBuilder().createAccount( + "id", + domain->domainId(), + shared_model::interface::types::PubkeyType( + std::string('5', 32))))))); + } + }; + + /** + * @given command + * @when trying to add signatory + * @then signatory is successfully added + */ + TEST_F(AddSignatory, ValidAddSignatoryTest) { + ASSERT_TRUE( + val(execute(buildCommand(TestTransactionBuilder().addSignatory( + account->accountId(), *pubkey))))); + auto signatories = query->getSignatories(account->accountId()); + ASSERT_TRUE(signatories); + ASSERT_TRUE(std::find(signatories->begin(), signatories->end(), *pubkey) + != signatories->end()); + } + + class AppendRole : public CommandExecutorTest { + public: + void SetUp() override { + CommandExecutorTest::SetUp(); + ASSERT_TRUE(val(execute(buildCommand( + TestTransactionBuilder().createRole(role, role_permissions))))); + ASSERT_TRUE(val(execute(buildCommand( + TestTransactionBuilder().createRole("role2", role_permissions))))); + ASSERT_TRUE(val(execute(buildCommand( + TestTransactionBuilder().createDomain(domain->domainId(), role))))); + ASSERT_TRUE( + val(execute(buildCommand(TestTransactionBuilder().createAccount( + "id", domain->domainId(), *pubkey))))); + } + }; + + /** + * @given command + * @when trying to append role + * @then role is successfully appended + */ + TEST_F(AppendRole, ValidAppendRoleTest) { + ASSERT_TRUE(val(execute(buildCommand(TestTransactionBuilder().appendRole( + account->accountId(), "role2"))))); + auto roles = query->getAccountRoles(account->accountId()); + ASSERT_TRUE(roles); + ASSERT_TRUE(std::find(roles->begin(), roles->end(), "role2") + != roles->end()); + } + + class CreateAccount : public CommandExecutorTest { + public: + void SetUp() override { + CommandExecutorTest::SetUp(); + account = clone(TestAccountBuilder() + .domainId(domain->domainId()) + .accountId("id@" + domain->domainId()) + .quorum(1) + .jsonData("{}") + .build()); + } + }; + + /** + * @given command and no target domain in ledger + * @when trying to create account + * @then account is not created + */ + TEST_F(CreateAccount, InvalidCreateAccountNoDomainTest) { + ASSERT_TRUE( + err(execute(buildCommand(TestTransactionBuilder().createAccount( + "id", domain->domainId(), *pubkey))))); + } + + /** + * @given command ] + * @when trying to create account + * @then account is created + */ + TEST_F(CreateAccount, ValidCreateAccountWithDomainTest) { + ASSERT_TRUE(val(execute(buildCommand( + TestTransactionBuilder().createRole(role, role_permissions))))); + ASSERT_TRUE(val(execute(buildCommand( + TestTransactionBuilder().createDomain(domain->domainId(), role))))); + ASSERT_TRUE( + val(execute(buildCommand(TestTransactionBuilder().createAccount( + "id", domain->domainId(), *pubkey))))); + auto acc = query->getAccount(account->accountId()); + ASSERT_TRUE(acc); + ASSERT_EQ(*account.get(), *acc.get()); + } + + class CreateAsset : public CommandExecutorTest { + public: + void SetUp() override { + CommandExecutorTest::SetUp(); + } + shared_model::interface::types::AssetIdType asset_name = "coin"; + shared_model::interface::types::AssetIdType asset_id = + "coin#" + domain->domainId(); + }; + + /** + * @given command and no target domain in ledger + * @when trying to create asset + * @then asset is not created + */ + TEST_F(CreateAsset, InvalidCreateAssetNoDomainTest) { + ASSERT_TRUE(err(execute(buildCommand(TestTransactionBuilder().createAsset( + asset_name, domain->domainId(), 1))))); + } + + /** + * @given command ] + * @when trying to create asset + * @then asset is created + */ + TEST_F(CreateAsset, ValidCreateAssetWithDomainTest) { + ASSERT_TRUE(val(execute(buildCommand( + TestTransactionBuilder().createRole(role, role_permissions))))); + ASSERT_TRUE(val(execute(buildCommand( + TestTransactionBuilder().createDomain(domain->domainId(), role))))); + auto asset = clone(TestAccountAssetBuilder() + .domainId(domain->domainId()) + .assetId(asset_id) + .precision(1) + .build()); + ASSERT_TRUE(val(execute(buildCommand(TestTransactionBuilder().createAsset( + "coin", domain->domainId(), 1))))); + auto ass = query->getAsset(asset->assetId()); + ASSERT_TRUE(ass); + ASSERT_EQ(*asset.get(), *ass.get()); + } + + class CreateDomain : public CommandExecutorTest { + public: + void SetUp() override { + CommandExecutorTest::SetUp(); + } + }; + + /** + * @given command when there is no role + * @when trying to create domain + * @then domain is not created + */ + TEST_F(CreateDomain, InvalidCreateDomainWhenNoRoleTest) { + ASSERT_TRUE(err(execute(buildCommand( + TestTransactionBuilder().createDomain(domain->domainId(), role))))); + } + + /** + * @given command when there is no role + * @when trying to create domain + * @then domain is not created + */ + TEST_F(CreateDomain, ValidCreateDomainTest) { + ASSERT_TRUE(val(execute(buildCommand( + TestTransactionBuilder().createRole(role, role_permissions))))); + ASSERT_TRUE(val(execute(buildCommand( + TestTransactionBuilder().createDomain(domain->domainId(), role))))); + auto dom = query->getDomain(domain->domainId()); + ASSERT_TRUE(dom); + ASSERT_EQ(*dom.get(), *domain.get()); + } + + class CreateRole : public CommandExecutorTest { + public: + void SetUp() override { + CommandExecutorTest::SetUp(); + } + }; + + /** + * @given command + * @when trying to create role + * @then role is created + */ + TEST_F(CreateRole, ValidCreateRoleTest) { + ASSERT_TRUE(val(execute(buildCommand( + TestTransactionBuilder().createRole(role, role_permissions))))); + auto rl = query->getRolePermissions(role); + ASSERT_TRUE(rl); + ASSERT_EQ(rl.get(), role_permissions); + } + + class DetachRole : public CommandExecutorTest { + public: + void SetUp() override { + CommandExecutorTest::SetUp(); + ASSERT_TRUE(val(execute(buildCommand( + TestTransactionBuilder().createRole(role, role_permissions))))); + ASSERT_TRUE(val(execute(buildCommand( + TestTransactionBuilder().createRole("role2", role_permissions))))); + ASSERT_TRUE(val(execute(buildCommand( + TestTransactionBuilder().createDomain(domain->domainId(), role))))); + ASSERT_TRUE( + val(execute(buildCommand(TestTransactionBuilder().createAccount( + "id", domain->domainId(), *pubkey))))); + ASSERT_TRUE( + val(execute(buildCommand(TestTransactionBuilder().appendRole( + account->accountId(), "role2"))))); + } + }; + + /** + * @given command + * @when trying to detach role + * @then role is detached + */ + TEST_F(DetachRole, ValidDetachRoleTest) { + ASSERT_TRUE(val(execute(buildCommand(TestTransactionBuilder().detachRole( + account->accountId(), "role2"))))); + auto roles = query->getAccountRoles(account->accountId()); + ASSERT_TRUE(roles); + ASSERT_TRUE(std::find(roles->begin(), roles->end(), "role2") + == roles->end()); + } + + class GrantPermission : public CommandExecutorTest { + public: + void SetUp() override { + CommandExecutorTest::SetUp(); + ASSERT_TRUE(val(execute(buildCommand( + TestTransactionBuilder().createRole(role, role_permissions))))); + ASSERT_TRUE(val(execute(buildCommand( + TestTransactionBuilder().createRole("role2", role_permissions))))); + ASSERT_TRUE(val(execute(buildCommand( + TestTransactionBuilder().createDomain(domain->domainId(), role))))); + ASSERT_TRUE( + val(execute(buildCommand(TestTransactionBuilder().createAccount( + "id", domain->domainId(), *pubkey))))); + } + }; + + /** + * @given command + * @when trying to grant permission + * @then permission is granted + */ + TEST_F(GrantPermission, ValidGrantPermissionTest) { + auto perm = shared_model::interface::permissions::Grantable::kSetMyQuorum; + ASSERT_TRUE(val( + execute(buildCommand(TestTransactionBuilder() + .grantPermission(account->accountId(), perm) + .creatorAccountId(account->accountId()))))); + auto has_perm = query->hasAccountGrantablePermission( + account->accountId(), account->accountId(), perm); + ASSERT_TRUE(has_perm); + } + + class RemoveSignatory : public CommandExecutorTest { + public: + void SetUp() override { + CommandExecutorTest::SetUp(); + pubkey = std::make_unique( + std::string('1', 32)); + ASSERT_TRUE(val(execute(buildCommand( + TestTransactionBuilder().createRole(role, role_permissions))))); + ASSERT_TRUE(val(execute(buildCommand( + TestTransactionBuilder().createDomain(domain->domainId(), role))))); + ASSERT_TRUE( + val(execute(buildCommand(TestTransactionBuilder().createAccount( + "id", domain->domainId(), *pubkey))))); + ASSERT_TRUE( + val(execute(buildCommand(TestTransactionBuilder().addSignatory( + account->accountId(), + shared_model::interface::types::PubkeyType( + std::string('5', 32))))))); + } + std::unique_ptr pubkey; + }; + + /** + * @given command + * @when trying to remove signatory + * @then signatory is successfully removed + */ + TEST_F(RemoveSignatory, ValidRemoveSignatoryTest) { + ASSERT_TRUE( + val(execute(buildCommand(TestTransactionBuilder().removeSignatory( + account->accountId(), *pubkey))))); + auto signatories = query->getSignatories(account->accountId()); + ASSERT_TRUE(signatories); + ASSERT_TRUE(std::find(signatories->begin(), signatories->end(), *pubkey) + == signatories->end()); + } + + class RevokePermission : public CommandExecutorTest { + public: + void SetUp() override { + CommandExecutorTest::SetUp(); + ASSERT_TRUE(val(execute(buildCommand( + TestTransactionBuilder().createRole(role, role_permissions))))); + ASSERT_TRUE(val(execute(buildCommand( + TestTransactionBuilder().createDomain(domain->domainId(), role))))); + ASSERT_TRUE( + val(execute(buildCommand(TestTransactionBuilder().createAccount( + "id", domain->domainId(), *pubkey))))); + ASSERT_TRUE(val(execute(buildCommand( + TestTransactionBuilder() + .grantPermission(account->accountId(), grantable_permission) + .creatorAccountId(account->accountId()))))); + } + }; + + /** + * @given command + * @when trying to revoke permission + * @then permission is revoked + */ + TEST_F(RevokePermission, ValidRevokePermissionTest) { + auto perm = + shared_model::interface::permissions::Grantable::kRemoveMySignatory; + ASSERT_TRUE(query->hasAccountGrantablePermission( + account->accountId(), account->accountId(), grantable_permission)); + + ASSERT_TRUE(val( + execute(buildCommand(TestTransactionBuilder() + .grantPermission(account->accountId(), perm) + .creatorAccountId(account->accountId()))))); + ASSERT_TRUE(query->hasAccountGrantablePermission( + account->accountId(), account->accountId(), grantable_permission)); + ASSERT_TRUE(query->hasAccountGrantablePermission( + account->accountId(), account->accountId(), perm)); + + ASSERT_TRUE(val(execute(buildCommand( + TestTransactionBuilder() + .revokePermission(account->accountId(), grantable_permission) + .creatorAccountId(account->accountId()))))); + ASSERT_FALSE(query->hasAccountGrantablePermission( + account->accountId(), account->accountId(), grantable_permission)); + ASSERT_TRUE(query->hasAccountGrantablePermission( + account->accountId(), account->accountId(), perm)); + } + + class SetAccountDetail : public CommandExecutorTest { + public: + void SetUp() override { + CommandExecutorTest::SetUp(); + ASSERT_TRUE(val(execute(buildCommand( + TestTransactionBuilder().createRole(role, role_permissions))))); + ASSERT_TRUE(val(execute(buildCommand( + TestTransactionBuilder().createDomain(domain->domainId(), role))))); + ASSERT_TRUE( + val(execute(buildCommand(TestTransactionBuilder().createAccount( + "id", domain->domainId(), *pubkey))))); + } + }; + + /** + * @given command + * @when trying to set kv + * @then kv is set + */ + TEST_F(SetAccountDetail, ValidSetAccountDetailTest) { + ASSERT_TRUE(val(execute(buildCommand( + TestTransactionBuilder() + .setAccountDetail(account->accountId(), "key", "value") + .creatorAccountId(account->accountId()))))); + auto kv = query->getAccountDetail(account->accountId()); + ASSERT_TRUE(kv); + ASSERT_EQ(kv.get(), "{\"id@domain\": {\"key\": \"value\"}}"); + } + + class SetQuorum : public CommandExecutorTest { + public: + void SetUp() override { + CommandExecutorTest::SetUp(); + ASSERT_TRUE(val(execute(buildCommand( + TestTransactionBuilder().createRole(role, role_permissions))))); + ASSERT_TRUE(val(execute(buildCommand( + TestTransactionBuilder().createDomain(domain->domainId(), role))))); + ASSERT_TRUE( + val(execute(buildCommand(TestTransactionBuilder().createAccount( + "id", domain->domainId(), *pubkey))))); + } + }; + + /** + * @given command + * @when trying to set kv + * @then kv is set + */ + TEST_F(SetQuorum, ValidSetQuorumTest) { + ASSERT_TRUE( + val(execute(buildCommand(TestTransactionBuilder().setAccountQuorum( + account->accountId(), 3))))); + } + + class SubtractAccountAssetTest : public CommandExecutorTest { + void SetUp() override { + CommandExecutorTest::SetUp(); + ASSERT_TRUE(val(execute(buildCommand( + TestTransactionBuilder().createRole(role, role_permissions))))); + ASSERT_TRUE(val(execute(buildCommand( + TestTransactionBuilder().createDomain(domain->domainId(), role))))); + ASSERT_TRUE( + val(execute(buildCommand(TestTransactionBuilder().createAccount( + "id", domain->domainId(), *pubkey))))); + } + + public: + /** + * Add default asset and check that it is done + */ + void addAsset() { + auto asset = clone(TestAccountAssetBuilder() + .domainId(domain->domainId()) + .assetId(asset_id) + .precision(1) + .build()); + + ASSERT_TRUE( + val(execute(buildCommand(TestTransactionBuilder().createAsset( + "coin", domain->domainId(), 1))))); + } + + shared_model::interface::types::AssetIdType asset_id = + "coin#" + domain->domainId(); + }; + + /** + * @given command + * @when trying to subtract account asset + * @then account asset is successfully subtracted + */ + TEST_F(SubtractAccountAssetTest, ValidSubtractAccountAssetTest) { + addAsset(); + ASSERT_TRUE(val( + execute(buildCommand(TestTransactionBuilder() + .addAssetQuantity(asset_id, "1.0") + .creatorAccountId(account->accountId()))))); + auto account_asset = + query->getAccountAsset(account->accountId(), asset_id); + ASSERT_TRUE(account_asset); + ASSERT_EQ("1.0", account_asset.get()->balance().toStringRepr()); + ASSERT_TRUE(val( + execute(buildCommand(TestTransactionBuilder() + .addAssetQuantity(asset_id, "1.0") + .creatorAccountId(account->accountId()))))); + account_asset = query->getAccountAsset(account->accountId(), asset_id); + ASSERT_TRUE(account_asset); + ASSERT_EQ("2.0", account_asset.get()->balance().toStringRepr()); + ASSERT_TRUE(val( + execute(buildCommand(TestTransactionBuilder() + .subtractAssetQuantity(asset_id, "1.0") + .creatorAccountId(account->accountId()))))); + account_asset = query->getAccountAsset(account->accountId(), asset_id); + ASSERT_TRUE(account_asset); + ASSERT_EQ("1.0", account_asset.get()->balance().toStringRepr()); + } + + /** + * @given command + * @when trying to subtract account asset with non-existing asset + * @then account asset fails to be subtracted + */ + TEST_F(SubtractAccountAssetTest, SubtractAccountAssetTestInvalidAsset) { + ASSERT_TRUE(err( + execute(buildCommand(TestTransactionBuilder() + .subtractAssetQuantity(asset_id, "1.0") + .creatorAccountId(account->accountId()))))); + } + + /** + * @given command + * @when trying to add account subtract with non-existing account + * @then account asset fails to subtracted + */ + TEST_F(SubtractAccountAssetTest, SubtractAccountAssetTestInvalidAccount) { + addAsset(); + ASSERT_TRUE( + err(execute(buildCommand(TestTransactionBuilder() + .subtractAssetQuantity(asset_id, "1.0") + .creatorAccountId("some@domain"))))); + } + + /** + * @given command + * @when trying to add account asset with wrong precision + * @then account asset fails to added + */ + TEST_F(SubtractAccountAssetTest, SubtractAccountAssetTestInvalidPrecision) { + addAsset(); + ASSERT_TRUE(err( + execute(buildCommand(TestTransactionBuilder() + .subtractAssetQuantity(asset_id, "1.0000") + .creatorAccountId(account->accountId()))))); + } + + /** + * @given command + * @when trying to add account asset that overflows + * @then account asset fails to added + */ + TEST_F(SubtractAccountAssetTest, SubtractAccountAssetTestUint256Overflow) { + addAsset(); + ASSERT_TRUE(val( + execute(buildCommand(TestTransactionBuilder() + .addAssetQuantity(asset_id, "1.0") + .creatorAccountId(account->accountId()))))); + ASSERT_TRUE(err( + execute(buildCommand(TestTransactionBuilder() + .subtractAssetQuantity(asset_id, "2.0") + .creatorAccountId(account->accountId()))))); + } + + class TransferAccountAssetTest : public CommandExecutorTest { + void SetUp() override { + CommandExecutorTest::SetUp(); + + account2 = clone(TestAccountBuilder() + .domainId(domain->domainId()) + .accountId("id2@" + domain->domainId()) + .quorum(1) + .jsonData("{}") + .build()); + + ASSERT_TRUE(val(execute(buildCommand( + TestTransactionBuilder().createRole(role, role_permissions))))); + ASSERT_TRUE(val(execute(buildCommand( + TestTransactionBuilder().createDomain(domain->domainId(), role))))); + ASSERT_TRUE( + val(execute(buildCommand(TestTransactionBuilder().createAccount( + "id", domain->domainId(), *pubkey))))); + ASSERT_TRUE( + val(execute(buildCommand(TestTransactionBuilder().createAccount( + "id2", domain->domainId(), *pubkey))))); + } + + public: + /** + * Add default asset and check that it is done + */ + void addAsset() { + auto asset = clone(TestAccountAssetBuilder() + .domainId(domain->domainId()) + .assetId(asset_id) + .precision(1) + .build()); + + ASSERT_TRUE( + val(execute(buildCommand(TestTransactionBuilder().createAsset( + "coin", domain->domainId(), 1))))); + } + + shared_model::interface::types::AssetIdType asset_id = + "coin#" + domain->domainId(); + std::unique_ptr account2; + }; + + /** + * @given command + * @when trying to add transfer asset + * @then account asset is successfully transfered + */ + TEST_F(TransferAccountAssetTest, ValidTransferAccountAssetTest) { + addAsset(); + ASSERT_TRUE(val( + execute(buildCommand(TestTransactionBuilder() + .addAssetQuantity(asset_id, "1.0") + .creatorAccountId(account->accountId()))))); + auto account_asset = + query->getAccountAsset(account->accountId(), asset_id); + ASSERT_TRUE(account_asset); + ASSERT_EQ("1.0", account_asset.get()->balance().toStringRepr()); + ASSERT_TRUE(val( + execute(buildCommand(TestTransactionBuilder() + .addAssetQuantity(asset_id, "1.0") + .creatorAccountId(account->accountId()))))); + account_asset = query->getAccountAsset(account->accountId(), asset_id); + ASSERT_TRUE(account_asset); + ASSERT_EQ("2.0", account_asset.get()->balance().toStringRepr()); + ASSERT_TRUE(val(execute(buildCommand( + TestTransactionBuilder().transferAsset(account->accountId(), + account2->accountId(), + asset_id, + "desc", + "1.0"))))); + account_asset = query->getAccountAsset(account->accountId(), asset_id); + ASSERT_TRUE(account_asset); + ASSERT_EQ("1.0", account_asset.get()->balance().toStringRepr()); + account_asset = query->getAccountAsset(account2->accountId(), asset_id); + ASSERT_TRUE(account_asset); + ASSERT_EQ("1.0", account_asset.get()->balance().toStringRepr()); + } + + /** + * @given command + * @when trying to transfer account asset with non-existing asset + * @then account asset fails to be transfered + */ + TEST_F(TransferAccountAssetTest, TransferAccountAssetTestInvalidAsset) { + ASSERT_TRUE(err(execute(buildCommand( + TestTransactionBuilder().transferAsset(account->accountId(), + account2->accountId(), + asset_id, + "desc", + "1.0"))))); + } + + /** + * @given command + * @when trying to transfer account asset with non-existing account + * @then account asset fails to transfered + */ + TEST_F(TransferAccountAssetTest, TransferAccountAssetTestInvalidAccount) { + addAsset(); + ASSERT_TRUE(val( + execute(buildCommand(TestTransactionBuilder() + .addAssetQuantity(asset_id, "1.0") + .creatorAccountId(account->accountId()))))); + ASSERT_TRUE( + err(execute(buildCommand(TestTransactionBuilder().transferAsset( + account->accountId(), "some@domain", asset_id, "desc", "1.0"))))); + ASSERT_TRUE(err(execute(buildCommand( + TestTransactionBuilder().transferAsset("some@domain", + account2->accountId(), + asset_id, + "desc", + "1.0"))))); + } + + /** + * @given command + * @when trying to transfer account asset that overflows + * @then account asset fails to transfered + */ + TEST_F(TransferAccountAssetTest, TransferAccountAssetOwerdraftTest) { + addAsset(); + ASSERT_TRUE(val( + execute(buildCommand(TestTransactionBuilder() + .addAssetQuantity(asset_id, "1.0") + .creatorAccountId(account->accountId()))))); + ASSERT_TRUE(err(execute(buildCommand( + TestTransactionBuilder().transferAsset(account->accountId(), + account2->accountId(), + asset_id, + "desc", + "2.0"))))); + } + + } // namespace ametsuchi +} // namespace iroha diff --git a/test/module/irohad/ametsuchi/wsv_query_command_test.cpp b/test/module/irohad/ametsuchi/wsv_query_command_test.cpp index 8c6278f1cd..a93adc3bda 100644 --- a/test/module/irohad/ametsuchi/wsv_query_command_test.cpp +++ b/test/module/irohad/ametsuchi/wsv_query_command_test.cpp @@ -20,6 +20,7 @@ #include "framework/result_fixture.hpp" #include "module/irohad/ametsuchi/ametsuchi_fixture.hpp" #include "module/shared_model/builders/protobuf/test_account_builder.hpp" +#include "module/shared_model/builders/protobuf/test_asset_builder.hpp" #include "module/shared_model/builders/protobuf/test_domain_builder.hpp" #include "module/shared_model/builders/protobuf/test_peer_builder.hpp" diff --git a/test/module/irohad/execution/command_validate_execute_test.cpp b/test/module/irohad/execution/command_validate_execute_test.cpp index d8dff8b10b..e2355549e2 100644 --- a/test/module/irohad/execution/command_validate_execute_test.cpp +++ b/test/module/irohad/execution/command_validate_execute_test.cpp @@ -55,8 +55,6 @@ class CommandValidateExecuteTest : public ::testing::Test { void SetUp() override { wsv_query = std::make_shared>(); wsv_command = std::make_shared>(); - - executor = std::make_unique(wsv_query, wsv_command); validator = std::make_unique(wsv_query); shared_model::builder::AccountBuilder< @@ -122,25 +120,10 @@ class CommandValidateExecuteTest : public ::testing::Test { }); } - iroha::CommandResult validateAndExecute( + iroha::CommandResult validate( const std::unique_ptr &command) { validator->setCreatorAccountId(creator->accountId()); - - return boost::apply_visitor(*validator, command->get()) - .match( - [this, &command](expected::Value &) -> iroha::CommandResult { - return execute(command); - }, - [](const auto &) -> iroha::CommandResult { - return expected::makeError(iroha::CommandError{ - "Validate", "validation of a command failed"}); - }); - } - - iroha::CommandResult execute( - const std::unique_ptr &command) { - executor->setCreatorAccountId(creator->accountId()); - return boost::apply_visitor(*executor, command->get()); + return boost::apply_visitor(*validator, command->get()); } /// return result with empty error message @@ -191,7 +174,6 @@ class CommandValidateExecuteTest : public ::testing::Test { std::shared_ptr wsv_query; std::shared_ptr wsv_command; - std::unique_ptr executor; std::unique_ptr validator; }; @@ -218,20 +200,12 @@ class AddAssetQuantityTest : public CommandValidateExecuteTest { * @then executor will be passed */ TEST_F(AddAssetQuantityTest, ValidWhenNewWallet) { - EXPECT_CALL(*wsv_query, getAccountAsset(creator->accountId(), _)) - .WillOnce(Return(boost::none)); - EXPECT_CALL(*wsv_query, getAsset(add_asset_quantity->assetId())) - .WillOnce(Return(asset)); - EXPECT_CALL(*wsv_query, getAccount(creator->accountId())) - .WillOnce(Return(account)); - EXPECT_CALL(*wsv_command, upsertAccountAsset(_)) - .WillOnce(Return(WsvCommandResult())); EXPECT_CALL(*wsv_query, getAccountRoles(creator->accountId())) .WillOnce(Return(admin_roles)); EXPECT_CALL(*wsv_query, getRolePermissions(kAdminRole)) .WillOnce(Return(role_permissions)); - ASSERT_TRUE(val(validateAndExecute(command))); + ASSERT_TRUE(val(validate(command))); } /** @@ -240,20 +214,11 @@ TEST_F(AddAssetQuantityTest, ValidWhenNewWallet) { * @then executor will be passed */ TEST_F(AddAssetQuantityTest, ValidWhenExistingWallet) { - EXPECT_CALL( - *wsv_query, - getAccountAsset(creator->accountId(), add_asset_quantity->assetId())) - .WillOnce(Return(wallet)); - EXPECT_CALL(*wsv_query, getAsset(kAssetId)).WillOnce(Return(asset)); - EXPECT_CALL(*wsv_query, getAccount(creator->accountId())) - .WillOnce(Return(account)); - EXPECT_CALL(*wsv_command, upsertAccountAsset(_)) - .WillOnce(Return(WsvCommandResult())); EXPECT_CALL(*wsv_query, getAccountRoles(creator->accountId())) .WillOnce(Return(admin_roles)); EXPECT_CALL(*wsv_query, getRolePermissions(kAdminRole)) .WillOnce(Return(role_permissions)); - ASSERT_TRUE(val(validateAndExecute(command))); + ASSERT_TRUE(val(validate(command))); } /** @@ -264,28 +229,7 @@ TEST_F(AddAssetQuantityTest, ValidWhenExistingWallet) { TEST_F(AddAssetQuantityTest, InvalidWhenNoRoles) { EXPECT_CALL(*wsv_query, getAccountRoles(creator->accountId())) .WillOnce(Return(boost::none)); - ASSERT_TRUE(err(validateAndExecute(command))); -} - -/** - * @given AddAssetQuantity with amount with wrong precision (must be 2) - * @when command is executed - * @then executor will be failed - */ -TEST_F(AddAssetQuantityTest, InvalidWhenWrongPrecision) { - // TODO 2018-04-20 Alexey Chernyshov - IR-1276 - IR-1276 - rework with - // CommandBuilder - command = buildCommand(TestTransactionBuilder().addAssetQuantity( - kAssetId, kAmountWrongPrecision)); - add_asset_quantity = - getConcreteCommand(command); - - EXPECT_CALL(*wsv_query, getAccountRoles(kAdminId)) - .WillOnce(Return(admin_roles)); - EXPECT_CALL(*wsv_query, getRolePermissions(kAdminRole)) - .WillOnce(Return(role_permissions)); - EXPECT_CALL(*wsv_query, getAsset(kAssetId)).WillOnce(Return(asset)); - ASSERT_TRUE(err(validateAndExecute(command))); + ASSERT_TRUE(err(validate(command))); } /** @@ -299,11 +243,8 @@ TEST_F(AddAssetQuantityTest, InvalidWhenNoAccount) { .WillOnce(Return(admin_roles)); EXPECT_CALL(*wsv_query, getRolePermissions(kAdminRole)) .WillOnce(Return(role_permissions)); - EXPECT_CALL(*wsv_query, getAsset(kAssetId)).WillOnce(Return(asset)); - EXPECT_CALL(*wsv_query, getAccount(creator->accountId())) - .WillOnce(Return(boost::none)); - ASSERT_TRUE(err(validateAndExecute(command))); + ASSERT_TRUE(val(validate(command))); } /** @@ -324,10 +265,7 @@ TEST_F(AddAssetQuantityTest, InvalidWhenNoAsset) { EXPECT_CALL(*wsv_query, getRolePermissions(kAdminRole)) .WillOnce(Return(role_permissions)); - EXPECT_CALL(*wsv_query, getAsset(add_asset_quantity->assetId())) - .WillOnce(Return(boost::none)); - - ASSERT_TRUE(err(validateAndExecute(command))); + ASSERT_TRUE(val(validate(command))); } /** @@ -342,19 +280,12 @@ TEST_F(AddAssetQuantityTest, InvalidWhenAssetAdditionFails) { add_asset_quantity = getConcreteCommand(command); - EXPECT_CALL( - *wsv_query, - getAccountAsset(creator->accountId(), add_asset_quantity->assetId())) - .WillOnce(Return(wallet)); - EXPECT_CALL(*wsv_query, getAsset(kAssetId)).WillOnce(Return(asset)); - EXPECT_CALL(*wsv_query, getAccount(creator->accountId())) - .WillOnce(Return(account)); EXPECT_CALL(*wsv_query, getAccountRoles(creator->accountId())) .WillOnce(Return(admin_roles)); EXPECT_CALL(*wsv_query, getRolePermissions(kAdminRole)) .WillOnce(Return(role_permissions)); - ASSERT_TRUE(err(validateAndExecute(command))); + ASSERT_TRUE(val(validate(command))); } class SubtractAssetQuantityTest : public CommandValidateExecuteTest { @@ -376,23 +307,6 @@ class SubtractAssetQuantityTest : public CommandValidateExecuteTest { subtract_asset_quantity; }; -/** - * @given SubtractAssetQuantity - * @when account doesn't have wallet of target asset - * @then executor will be failed - */ -TEST_F(SubtractAssetQuantityTest, InvalidWhenNoWallet) { - EXPECT_CALL( - *wsv_query, - getAccountAsset(creator->accountId(), subtract_asset_quantity->assetId())) - .WillOnce(Return(boost::none)); - EXPECT_CALL(*wsv_query, getAccountRoles(creator->accountId())) - .WillOnce(Return(admin_roles)); - EXPECT_CALL(*wsv_query, getRolePermissions(kAdminRole)) - .WillOnce(Return(role_permissions)); - EXPECT_CALL(*wsv_query, getAsset(kAssetId)).WillOnce(Return(asset)); - ASSERT_TRUE(err(validateAndExecute(command))); -} /** * @given SubtractAssetQuantity @@ -400,100 +314,11 @@ TEST_F(SubtractAssetQuantityTest, InvalidWhenNoWallet) { * @then executor will be passed */ TEST_F(SubtractAssetQuantityTest, ValidWhenExistingWallet) { - EXPECT_CALL( - *wsv_query, - getAccountAsset(creator->accountId(), subtract_asset_quantity->assetId())) - .WillOnce(Return(wallet)); - EXPECT_CALL(*wsv_query, getAsset(kAssetId)).WillOnce(Return(asset)); - EXPECT_CALL(*wsv_command, upsertAccountAsset(_)) - .WillOnce(Return(WsvCommandResult())); EXPECT_CALL(*wsv_query, getAccountRoles(creator->accountId())) .WillOnce(Return(admin_roles)); EXPECT_CALL(*wsv_query, getRolePermissions(kAdminRole)) .WillOnce(Return(role_permissions)); - ASSERT_TRUE(val(validateAndExecute(command))); -} - -/** - * @given SubtractAssetQuantity - * @when arguments amount is greater than wallet's amount - * @then executor will be failed - */ -TEST_F(SubtractAssetQuantityTest, InvalidWhenOverAmount) { - // TODO 2018-04-20 Alexey Chernyshov - IR-1276 - rework with CommandBuilder - command = buildCommand(TestTransactionBuilder().subtractAssetQuantity( - kAssetId, kAmountOverflow)); - subtract_asset_quantity = - getConcreteCommand( - command); - - EXPECT_CALL( - *wsv_query, - getAccountAsset(creator->accountId(), subtract_asset_quantity->assetId())) - .WillOnce(Return(wallet)); - - EXPECT_CALL(*wsv_query, getAccountRoles(creator->accountId())) - .WillOnce(Return(admin_roles)); - EXPECT_CALL(*wsv_query, getRolePermissions(kAdminRole)) - .WillOnce(Return(role_permissions)); - EXPECT_CALL(*wsv_query, getAsset(kAssetId)).WillOnce(Return(asset)); - - ASSERT_TRUE(err(validateAndExecute(command))); -} - -/** - * @given SubtractAssetQuantity - * @when account doesn't have role - * @then executor will be failed - */ -TEST_F(SubtractAssetQuantityTest, InvalidWhenNoRoles) { - EXPECT_CALL(*wsv_query, getAccountRoles(creator->accountId())) - .WillOnce(Return(boost::none)); - ASSERT_TRUE(err(validateAndExecute(command))); -} - -/** - * @given SubtractAssetQuantity - * @when arguments amount precision is invalid (greater than 2) - * @then executor will be failed - */ -TEST_F(SubtractAssetQuantityTest, InvalidWhenWrongPrecision) { - // TODO 2018-04-20 Alexey Chernyshov - IR-1276 - rework with CommandBuilder - command = buildCommand(TestTransactionBuilder().subtractAssetQuantity( - kAssetId, kAmountWrongPrecision)); - subtract_asset_quantity = - getConcreteCommand( - command); - - EXPECT_CALL(*wsv_query, getAccountRoles(kAdminId)) - .WillOnce(Return(admin_roles)); - EXPECT_CALL(*wsv_query, getRolePermissions(kAdminRole)) - .WillOnce(Return(role_permissions)); - EXPECT_CALL(*wsv_query, getAsset(kAssetId)).WillOnce(Return(asset)); - ASSERT_TRUE(err(validateAndExecute(command))); -} - -/** - * @given SubtractAssetQuantity - * @when asset doesn't exist - * @then executor will be failed - */ -TEST_F(SubtractAssetQuantityTest, InvalidWhenNoAsset) { - // TODO 2018-04-20 Alexey Chernyshov - IR-1276 - rework with CommandBuilder - command = buildCommand( - TestTransactionBuilder().subtractAssetQuantity(kNoAssetId, kAmount)); - subtract_asset_quantity = - getConcreteCommand( - command); - - EXPECT_CALL(*wsv_query, getAccountRoles(kAdminId)) - .WillOnce(Return(admin_roles)); - EXPECT_CALL(*wsv_query, getRolePermissions(kAdminRole)) - .WillOnce(Return(role_permissions)); - EXPECT_CALL(*wsv_query, getAsset(subtract_asset_quantity->assetId())) - .WillOnce(Return(boost::none)); - - ASSERT_TRUE(err(validateAndExecute(command))); + ASSERT_TRUE(val(validate(command))); } class AddSignatoryTest : public CommandValidateExecuteTest { @@ -524,13 +349,7 @@ TEST_F(AddSignatoryTest, ValidWhenCreatorHasPermissions) { hasAccountGrantablePermission( kAdminId, add_signatory->accountId(), Grantable::kAddMySignatory)) .WillOnce(Return(true)); - EXPECT_CALL(*wsv_command, insertSignatory(add_signatory->pubkey())) - .WillOnce(Return(WsvCommandResult())); - EXPECT_CALL(*wsv_command, - insertAccountSignatory(add_signatory->accountId(), - add_signatory->pubkey())) - .WillOnce(Return(WsvCommandResult())); - ASSERT_TRUE(val(validateAndExecute(command))); + ASSERT_TRUE(val(validate(command))); } /** @@ -549,14 +368,7 @@ TEST_F(AddSignatoryTest, ValidWhenSameAccount) { .WillOnce(Return(admin_roles)); EXPECT_CALL(*wsv_query, getRolePermissions(kAdminRole)) .WillOnce(Return(role_permissions)); - EXPECT_CALL(*wsv_command, insertSignatory(add_signatory->pubkey())) - .WillOnce(Return(WsvCommandResult())); - EXPECT_CALL(*wsv_command, - insertAccountSignatory(add_signatory->accountId(), - add_signatory->pubkey())) - .WillOnce(Return(WsvCommandResult())); - - ASSERT_TRUE(val(validateAndExecute(command))); + ASSERT_TRUE(val(validate(command))); } /** @@ -571,7 +383,7 @@ TEST_F(AddSignatoryTest, InvalidWhenNoPermissions) { kAdminId, add_signatory->accountId(), Grantable::kAddMySignatory)) .WillOnce(Return(false)); - ASSERT_TRUE(err(validateAndExecute(command))); + ASSERT_TRUE(err(validate(command))); } /** @@ -592,7 +404,7 @@ TEST_F(AddSignatoryTest, InvalidWhenNoAccount) { kAdminId, add_signatory->accountId(), Grantable::kAddMySignatory)) .WillOnce(Return(false)); - ASSERT_TRUE(err(validateAndExecute(command))); + ASSERT_TRUE(err(validate(command))); } /** @@ -612,10 +424,8 @@ TEST_F(AddSignatoryTest, InvalidWhenSameKey) { hasAccountGrantablePermission( kAdminId, add_signatory->accountId(), Grantable::kAddMySignatory)) .WillOnce(Return(true)); - EXPECT_CALL(*wsv_command, insertSignatory(add_signatory->pubkey())) - .WillOnce(Return(makeEmptyError())); - ASSERT_TRUE(err(validateAndExecute(command))); + ASSERT_TRUE(val(validate(command))); } class CreateAccountTest : public CommandValidateExecuteTest { @@ -652,20 +462,8 @@ TEST_F(CreateAccountTest, ValidWhenNewAccount) { .WillOnce(Return(admin_roles)); EXPECT_CALL(*wsv_query, getRolePermissions(kAdminRole)) .WillOnce(Return(role_permissions)); - EXPECT_CALL(*wsv_query, getDomain(kDomainId)) - .WillOnce(Return(default_domain)); - EXPECT_CALL(*wsv_command, insertSignatory(create_account->pubkey())) - .Times(1) - .WillOnce(Return(WsvCommandResult())); - EXPECT_CALL(*wsv_command, insertAccount(_)) - .WillOnce(Return(WsvCommandResult())); - EXPECT_CALL(*wsv_command, - insertAccountSignatory(kAccountId, create_account->pubkey())) - .WillOnce(Return(WsvCommandResult())); - EXPECT_CALL(*wsv_command, insertAccountRole(kAccountId, kAdminRole)) - .WillOnce(Return(WsvCommandResult())); - ASSERT_TRUE(val(validateAndExecute(command))); + ASSERT_TRUE(val(validate(command))); } /** @@ -677,7 +475,7 @@ TEST_F(CreateAccountTest, InvalidWhenNoPermissions) { // Creator has no permission EXPECT_CALL(*wsv_query, getAccountRoles(kAdminId)) .WillOnce(Return(boost::none)); - ASSERT_TRUE(err(validateAndExecute(command))); + ASSERT_TRUE(err(validate(command))); } /** @@ -690,9 +488,8 @@ TEST_F(CreateAccountTest, InvalidWhenNoDomain) { .WillOnce(Return(admin_roles)); EXPECT_CALL(*wsv_query, getRolePermissions(kAdminRole)) .WillOnce(Return(role_permissions)); - EXPECT_CALL(*wsv_query, getDomain(kDomainId)).WillOnce(Return(boost::none)); - ASSERT_TRUE(err(validateAndExecute(command))); + ASSERT_TRUE(val(validate(command))); } class CreateAssetTest : public CommandValidateExecuteTest { @@ -722,10 +519,7 @@ TEST_F(CreateAssetTest, ValidWhenCreatorHasPermissions) { .WillOnce(Return(admin_roles)); EXPECT_CALL(*wsv_query, getRolePermissions(kAdminRole)) .WillOnce(Return(role_permissions)); - EXPECT_CALL(*wsv_command, insertAsset(_)) - .WillOnce(Return(WsvCommandResult())); - - ASSERT_TRUE(val(validateAndExecute(command))); + ASSERT_TRUE(val(validate(command))); } /** @@ -739,18 +533,7 @@ TEST_F(CreateAssetTest, InvalidWhenNoPermissions) { EXPECT_CALL(*wsv_query, getRolePermissions(kAdminRole)) .WillOnce(Return(boost::none)); - ASSERT_TRUE(err(validateAndExecute(command))); -} - -/** - * @given CreateAsset - * @when command tries to create asset, but insertion fails - * @then execute() fails - */ -TEST_F(CreateAssetTest, InvalidWhenAssetInsertionFails) { - EXPECT_CALL(*wsv_command, insertAsset(_)).WillOnce(Return(makeEmptyError())); - - ASSERT_TRUE(err(execute(command))); + ASSERT_TRUE(err(validate(command))); } class CreateDomainTest : public CommandValidateExecuteTest { @@ -781,10 +564,7 @@ TEST_F(CreateDomainTest, ValidWhenCreatorHasPermissions) { EXPECT_CALL(*wsv_query, getRolePermissions(kAdminRole)) .WillOnce(Return(role_permissions)); - EXPECT_CALL(*wsv_command, insertDomain(_)) - .WillOnce(Return(WsvCommandResult())); - - ASSERT_TRUE(val(validateAndExecute(command))); + ASSERT_TRUE(val(validate(command))); } /** @@ -795,18 +575,7 @@ TEST_F(CreateDomainTest, ValidWhenCreatorHasPermissions) { TEST_F(CreateDomainTest, InvalidWhenNoPermissions) { EXPECT_CALL(*wsv_query, getAccountRoles(kAdminId)) .WillOnce(Return(boost::none)); - ASSERT_TRUE(err(validateAndExecute(command))); -} - -/** - * @given CreateDomain - * @when command tries to create domain, but insertion fails - * @then execute() fails - */ -TEST_F(CreateDomainTest, InvalidWhenDomainInsertionFails) { - EXPECT_CALL(*wsv_command, insertDomain(_)).WillOnce(Return(makeEmptyError())); - - ASSERT_TRUE(err(execute(command))); + ASSERT_TRUE(err(validate(command))); } class RemoveSignatoryTest : public CommandValidateExecuteTest { @@ -854,13 +623,7 @@ TEST_F(RemoveSignatoryTest, ValidWhenMultipleKeys) { EXPECT_CALL(*wsv_query, getSignatories(remove_signatory->accountId())) .WillOnce(Return(many_pubkeys)); - EXPECT_CALL(*wsv_command, - deleteAccountSignatory(remove_signatory->accountId(), - remove_signatory->pubkey())) - .WillOnce(Return(WsvCommandResult())); - EXPECT_CALL(*wsv_command, deleteSignatory(remove_signatory->pubkey())) - .WillOnce(Return(WsvCommandResult())); - ASSERT_TRUE(val(validateAndExecute(command))); + ASSERT_TRUE(val(validate(command))); } /** @@ -889,7 +652,7 @@ TEST_F(RemoveSignatoryTest, InvalidWhenSingleKey) { EXPECT_CALL(*wsv_command, deleteSignatory(remove_signatory->pubkey())) .Times(0); - ASSERT_TRUE(err(validateAndExecute(command))); + ASSERT_TRUE(err(validate(command))); } /** @@ -904,7 +667,7 @@ TEST_F(RemoveSignatoryTest, InvalidWhenNoPermissions) { Grantable::kRemoveMySignatory)) .WillOnce(Return(false)); - ASSERT_TRUE(err(validateAndExecute(command))); + ASSERT_TRUE(err(validate(command))); } /** @@ -935,7 +698,7 @@ TEST_F(RemoveSignatoryTest, InvalidWhenNoKey) { getSignatories(wrong_key_remove_signatory->accountId())) .WillOnce(Return(account_pubkeys)); - ASSERT_TRUE(err(validateAndExecute(wrong_key_command))); + ASSERT_TRUE(err(validate(wrong_key_command))); } /** @@ -956,7 +719,7 @@ TEST_F(RemoveSignatoryTest, InvalidWhenNoAccount) { EXPECT_CALL(*wsv_query, getSignatories(remove_signatory->accountId())) .WillOnce(Return(many_pubkeys)); - ASSERT_TRUE(err(validateAndExecute(command))); + ASSERT_TRUE(err(validate(command))); } /** @@ -978,7 +741,7 @@ TEST_F(RemoveSignatoryTest, InvalidWhenNoSignatories) { EXPECT_CALL(*wsv_query, getSignatories(remove_signatory->accountId())) .WillOnce(Return(boost::none)); - ASSERT_TRUE(err(validateAndExecute(command))); + ASSERT_TRUE(err(validate(command))); } /** @@ -1000,7 +763,7 @@ TEST_F(RemoveSignatoryTest, InvalidWhenNoAccountAndSignatories) { EXPECT_CALL(*wsv_query, getSignatories(remove_signatory->accountId())) .WillOnce(Return(boost::none)); - ASSERT_TRUE(err(validateAndExecute(command))); + ASSERT_TRUE(err(validate(command))); } /** @@ -1025,21 +788,7 @@ TEST_F(RemoveSignatoryTest, InvalidWhenNoPermissionToRemoveFromSelf) { kAdminId, kAdminId, Grantable::kRemoveMySignatory)) .WillOnce(Return(false)); - ASSERT_TRUE(err(validateAndExecute(command))); -} - -/** - * @given RemoveSignatory - * @when command tries to remove signatory but deletion fails - * @then execute() fails - */ -TEST_F(RemoveSignatoryTest, InvalidWhenAccountSignatoryDeletionFails) { - EXPECT_CALL(*wsv_command, - deleteAccountSignatory(remove_signatory->accountId(), - remove_signatory->pubkey())) - .WillOnce(Return(makeEmptyError())); - - ASSERT_TRUE(err(execute(command))); + ASSERT_TRUE(err(validate(command))); } class SetQuorumTest : public CommandValidateExecuteTest { @@ -1080,14 +829,9 @@ TEST_F(SetQuorumTest, ValidWhenCreatorHasPermissions) { hasAccountGrantablePermission( kAdminId, set_quorum->accountId(), Grantable::kSetMyQuorum)) .WillOnce(Return(true)); - EXPECT_CALL(*wsv_query, getAccount(set_quorum->accountId())) - .WillOnce(Return(account)); EXPECT_CALL(*wsv_query, getSignatories(set_quorum->accountId())) .WillOnce(Return(account_pubkeys)); - EXPECT_CALL(*wsv_command, updateAccount(_)) - .WillOnce(Return(WsvCommandResult())); - - ASSERT_TRUE(val(validateAndExecute(command))); + ASSERT_TRUE(val(validate(command))); } /** @@ -1100,14 +844,9 @@ TEST_F(SetQuorumTest, ValidWhenSameAccount) { .WillOnce(Return(admin_roles)); EXPECT_CALL(*wsv_query, getRolePermissions(kAdminRole)) .WillOnce(Return(role_permissions)); - EXPECT_CALL(*wsv_query, getAccount(creator_set_quorum->accountId())) - .WillOnce(Return(account)); EXPECT_CALL(*wsv_query, getSignatories(creator_set_quorum->accountId())) .WillOnce(Return(account_pubkeys)); - EXPECT_CALL(*wsv_command, updateAccount(_)) - .WillOnce(Return(WsvCommandResult())); - - ASSERT_TRUE(val(validateAndExecute(creator_command))); + ASSERT_TRUE(val(validate(creator_command))); } /** * @given SetQuorum and creator has not grantable permissions @@ -1120,7 +859,7 @@ TEST_F(SetQuorumTest, InvalidWhenNoPermissions) { kAdminId, set_quorum->accountId(), Grantable::kSetMyQuorum)) .WillOnce(Return(false)); - ASSERT_TRUE(err(validateAndExecute(command))); + ASSERT_TRUE(err(validate(command))); } /** * @given SetQuorum and account parameter is invalid @@ -1138,7 +877,7 @@ TEST_F(SetQuorumTest, InvalidWhenNoAccount) { kAdminId, set_quorum->accountId(), Grantable::kSetMyQuorum)) .WillOnce(Return(false)); - ASSERT_TRUE(err(validateAndExecute(command))); + ASSERT_TRUE(err(validate(command))); } /** @@ -1154,10 +893,7 @@ TEST_F(SetQuorumTest, InvalidWhenNoAccountButPassedPermissions) { EXPECT_CALL(*wsv_query, getSignatories(creator_set_quorum->accountId())) .WillOnce(Return(account_pubkeys)); - EXPECT_CALL(*wsv_query, getAccount(creator_set_quorum->accountId())) - .WillOnce(Return(boost::none)); - - ASSERT_TRUE(err(validateAndExecute(creator_command))); + ASSERT_TRUE(val(validate(creator_command))); } /** @@ -1174,7 +910,7 @@ TEST_F(SetQuorumTest, InvalidWhenNoSignatories) { EXPECT_CALL(*wsv_query, getSignatories(creator_set_quorum->accountId())) .WillOnce(Return(boost::none)); - ASSERT_TRUE(err(validateAndExecute(creator_command))); + ASSERT_TRUE(err(validate(creator_command))); } /** @@ -1196,7 +932,7 @@ TEST_F(SetQuorumTest, InvalidWhenNotEnoughSignatories) { .WillOnce(Return(acc_pubkeys)); EXPECT_CALL(*wsv_command, updateAccount(_)).Times(0); - ASSERT_TRUE(err(validateAndExecute(creator_command))); + ASSERT_TRUE(err(validate(creator_command))); } class TransferAssetTest : public CommandValidateExecuteTest { @@ -1230,41 +966,6 @@ class TransferAssetTest : public CommandValidateExecuteTest { std::shared_ptr transfer_asset; }; -/** - * @given TransferAsset and destination account has not AccountAsset - * @when command is executed and new AccountAsset will be created - * @then execute successes - */ -TEST_F(TransferAssetTest, ValidWhenNewWallet) { - EXPECT_CALL(*wsv_query, getAccountRoles(transfer_asset->destAccountId())) - .WillOnce(Return(admin_roles)); - EXPECT_CALL(*wsv_query, getAccountRoles(transfer_asset->srcAccountId())) - .WillOnce(Return(admin_roles)); - EXPECT_CALL(*wsv_query, getRolePermissions(kAdminRole)) - .Times(2) - .WillRepeatedly(Return(role_permissions)); - - EXPECT_CALL(*wsv_query, getAccountAsset(transfer_asset->destAccountId(), _)) - .WillOnce(Return(boost::none)); - - EXPECT_CALL(*wsv_query, - getAccountAsset(transfer_asset->srcAccountId(), - transfer_asset->assetId())) - .Times(2) - .WillRepeatedly(Return(src_wallet)); - EXPECT_CALL(*wsv_query, getAsset(transfer_asset->assetId())) - .Times(2) - .WillRepeatedly(Return(asset)); - EXPECT_CALL(*wsv_query, getAccount(transfer_asset->destAccountId())) - .WillOnce(Return(account)); - - EXPECT_CALL(*wsv_command, upsertAccountAsset(_)) - .Times(2) - .WillRepeatedly(Return(WsvCommandResult())); - - ASSERT_TRUE(val(validateAndExecute(command))); -} - /** * @given TransferAsset * @when command is executed @@ -1279,233 +980,6 @@ TEST_F(TransferAssetTest, ValidWhenExistingWallet) { EXPECT_CALL(*wsv_query, getRolePermissions(kAdminRole)) .Times(2) .WillRepeatedly(Return(role_permissions)); - - EXPECT_CALL(*wsv_query, - getAccountAsset(transfer_asset->destAccountId(), - transfer_asset->assetId())) - .WillOnce(Return(dst_wallet)); - - EXPECT_CALL(*wsv_query, - getAccountAsset(transfer_asset->srcAccountId(), - transfer_asset->assetId())) - .Times(2) - .WillRepeatedly(Return(src_wallet)); - EXPECT_CALL(*wsv_query, getAsset(transfer_asset->assetId())) - .Times(2) - .WillRepeatedly(Return(asset)); - EXPECT_CALL(*wsv_query, getAccount(transfer_asset->destAccountId())) - .WillOnce(Return(account)); - - EXPECT_CALL(*wsv_command, upsertAccountAsset(_)) - .Times(2) - .WillRepeatedly(Return(WsvCommandResult())); - - ASSERT_TRUE(val(validateAndExecute(command))); -} - -/** - * @given TransferAsset and creator has permissions - * @when command tries to transfer - * @then execute succeses - */ -TEST_F(TransferAssetTest, ValidWhenCreatorHasPermission) { - // Transfer creator is not connected to account - // TODO 2018-04-20 Alexey Chernyshov - IR-1276 - rework with CommandBuilder - command = buildCommand(TestTransactionBuilder().transferAsset( - kAccountId, kAdminId, kAssetId, kDescription, kAmount)); - auto transfer_asset = - getConcreteCommand(command); - - EXPECT_CALL(*wsv_query, - hasAccountGrantablePermission( - kAdminId, kAccountId, Grantable::kTransferMyAssets)) - .WillOnce(Return(true)); - - EXPECT_CALL(*wsv_query, getAccountRoles(transfer_asset->destAccountId())) - .WillOnce(Return(admin_roles)); - EXPECT_CALL(*wsv_query, getRolePermissions(kAdminRole)) - .WillOnce(Return(role_permissions)); - - EXPECT_CALL(*wsv_query, getAccountAsset(transfer_asset->destAccountId(), _)) - .WillOnce(Return(boost::none)); - - EXPECT_CALL(*wsv_query, getAccountAsset(transfer_asset->srcAccountId(), _)) - .Times(2) - .WillRepeatedly(Return(src_wallet)); - EXPECT_CALL(*wsv_query, getAsset(transfer_asset->assetId())) - .Times(2) - .WillRepeatedly(Return(asset)); - EXPECT_CALL(*wsv_query, getAccount(transfer_asset->destAccountId())) - .WillOnce(Return(account)); - - EXPECT_CALL(*wsv_command, upsertAccountAsset(_)) - .Times(2) - .WillRepeatedly(Return(WsvCommandResult())); - - ASSERT_TRUE(val(validateAndExecute(command))); -} - -/** - * @given TransferAsset and creator has not account roles - * @when command is executed - * @then execute fails - */ -TEST_F(TransferAssetTest, InvalidWhenNoPermissions) { - // Creator has no permissions - EXPECT_CALL(*wsv_query, getAccountRoles(transfer_asset->srcAccountId())) - .WillOnce(Return(boost::none)); - - EXPECT_CALL(*wsv_query, getAccountRoles(transfer_asset->destAccountId())) - .WillOnce(Return(admin_roles)); - EXPECT_CALL(*wsv_query, getRolePermissions(kAdminRole)) - .WillOnce(Return(role_permissions)); - - ASSERT_TRUE(err(validateAndExecute(command))); -} - -/** - * @given TransferAsset and destination account doesn't have any role - * @when command is executed - * @then execute fails - */ -TEST_F(TransferAssetTest, InvalidWhenNoDestAccount) { - // TODO 2018-04-20 Alexey Chernyshov - IR-1276 - rework with CommandBuilder - command = buildCommand(TestTransactionBuilder().transferAsset( - kAdminId, kNoAcountId, kAssetId, kDescription, kAmount)); - auto transfer_asset = - getConcreteCommand(command); - - EXPECT_CALL(*wsv_query, getAccountRoles(transfer_asset->destAccountId())) - .WillOnce(Return(boost::none)); - - ASSERT_TRUE(err(validateAndExecute(command))); -} - -/** - * @given TransferAsset and source account doesn't have asset - * @when command is executed - * @then execute fails - */ -TEST_F(TransferAssetTest, InvalidWhenNoSrcAccountAsset) { - EXPECT_CALL(*wsv_query, getAccountRoles(transfer_asset->destAccountId())) - .WillOnce(Return(admin_roles)); - EXPECT_CALL(*wsv_query, getAccountRoles(transfer_asset->srcAccountId())) - .WillOnce(Return(admin_roles)); - EXPECT_CALL(*wsv_query, getRolePermissions(kAdminRole)) - .Times(2) - .WillRepeatedly(Return(role_permissions)); - - EXPECT_CALL(*wsv_query, getAsset(transfer_asset->assetId())) - .WillOnce(Return(asset)); - EXPECT_CALL(*wsv_query, - getAccountAsset(transfer_asset->srcAccountId(), - transfer_asset->assetId())) - .WillOnce(Return(boost::none)); - - ASSERT_TRUE(err(validateAndExecute(command))); -} - -/** - * @given TransferAsset - * @when command tries to transfer asset from non-existing account - * @then execute fails - */ -TEST_F(TransferAssetTest, InvalidWhenNoSrcAccountAssetDuringExecute) { - // No source account asset exists - EXPECT_CALL(*wsv_query, getAccountRoles(transfer_asset->destAccountId())) - .WillOnce(Return(admin_roles)); - EXPECT_CALL(*wsv_query, getAccountRoles(transfer_asset->srcAccountId())) - .WillOnce(Return(admin_roles)); - EXPECT_CALL(*wsv_query, getRolePermissions(kAdminRole)) - .Times(2) - .WillRepeatedly(Return(role_permissions)); - - EXPECT_CALL(*wsv_query, getAsset(transfer_asset->assetId())) - .WillOnce(Return(asset)); - EXPECT_CALL(*wsv_query, - getAccountAsset(transfer_asset->srcAccountId(), - transfer_asset->assetId())) - .Times(2) - .WillOnce(Return(src_wallet)) - .WillOnce(Return(boost::none)); - EXPECT_CALL(*wsv_query, getAccount(transfer_asset->destAccountId())) - .WillOnce(Return(account)); - - ASSERT_TRUE(err(validateAndExecute(command))); -} - -/** - * @given TransferAsset - * @when command tries to transfer non-existent asset - * @then isValid fails - */ -TEST_F(TransferAssetTest, InvalidWhenNoAssetDuringValidation) { - EXPECT_CALL(*wsv_query, getAccountRoles(transfer_asset->destAccountId())) - .WillOnce(Return(admin_roles)); - EXPECT_CALL(*wsv_query, getAccountRoles(transfer_asset->srcAccountId())) - .WillOnce(Return(admin_roles)); - EXPECT_CALL(*wsv_query, getRolePermissions(kAdminRole)) - .Times(2) - .WillRepeatedly(Return(role_permissions)); - - EXPECT_CALL(*wsv_query, getAsset(transfer_asset->assetId())) - .WillOnce(Return(boost::none)); - - ASSERT_TRUE(err(validateAndExecute(command))); -} - -/** - * @given TransferAsset - * @when command tries to transfer non-existent asset - * @then execute fails - */ -TEST_F(TransferAssetTest, InvalidWhenNoAssetId) { - EXPECT_CALL(*wsv_query, getAccountRoles(transfer_asset->destAccountId())) - .WillOnce(Return(admin_roles)); - EXPECT_CALL(*wsv_query, getAccountRoles(transfer_asset->srcAccountId())) - .WillOnce(Return(admin_roles)); - EXPECT_CALL(*wsv_query, getRolePermissions(kAdminRole)) - .Times(2) - .WillRepeatedly(Return(role_permissions)); - - EXPECT_CALL(*wsv_query, getAsset(transfer_asset->assetId())) - .WillOnce(Return(asset)) - .WillOnce(Return(boost::none)); - EXPECT_CALL(*wsv_query, - getAccountAsset(transfer_asset->srcAccountId(), - transfer_asset->assetId())) - .Times(2) - .WillRepeatedly(Return(src_wallet)); - EXPECT_CALL(*wsv_query, - getAccountAsset(transfer_asset->destAccountId(), - transfer_asset->assetId())) - .WillOnce(Return(dst_wallet)); - EXPECT_CALL(*wsv_query, getAccount(transfer_asset->destAccountId())) - .WillOnce(Return(account)); - - ASSERT_TRUE(err(validateAndExecute(command))); -} - -/** - * @given TransferAsset - * @when command tries to transfer amount which is less than source balance - * @then execute fails - */ -TEST_F(TransferAssetTest, InvalidWhenInsufficientFunds) { - // TODO 2018-04-20 Alexey Chernyshov - IR-1276 - rework with CommandBuilder - command = buildCommand(TestTransactionBuilder().transferAsset( - kAdminId, kAccountId, kAssetId, kDescription, kAmountOverflow)); - auto transfer_asset = - getConcreteCommand(command); - - EXPECT_CALL(*wsv_query, getAccountRoles(transfer_asset->destAccountId())) - .WillOnce(Return(admin_roles)); - EXPECT_CALL(*wsv_query, getAccountRoles(transfer_asset->srcAccountId())) - .WillOnce(Return(admin_roles)); - EXPECT_CALL(*wsv_query, getRolePermissions(kAdminRole)) - .Times(2) - .WillRepeatedly(Return(role_permissions)); - EXPECT_CALL(*wsv_query, getAccountAsset(transfer_asset->srcAccountId(), transfer_asset->assetId())) @@ -1514,140 +988,7 @@ TEST_F(TransferAssetTest, InvalidWhenInsufficientFunds) { .WillOnce(Return(asset)); EXPECT_CALL(*wsv_query, getAccount(transfer_asset->destAccountId())) .WillOnce(Return(account)); - - ASSERT_TRUE(err(validateAndExecute(command))); -} - -/** - * @given TransferAsset - * @when command tries to transfer amount which is less than source balance - * @then execute fails - */ -TEST_F(TransferAssetTest, InvalidWhenInsufficientFundsDuringExecute) { - // TODO 2018-04-20 Alexey Chernyshov - IR-1276 - rework with CommandBuilder - command = buildCommand(TestTransactionBuilder().transferAsset( - kAdminId, kAccountId, kAssetId, kDescription, kAmountOverflow)); - auto transfer_asset = - getConcreteCommand(command); - - EXPECT_CALL(*wsv_query, getAsset(transfer_asset->assetId())) - .WillOnce(Return(asset)); - EXPECT_CALL(*wsv_query, - getAccountAsset(transfer_asset->srcAccountId(), - transfer_asset->assetId())) - .WillOnce(Return(src_wallet)); - EXPECT_CALL(*wsv_query, - getAccountAsset(transfer_asset->destAccountId(), - transfer_asset->assetId())) - .WillOnce(Return(dst_wallet)); - - ASSERT_TRUE(err(execute(command))); -} - -/** - * @given TransferAsset - * @when command tries to transfer amount which has wrong precesion (must be 2) - * @then execute fails - */ -TEST_F(TransferAssetTest, InvalidWhenWrongPrecision) { - // TODO 2018-04-20 Alexey Chernyshov - IR-1276 - rework with CommandBuilder - command = buildCommand(TestTransactionBuilder().transferAsset( - kAdminId, kAccountId, kAssetId, kDescription, kAmountWrongPrecision)); - auto transfer_asset = - getConcreteCommand(command); - - EXPECT_CALL(*wsv_query, getAccountRoles(transfer_asset->destAccountId())) - .WillOnce(Return(admin_roles)); - EXPECT_CALL(*wsv_query, getAccountRoles(transfer_asset->srcAccountId())) - .WillOnce(Return(admin_roles)); - EXPECT_CALL(*wsv_query, getRolePermissions(kAdminRole)) - .Times(2) - .WillRepeatedly(Return(role_permissions)); - - EXPECT_CALL(*wsv_query, getAsset(transfer_asset->assetId())) - .WillOnce(Return(asset)); - - ASSERT_TRUE(err(validateAndExecute(command))); -} - -/** - * @given TransferAsset - * @when command tries to transfer amount with wrong precision - * @then execute fails - */ -TEST_F(TransferAssetTest, InvalidWhenWrongPrecisionDuringExecute) { - // TODO 2018-04-20 Alexey Chernyshov - IR-1276 - rework with CommandBuilder - command = buildCommand(TestTransactionBuilder().transferAsset( - kAdminId, kAccountId, kAssetId, kDescription, kAmountWrongPrecision)); - auto transfer_asset = - getConcreteCommand(command); - - EXPECT_CALL(*wsv_query, getAsset(transfer_asset->assetId())) - .WillOnce(Return(asset)); - EXPECT_CALL(*wsv_query, - getAccountAsset(transfer_asset->srcAccountId(), - transfer_asset->assetId())) - .WillOnce(Return(src_wallet)); - EXPECT_CALL(*wsv_query, - getAccountAsset(transfer_asset->destAccountId(), - transfer_asset->assetId())) - .WillOnce(Return(dst_wallet)); - - ASSERT_TRUE(err(execute(command))); -} - -/** - * @given TransferAsset - * @when command tries to transfer asset which overflows destination balance - * @then execute fails - */ -TEST_F(TransferAssetTest, InvalidWhenAmountOverflow) { - std::shared_ptr max_wallet = clone( - shared_model::proto::AccountAssetBuilder() - .assetId(src_wallet->assetId()) - .accountId(src_wallet->accountId()) - .balance(shared_model::interface::Amount( - std::numeric_limits::max().str() - + ".00")) - .build()); - - EXPECT_CALL(*wsv_query, getAsset(transfer_asset->assetId())) - .WillOnce(Return(asset)); - EXPECT_CALL(*wsv_query, - getAccountAsset(transfer_asset->srcAccountId(), - transfer_asset->assetId())) - .WillOnce(Return(src_wallet)); - EXPECT_CALL(*wsv_query, - getAccountAsset(transfer_asset->destAccountId(), - transfer_asset->assetId())) - .WillOnce(Return(max_wallet)); - - ASSERT_TRUE(err(execute(command))); -} - -/** - * @given TransferAsset and creator has not grantable permissions - * @when command tries to transfer - * @then execute fails - */ -TEST_F(TransferAssetTest, InvalidWhenCreatorHasNoPermission) { - // Transfer creator is not connected to account - // TODO 2018-04-20 Alexey Chernyshov - IR-1276 - rework with CommandBuilder - command = buildCommand(TestTransactionBuilder().transferAsset( - kAccountId, kAdminId, kAssetId, kDescription, kAmount)); - auto transfer_asset = - getConcreteCommand(command); - - EXPECT_CALL(*wsv_query, getAccountRoles(transfer_asset->destAccountId())) - .WillOnce(Return(admin_roles)); - EXPECT_CALL(*wsv_query, getRolePermissions(kAdminRole)) - .WillOnce(Return(role_permissions)); - - EXPECT_CALL(*wsv_query, - hasAccountGrantablePermission( - kAdminId, kAccountId, Grantable::kTransferMyAssets)) - .WillOnce(Return(false)); - ASSERT_TRUE(err(validateAndExecute(command))); + ASSERT_TRUE(val(validate(command))); } class AddPeerTest : public CommandValidateExecuteTest { @@ -1674,9 +1015,8 @@ TEST_F(AddPeerTest, ValidCase) { .WillOnce(Return(admin_roles)); EXPECT_CALL(*wsv_query, getRolePermissions(kAdminRole)) .WillOnce(Return(role_permissions)); - EXPECT_CALL(*wsv_command, insertPeer(_)).WillOnce(Return(WsvCommandResult())); - ASSERT_TRUE(val(validateAndExecute(command))); + ASSERT_TRUE(val(validate(command))); } /** @@ -1690,19 +1030,9 @@ TEST_F(AddPeerTest, InvalidCaseWhenNoPermissions) { .WillOnce(Return(admin_roles)); EXPECT_CALL(*wsv_query, getRolePermissions(kAdminRole)) .WillOnce(Return(boost::none)); - ASSERT_TRUE(err(validateAndExecute(command))); + ASSERT_TRUE(err(validate(command))); } -/** - * @given AddPeer - * @when command tries to insert peer but insertion fails - * @then execute failed - */ -TEST_F(AddPeerTest, InvalidCaseWhenInsertPeerFails) { - EXPECT_CALL(*wsv_command, insertPeer(_)).WillOnce(Return(makeEmptyError())); - - ASSERT_TRUE(err(execute(command))); -} class CreateRoleTest : public CommandValidateExecuteTest { public: @@ -1731,13 +1061,7 @@ TEST_F(CreateRoleTest, ValidCase) { .WillRepeatedly(Return(admin_roles)); EXPECT_CALL(*wsv_query, getRolePermissions(kAdminRole)) .WillRepeatedly(Return(role_permissions)); - EXPECT_CALL(*wsv_command, insertRole(create_role->roleName())) - .WillOnce(Return(WsvCommandResult())); - EXPECT_CALL(*wsv_command, - insertRolePermissions(create_role->roleName(), - create_role->rolePermissions())) - .WillOnce(Return(WsvCommandResult())); - ASSERT_TRUE(val(validateAndExecute(command))); + ASSERT_TRUE(val(validate(command))); } /** @@ -1750,7 +1074,7 @@ TEST_F(CreateRoleTest, InvalidCaseWhenNoPermissions) { .WillRepeatedly(Return(admin_roles)); EXPECT_CALL(*wsv_query, getRolePermissions(kAdminRole)) .WillRepeatedly(Return(boost::none)); - ASSERT_TRUE(err(validateAndExecute(command))); + ASSERT_TRUE(err(validate(command))); } /** @@ -1769,18 +1093,7 @@ TEST_F(CreateRoleTest, InvalidCaseWhenRoleSuperset) { .WillRepeatedly(Return(admin_roles)); EXPECT_CALL(*wsv_query, getRolePermissions(kAdminRole)) .WillRepeatedly(Return(role_permissions)); - ASSERT_TRUE(err(validateAndExecute(command))); -} - -/** - * @given CreateRole - * @when command tries to create new role, but insertion fails - * @then execute failed - */ -TEST_F(CreateRoleTest, InvalidCaseWhenRoleInsertionFails) { - EXPECT_CALL(*wsv_command, insertRole(create_role->roleName())) - .WillOnce(Return(makeEmptyError())); - ASSERT_TRUE(err(execute(command))); + ASSERT_TRUE(err(validate(command))); } class AppendRoleTest : public CommandValidateExecuteTest { @@ -1815,11 +1128,7 @@ TEST_F(AppendRoleTest, ValidCase) { EXPECT_CALL(*wsv_query, getRolePermissions(kMasterRole)) .WillOnce(Return(role_permissions)); - EXPECT_CALL( - *wsv_command, - insertAccountRole(append_role->accountId(), append_role->roleName())) - .WillOnce(Return(WsvCommandResult())); - ASSERT_TRUE(val(validateAndExecute(command))); + ASSERT_TRUE(val(validate(command))); } /** @@ -1832,7 +1141,7 @@ TEST_F(AppendRoleTest, InvalidCaseNoPermissions) { .WillOnce(Return(admin_roles)); EXPECT_CALL(*wsv_query, getRolePermissions(kAdminRole)) .WillOnce(Return(boost::none)); - ASSERT_TRUE(err(validateAndExecute(command))); + ASSERT_TRUE(err(validate(command))); } /** @@ -1849,7 +1158,7 @@ TEST_F(AppendRoleTest, InvalidCaseNoAccountRole) { .WillOnce(Return(role_permissions)); EXPECT_CALL(*wsv_query, getRolePermissions(kMasterRole)) .WillOnce(Return(role_permissions)); - ASSERT_TRUE(err(validateAndExecute(command))); + ASSERT_TRUE(err(validate(command))); } /** @@ -1868,7 +1177,7 @@ TEST_F(AppendRoleTest, InvalidCaseNoAccountRoleAndNoPermission) { .WillOnce(Return(role_permissions)); EXPECT_CALL(*wsv_query, getRolePermissions(kMasterRole)) .WillOnce(Return(boost::none)); - ASSERT_TRUE(err(validateAndExecute(command))); + ASSERT_TRUE(err(validate(command))); } /** @@ -1889,20 +1198,7 @@ TEST_F(AppendRoleTest, InvalidCaseRoleHasNoPermissions) { EXPECT_CALL(*wsv_query, getRolePermissions(kMasterRole)) .WillOnce(Return(role_permissions)); - ASSERT_TRUE(err(validateAndExecute(command))); -} - -/** - * @given AppendRole - * @when command tries to append role, but insertion of account fails - * @then execute() fails - */ -TEST_F(AppendRoleTest, InvalidCaseInsertAccountRoleFails) { - EXPECT_CALL( - *wsv_command, - insertAccountRole(append_role->accountId(), append_role->roleName())) - .WillOnce(Return(makeEmptyError())); - ASSERT_TRUE(err(execute(command))); + ASSERT_TRUE(err(validate(command))); } class DetachRoleTest : public CommandValidateExecuteTest { @@ -1931,11 +1227,7 @@ TEST_F(DetachRoleTest, ValidCase) { .WillOnce(Return(admin_roles)); EXPECT_CALL(*wsv_query, getRolePermissions(kAdminRole)) .WillOnce(Return(role_permissions)); - EXPECT_CALL( - *wsv_command, - deleteAccountRole(detach_role->accountId(), detach_role->roleName())) - .WillOnce(Return(WsvCommandResult())); - ASSERT_TRUE(val(validateAndExecute(command))); + ASSERT_TRUE(val(validate(command))); } /** @@ -1948,20 +1240,7 @@ TEST_F(DetachRoleTest, InvalidCase) { .WillOnce(Return(admin_roles)); EXPECT_CALL(*wsv_query, getRolePermissions(kAdminRole)) .WillOnce(Return(boost::none)); - ASSERT_TRUE(err(validateAndExecute(command))); -} - -/** - * @given DetachRole - * @when deletion of account role fails - * @then execute fails() - */ -TEST_F(DetachRoleTest, InvalidCaseWhenDeleteAccountRoleFails) { - EXPECT_CALL( - *wsv_command, - deleteAccountRole(detach_role->accountId(), detach_role->roleName())) - .WillOnce(Return(makeEmptyError())); - ASSERT_TRUE(err(execute(command))); + ASSERT_TRUE(err(validate(command))); } class GrantPermissionTest : public CommandValidateExecuteTest { @@ -1992,12 +1271,7 @@ TEST_F(GrantPermissionTest, ValidCase) { .WillOnce(Return(admin_roles)); EXPECT_CALL(*wsv_query, getRolePermissions(kAdminRole)) .WillOnce(Return(role_permissions)); - EXPECT_CALL(*wsv_command, - insertAccountGrantablePermission(grant_permission->accountId(), - creator->accountId(), - expected_permission)) - .WillOnce(Return(WsvCommandResult())); - ASSERT_TRUE(val(validateAndExecute(command))); + ASSERT_TRUE(val(validate(command))); } /** @@ -2010,21 +1284,7 @@ TEST_F(GrantPermissionTest, InvalidCaseWhenNoPermissions) { .WillOnce(Return(admin_roles)); EXPECT_CALL(*wsv_query, getRolePermissions(kAdminRole)) .WillOnce(Return(boost::none)); - ASSERT_TRUE(err(validateAndExecute(command))); -} - -/** - * @given GrantPermission - * @when command tries to grant permission but insertion fails - * @then execute() fails - */ -TEST_F(GrantPermissionTest, InvalidCaseWhenInsertGrantablePermissionFails) { - EXPECT_CALL(*wsv_command, - insertAccountGrantablePermission(grant_permission->accountId(), - creator->accountId(), - expected_permission)) - .WillOnce(Return(makeEmptyError())); - ASSERT_TRUE(err(execute(command))); + ASSERT_TRUE(err(validate(command))); } class RevokePermissionTest : public CommandValidateExecuteTest { @@ -2056,12 +1316,7 @@ TEST_F(RevokePermissionTest, ValidCase) { hasAccountGrantablePermission( revoke_permission->accountId(), kAdminId, expected_permission)) .WillOnce(Return(true)); - EXPECT_CALL(*wsv_command, - deleteAccountGrantablePermission(revoke_permission->accountId(), - creator->accountId(), - expected_permission)) - .WillOnce(Return(WsvCommandResult())); - ASSERT_TRUE(val(validateAndExecute(command))); + ASSERT_TRUE(val(validate(command))); } /** @@ -2075,21 +1330,7 @@ TEST_F(RevokePermissionTest, InvalidCaseNoPermissions) { hasAccountGrantablePermission( revoke_permission->accountId(), kAdminId, expected_permission)) .WillOnce(Return(false)); - ASSERT_TRUE(err(validateAndExecute(command))); -} - -/** - * @given RevokePermission - * @when deleting permission fails - * @then execute fails - */ -TEST_F(RevokePermissionTest, InvalidCaseDeleteAccountPermissionvFails) { - EXPECT_CALL(*wsv_command, - deleteAccountGrantablePermission(revoke_permission->accountId(), - creator->accountId(), - expected_permission)) - .WillOnce(Return(makeEmptyError())); - ASSERT_TRUE(err(execute(command))); + ASSERT_TRUE(err(validate(command))); } class SetAccountDetailTest : public CommandValidateExecuteTest { @@ -2119,13 +1360,7 @@ class SetAccountDetailTest : public CommandValidateExecuteTest { * @then successfully execute the command */ TEST_F(SetAccountDetailTest, ValidWhenSetOwnAccount) { - EXPECT_CALL(*wsv_command, - setAccountKV(set_account_detail->accountId(), - creator->accountId(), - set_account_detail->key(), - set_account_detail->value())) - .WillOnce(Return(WsvCommandResult())); - ASSERT_TRUE(val(validateAndExecute(command))); + ASSERT_TRUE(val(validate(command))); } /** @@ -2146,7 +1381,7 @@ TEST_F(SetAccountDetailTest, InvalidWhenOtherCreator) { hasAccountGrantablePermission( kAdminId, set_account_detail->accountId(), kNeededPermission)) .WillOnce(Return(false)); - ASSERT_TRUE(err(validateAndExecute(command))); + ASSERT_TRUE(err(validate(command))); } /** @@ -2166,13 +1401,7 @@ TEST_F(SetAccountDetailTest, ValidWhenHasRolePermission) { EXPECT_CALL(*wsv_query, getRolePermissions(kAdminRole)) .WillOnce(Return(role_permissions)); - EXPECT_CALL(*wsv_command, - setAccountKV(set_account_detail->accountId(), - creator->accountId(), - set_account_detail->key(), - set_account_detail->value())) - .WillOnce(Return(WsvCommandResult())); - ASSERT_TRUE(val(validateAndExecute(command))); + ASSERT_TRUE(val(validate(command))); } /** @@ -2193,26 +1422,6 @@ TEST_F(SetAccountDetailTest, ValidWhenHasGrantblePermission) { hasAccountGrantablePermission( kAdminId, set_account_detail->accountId(), kNeededPermission)) .WillOnce(Return(true)); - EXPECT_CALL(*wsv_command, - setAccountKV(set_account_detail->accountId(), - creator->accountId(), - set_account_detail->key(), - set_account_detail->value())) - .WillOnce(Return(WsvCommandResult())); - ASSERT_TRUE(val(validateAndExecute(command))); + ASSERT_TRUE(val(validate(command))); } -/** - * @given SetAccountDetail - * @when command tries to set details, but setting key-value fails - * @then execute fails - */ -TEST_F(SetAccountDetailTest, InvalidWhenSetAccountKVFails) { - EXPECT_CALL(*wsv_command, - setAccountKV(set_account_detail->accountId(), - creator->accountId(), - set_account_detail->key(), - set_account_detail->value())) - .WillOnce(Return(makeEmptyError())); - ASSERT_TRUE(err(execute(command))); -} diff --git a/test/module/irohad/simulator/simulator_test.cpp b/test/module/irohad/simulator/simulator_test.cpp index b8ee71a404..a6e5f4bf1c 100644 --- a/test/module/irohad/simulator/simulator_test.cpp +++ b/test/module/irohad/simulator/simulator_test.cpp @@ -289,9 +289,9 @@ TEST_F(SimulatorTest, RightNumberOfFailedTxs) { .transactions(std::vector{tx}) .build()); auto tx_errors = iroha::validation::TransactionsErrors{ - std::make_pair(CommandError{"SomeCommand", "SomeError", true}, + std::make_pair(validation::CommandError{"SomeCommand", "SomeError", true}, shared_model::crypto::Hash(std::string(32, '0'))), - std::make_pair(CommandError{"SomeCommand", "SomeError", true}, + std::make_pair(validation::CommandError{"SomeCommand", "SomeError", true}, shared_model::crypto::Hash(std::string(32, '0')))}; shared_model::proto::Block block = makeBlock(proposal->height() - 1); diff --git a/test/module/shared_model/builders/protobuf/test_account_asset_builder.hpp b/test/module/shared_model/builders/protobuf/test_account_asset_builder.hpp index d7afc38019..ca7eb86c5d 100644 --- a/test/module/shared_model/builders/protobuf/test_account_asset_builder.hpp +++ b/test/module/shared_model/builders/protobuf/test_account_asset_builder.hpp @@ -24,6 +24,6 @@ * Builder alias, to build shared model proto block object avoiding validation * and "required fields" check */ -using TestAccountAssetBuilder = shared_model::proto::AccountAsset; +using TestAccountAssetBuilder = shared_model::proto::AccountAssetBuilder; #endif //IROHA_TEST_ACCOUNT_BUILDER_HPP diff --git a/test/module/shared_model/builders/protobuf/test_asset_builder.hpp b/test/module/shared_model/builders/protobuf/test_asset_builder.hpp new file mode 100644 index 0000000000..3ef9a470f3 --- /dev/null +++ b/test/module/shared_model/builders/protobuf/test_asset_builder.hpp @@ -0,0 +1,29 @@ +/** + * Copyright Soramitsu Co., Ltd. 2018 All Rights Reserved. + * http://soramitsu.co.jp + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef IROHA_TEST_ASSET_BUILDER_HPP +#define IROHA_TEST_ASSET_BUILDER_HPP + +#include "builders/protobuf/common_objects/proto_asset_builder.hpp" + +/** + * Builder alias, to build shared model proto block object avoiding validation + * and "required fields" check + */ +using TestAccountAssetBuilder = shared_model::proto::AssetBuilder; + +#endif //IROHA_TEST_ASSET_BUILDER_HPP From b50132e507ea26d5e3d4a20e588ccabc0634bdf2 Mon Sep 17 00:00:00 2001 From: Kitsu Date: Mon, 23 Jul 2018 18:04:49 +0300 Subject: [PATCH 65/97] Fix develop merge issue (#1588) Signed-off-by: Kitsu --- irohad/ametsuchi/impl/postgres_command_executor.cpp | 2 +- irohad/ametsuchi/impl/postgres_command_executor.hpp | 3 +-- .../module/irohad/ametsuchi/postgres_executor_test.cpp | 10 +++++++--- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/irohad/ametsuchi/impl/postgres_command_executor.cpp b/irohad/ametsuchi/impl/postgres_command_executor.cpp index 8f093b23b4..f29018d8be 100644 --- a/irohad/ametsuchi/impl/postgres_command_executor.cpp +++ b/irohad/ametsuchi/impl/postgres_command_executor.cpp @@ -7,7 +7,7 @@ #include -#include "ametsuchi/impl/postgres_wsv_common.hpp" +#include "ametsuchi/impl/soci_utils.hpp" #include "backend/protobuf/permissions.hpp" #include "interfaces/commands/add_asset_quantity.hpp" #include "interfaces/commands/add_peer.hpp" diff --git a/irohad/ametsuchi/impl/postgres_command_executor.hpp b/irohad/ametsuchi/impl/postgres_command_executor.hpp index 79ad1522b1..4c4441b96d 100644 --- a/irohad/ametsuchi/impl/postgres_command_executor.hpp +++ b/irohad/ametsuchi/impl/postgres_command_executor.hpp @@ -7,8 +7,7 @@ #define IROHA_POSTGRES_COMMAND_EXECUTOR_HPP #include "ametsuchi/command_executor.hpp" - -#include "ametsuchi/impl/postgres_wsv_common.hpp" +#include "ametsuchi/impl/soci_utils.hpp" namespace iroha { namespace ametsuchi { diff --git a/test/module/irohad/ametsuchi/postgres_executor_test.cpp b/test/module/irohad/ametsuchi/postgres_executor_test.cpp index fe19034dbe..7cff9aba81 100644 --- a/test/module/irohad/ametsuchi/postgres_executor_test.cpp +++ b/test/module/irohad/ametsuchi/postgres_executor_test.cpp @@ -42,7 +42,10 @@ namespace iroha { AmetsuchiTest::SetUp(); sql = std::make_unique(soci::postgresql, pgopt_); - query = std::make_unique(*sql); + auto factory = + std::make_shared>(); + query = std::make_unique(*sql, factory); executor = std::make_unique(*sql); *sql << init_; @@ -173,8 +176,9 @@ namespace iroha { */ TEST_F(AddAccountAssetTest, AddAccountAssetTestUint256Overflow) { std::string uint256_halfmax = - "578960446186580977117854925043439539266349923328202820197287920039565648" - "19966.0"; // 2**255 - 2tra + "57896044618658097711785492504343953926634992332820282019728792003956" + "5648" + "19966.0"; // 2**255 - 2tra addAsset(); ASSERT_TRUE(val( execute(buildCommand(TestTransactionBuilder() From 93983b49f6d96b56364b94400ac478a4d353c0ea Mon Sep 17 00:00:00 2001 From: Kitsu Date: Mon, 23 Jul 2018 20:28:15 +0300 Subject: [PATCH 66/97] Add mst documentation (#1566) Signed-off-by: Kitsu --- docs/source/api/queries.rst | 6 ++---- docs/source/core_concepts/glossary.rst | 22 +++++++++++++++------- docs/source/use_cases/index.rst | 16 ++++++++++++++++ 3 files changed, 33 insertions(+), 11 deletions(-) diff --git a/docs/source/api/queries.rst b/docs/source/api/queries.rst index 54e5459359..92db08badd 100644 --- a/docs/source/api/queries.rst +++ b/docs/source/api/queries.rst @@ -165,10 +165,8 @@ Get Pending Transactions Purpose ------- -.. TODO, igor-egorov, 2018-07-03, IR-1356, add a link to MST description here - -GetPendingTransactions is used for retrieving a list of pending (not fully signed) multisignature transactions -or batches of transactions issued by account of query creator. +GetPendingTransactions is used for retrieving a list of pending (not fully signed) `multisignature transactions <../core_concepts/glossary.html#multisignature-transactions>`_ +or `batches of transactions <../core_concepts/glossary.html#batch-of-transactions>`__ issued by account of query creator. Request Schema -------------- diff --git a/docs/source/core_concepts/glossary.rst b/docs/source/core_concepts/glossary.rst index 359ea0f9d7..99b1bd3d9e 100644 --- a/docs/source/core_concepts/glossary.rst +++ b/docs/source/core_concepts/glossary.rst @@ -189,6 +189,12 @@ Role A named abstraction that holds a set of `permissions <#permission>`__. +Signatory +========= + +Represents an entity that can confirm multisignature transactions for some `account <#account>`__. +It can be attached to account via `AddSignatory <../api/commands.html#add-signatory>`__ and detached via `RemoveSignatory <../api/commands.html#remove-signatory>`__. + Simulator ========= @@ -263,11 +269,8 @@ Transaction Status Set Pending Transactions ^^^^^^^^^^^^^^^^^^^^ -.. TODO igor-egorov, 2018-07-04, IR-1356, add here a link to MST docs - Any transaction that has lesser signatures at the moment than `quorum`_ of transaction creator account is considered as pending. -Pending transaction will be submitted for stateful validation as soon as multi signature mechanism (*to be documented*) -will collect required amount of signatures for quorum. +Pending transaction will be submitted for `stateful validation`_ as soon as `multisignature <#multisignature-transactions>`__ mechanism will collect required amount of signatures for quorum. Transaction that already has quorum of signatures can also be considered as pending in cases when the transaction is a part of `batch of transactions`_ and there is a not fully signed transaction. @@ -284,10 +287,8 @@ Batch meta contains batch type identifier (atomic or ordered) and a list of `red of all transactions within a batch. The order of hashes prescribes transactions sequence. -.. TODO igor-egorov, 2018-07-04, IR-1356, add here a link to MST docs - Batch can contain transactions created by different accounts. -Any transaction within a batch can require single or multiple signatures (depends on quorum set for an account of transaction creator). +Any transaction within a batch can require single or `multiple <#multisignature-transactions>`__ signatures (depends on quorum set for an account of transaction creator). At least one transaction inside a batch should have at least one signature to let the batch pass `stateful validation`_. Atomic Batch @@ -302,6 +303,13 @@ Ordered batch preserves only the sequence of transactions applying to a ledger. All the transactions that able to pass stateful validation within a batch will be applied to a ledger. Validation failure of one transaction would NOT directly imply the failure of the whole batch. +Multisignature Transactions +=========================== + +A transaction which has the `quorum`_ greater than one is considered as multisignature (also called mst). +To achieve `stateful validity <#stateful-validation>`__ the confirmation is required by the `signatories <#signatory>`__ of the creator account. +These participants need to send the same transaction with their signature. + Validator ========= diff --git a/docs/source/use_cases/index.rst b/docs/source/use_cases/index.rst index bc273be590..dbebce0152 100644 --- a/docs/source/use_cases/index.rst +++ b/docs/source/use_cases/index.rst @@ -81,6 +81,22 @@ For example, ``gangreen`` is a registered farmer ``tomato`` asset creator, he se We simplified asset creation to just a single command ``CreateAsset`` without the need to create complex smart contracts. One the major advantages of Hyperledger Iroha is in its ease, that allows developers to focus on the provided value of their applications. +Fund Management +--------------- + +With the support of multisignature transactions it is possible to maintain a fund by many managers. In that scheme investment can only be made after the confirmation of the quorum participants. + +Example +^^^^^^^ + +The fund assets should be held at one account. +Its signatories should be fund managers, who are dealing with investments and portfolio distributions. +That can be added via ``AddSignatory`` command. +All of the assets should be held within one account, which signatories represent the fund managers. +Thus the concrete exchanges can be performed with the multisignature transaction so that everyone will decide on a particular financial decision. +The one may confirm a deal by sending the original transaction and one of managers' signature. +Iroha will maintain the transaction sending so that the deal will not be completed until it receives the required number of confirmation, which is parametrized with the transaction quorum parameter. + Related Research ---------------- From 47c3bfa6c7f3244c76b08efebe819802c5cd1165 Mon Sep 17 00:00:00 2001 From: Fedor Muratov Date: Wed, 25 Jul 2018 13:00:37 +0300 Subject: [PATCH 67/97] Implement test batch builder (#1583) Add test batch creator which is useful for tests in MST Signed-off-by: Fedor Muratov --- shared_model/interfaces/transaction.hpp | 1 + test/framework/batch_helper.hpp | 94 +++++++++++++++++++ .../backend_proto/proto_batch_test.cpp | 78 +++++++++++++++ .../protobuf/test_transaction_builder.hpp | 23 ++++- 4 files changed, 194 insertions(+), 2 deletions(-) diff --git a/shared_model/interfaces/transaction.hpp b/shared_model/interfaces/transaction.hpp index 18d4222108..929de17b9a 100644 --- a/shared_model/interfaces/transaction.hpp +++ b/shared_model/interfaces/transaction.hpp @@ -82,6 +82,7 @@ namespace shared_model { [](auto &command) { return command.toString(); }) .append("batch_meta", batchMeta() ? batchMeta()->get()->toString() : "") + .append("reducedHash", reducedHash().toString()) .append("signatures") .appendAll(signatures(), [](auto &sig) { return sig.toString(); }) .finalize(); diff --git a/test/framework/batch_helper.hpp b/test/framework/batch_helper.hpp index 929747bcb1..ddcc6eeaf5 100644 --- a/test/framework/batch_helper.hpp +++ b/test/framework/batch_helper.hpp @@ -195,6 +195,100 @@ namespace framework { return framework::expected::val(result_batch).value().value; } + /** + * Namespace provides useful functions which are related to implementation + * but they are internal API + */ + namespace internal { + + /** + * Create list of hashes + * @tparam TxBuilderCollection - type of builder + * @param builders - initializer list which contains all builders + * @return vector with transactions hashes + */ + template + auto fetchReducedHashes( + const std::initializer_list &builders) { + std::vector hashes; + std::transform( + builders.begin(), + builders.end(), + std::back_inserter(hashes), + [](const auto &builder) { return builder.build().reducedHash(); }); + return hashes; + } + + /** + * Variadic to initializer list adapter + */ + template + auto fetchReducedHashes(const TxBuilders &... builders) { + return fetchReducedHashes({builders...}); + } + + /** + * Function create transactions from builder and passed hashes + * @tparam TxBuilder - type of one builder + * @param hashes - vector of hashes of transactions + * @param builders - initializer list of tx builders + * @return transactions with correct batch meta + */ + template + auto makeTxBatchCollection( + const std::vector &hashes, + std::initializer_list builders) { + shared_model::interface::types::SharedTxsCollectionType transactions; + + std::transform( + builders.begin(), + builders.end(), + std::back_inserter(transactions), + [&hashes](const auto &builder) { + return makePolyTxFromBuilder(builder.batchMeta( + shared_model::interface::types::BatchType::ATOMIC, hashes)); + }); + return transactions; + } + + /** + * Variadic to initializer list adapter + */ + template + auto makeTxBatchCollection(TxBuilders &&... builders) { + auto hashes = fetchReducedHashes(builders...); + return makeTxBatchCollection(std::move(hashes), + {std::forward(builders)...}); + } + } // namespace internal + + /** + * Create test batch from passed transaction builders + * @tparam TxBuilders - variadic types of tx builders + * @return shared_ptr for batch + */ + template + auto makeTestBatch(TxBuilders &&... builders) { + auto transactions = internal::makeTxBatchCollection( + std::forward(builders)...); + + using namespace shared_model::validation; + using TxValidator = + TransactionValidator>; + + using TxsValidator = + UnsignedTransactionsCollectionValidator; + + auto batch = + shared_model::interface::TransactionBatch::createTransactionBatch( + transactions, TxsValidator()); + + return std::make_shared( + framework::expected::val(batch).value().value); + } + } // namespace batch } // namespace framework diff --git a/test/module/shared_model/backend_proto/proto_batch_test.cpp b/test/module/shared_model/backend_proto/proto_batch_test.cpp index 2dbb7a860d..23870846ae 100644 --- a/test/module/shared_model/backend_proto/proto_batch_test.cpp +++ b/test/module/shared_model/backend_proto/proto_batch_test.cpp @@ -4,6 +4,8 @@ */ #include + +#include "builders/protobuf/transaction.hpp" #include "framework/batch_helper.hpp" #include "framework/result_fixture.hpp" #include "interfaces/iroha_internal/transaction_batch.hpp" @@ -191,3 +193,79 @@ TEST(TransactionBatchTest, BatchWithMissingSignatures) { << framework::expected::err(transaction_batch).value().error; ASSERT_FALSE(transaction_batch_val->value.hasAllSignatures()); } + +/** + * Create test transaction builder + * @param acc_quorum - quorum number for setAccountDetail + * @param created_time - time of creation + * @param quorum - tx quorum number + * @return test tx builder + */ +inline auto makeTxBuilder( + const shared_model::interface::types::QuorumType &acc_quorum = 1, + uint64_t created_time = iroha::time::now(), + uint8_t quorum = 3) { + return TestTransactionBuilder() + .createdTime(created_time) + .creatorAccountId("user@test") + .setAccountQuorum("user@test", acc_quorum) + .quorum(quorum); +} + +/** + * @given one tx-builder + * @when try to fetch hash + * @then got only one hash + */ +TEST(TransactionBatchTest, TemplateHasherOne) { + ASSERT_EQ( + 1, + framework::batch::internal::fetchReducedHashes(makeTxBuilder()).size()); +} + +/** + * @given 3 tx-builders + * @when try to fetch hashes + * @then got exactly 3 hashes + */ +TEST(TransactionBatchTest, TemplateHasherVariadic) { + ASSERT_EQ(3, + framework::batch::internal::fetchReducedHashes( + makeTxBuilder(), makeTxBuilder(), makeTxBuilder()) + .size()); +} + +/** + * @given one tx-builder + * @when try to create transaction + * @then got exactly one tx + */ +TEST(TransactionBatchTest, MakeTxBatchCollectionOne) { + ASSERT_EQ(1, + framework::batch::internal::makeTxBatchCollection(makeTxBuilder()) + .size()); +} + +/** + * @given three tx-builders + * @when try to create transaction collection + * @then got exactly 3 txes + */ +TEST(TransactionBatchTest, MakeTxBatchCollectionMany) { + ASSERT_EQ(3, + framework::batch::internal::makeTxBatchCollection( + makeTxBuilder(), makeTxBuilder(), makeTxBuilder()) + .size()); +} + +/** + * @given two tx-builders + * @when try to create batch + * @then batch contains two transactions + */ +TEST(TransactionBatchTest, CreateTestBatchTest) { + ASSERT_EQ(2, + framework::batch::makeTestBatch(makeTxBuilder(2), makeTxBuilder()) + ->transactions() + .size()); +} diff --git a/test/module/shared_model/builders/protobuf/test_transaction_builder.hpp b/test/module/shared_model/builders/protobuf/test_transaction_builder.hpp index 9b9f74da50..8edb5917da 100644 --- a/test/module/shared_model/builders/protobuf/test_transaction_builder.hpp +++ b/test/module/shared_model/builders/protobuf/test_transaction_builder.hpp @@ -18,9 +18,13 @@ #ifndef IROHA_TEST_TRANSACTION_BUILDER_HPP #define IROHA_TEST_TRANSACTION_BUILDER_HPP +#include + #include "builders/protobuf/builder_templates/transaction_template.hpp" #include "module/shared_model/validators/validators.hpp" +using ProtoTxType = shared_model::proto::Transaction; + /** * Builder alias, to build shared model proto transaction object avoiding * validation and "required fields" check @@ -28,12 +32,27 @@ using TestTransactionBuilder = shared_model::proto::TemplateTransactionBuilder< (1 << shared_model::proto::TemplateTransactionBuilder<>::total) - 1, shared_model::validation::AlwaysValidValidator, - shared_model::proto::Transaction>; + ProtoTxType>; +/** + * Builder for creating \class shared_model::proto::UnsignedWrapper of \class + * ProtoTxType + */ using TestUnsignedTransactionBuilder = shared_model::proto::TemplateTransactionBuilder< (1 << shared_model::proto::TemplateTransactionBuilder<>::total) - 1, shared_model::validation::AlwaysValidValidator, - shared_model::proto::UnsignedWrapper>; + shared_model::proto::UnsignedWrapper>; + +/** + * Wrapper for making shared_ptr on transaction from the passed builder + * @tparam Builder - universal reference type of the passed builder + * @param builder - instance of the passed builder + * @return shared_ptr to completed object + */ +template +inline auto makePolyTxFromBuilder(Builder &&builder) { + return std::make_shared(builder.build()); +} #endif // IROHA_TEST_TRANSACTION_BUILDER_HPP From dbf874a008ad0d29fa5f007308c08f44a01e7940 Mon Sep 17 00:00:00 2001 From: Fedor Muratov Date: Wed, 25 Jul 2018 13:20:55 +0300 Subject: [PATCH 68/97] Add fix for Signable::operator== (#1570) Fix the wrong behavior of signable::operator==. And provide tests for it. Fix unordered set checks in operator== Current solution consumes about N^2 operations. There is possible to improve it with O(N). Signed-off-by: Fedor Muratov --- shared_model/interfaces/base/signable.hpp | 6 +- test/module/shared_model/CMakeLists.txt | 8 ++ test/module/shared_model/interface_test.cpp | 89 +++++++++++++++++++++ 3 files changed, 101 insertions(+), 2 deletions(-) create mode 100644 test/module/shared_model/interface_test.cpp diff --git a/shared_model/interfaces/base/signable.hpp b/shared_model/interfaces/base/signable.hpp index 11bcb1162f..bdacab65ae 100644 --- a/shared_model/interfaces/base/signable.hpp +++ b/shared_model/interfaces/base/signable.hpp @@ -80,8 +80,10 @@ namespace shared_model { */ bool operator==(const Model &rhs) const override { return this->hash() == rhs.hash() - and boost::equal(this->signatures(), rhs.signatures()) - and this->createdTime() == rhs.createdTime(); + // is_permutation consumes ~O(N^2) + and std::is_permutation(signatures().begin(), + signatures().end(), + rhs.signatures().begin()); } const types::HashType &hash() const { diff --git a/test/module/shared_model/CMakeLists.txt b/test/module/shared_model/CMakeLists.txt index aac86f3bde..09e2b5933a 100644 --- a/test/module/shared_model/CMakeLists.txt +++ b/test/module/shared_model/CMakeLists.txt @@ -41,3 +41,11 @@ target_link_libraries(amount_utils_test shared_model_default_builders shared_model_amount_utils ) + +AddTest(interface_test + interface_test.cpp + ) +target_link_libraries(interface_test + shared_model_default_builders + logger + ) diff --git a/test/module/shared_model/interface_test.cpp b/test/module/shared_model/interface_test.cpp new file mode 100644 index 0000000000..99ec56e7c1 --- /dev/null +++ b/test/module/shared_model/interface_test.cpp @@ -0,0 +1,89 @@ +/** + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "logger/logger.hpp" + +#include "builders/protobuf/transaction.hpp" + +class TransactionFixture : public ::testing::Test { + public: + TransactionFixture() + : keypair(shared_model::crypto::DefaultCryptoAlgorithmType:: + generateKeypair()), + time(iroha::time::now()) {} + + shared_model::crypto::Keypair keypair; + shared_model::interface::types::TimestampType time; + + logger::Logger log = logger::log("TransactionFixture"); + + auto makeTx() { + log->info("keypair = {}, timestemp = {}", keypair.toString(), time); + return std::make_shared( + shared_model::proto::TransactionBuilder() + .createdTime(time) + .creatorAccountId("user@test") + .setAccountQuorum("user@test", 1u) + .quorum(1) + .build() + .signAndAddSignature(keypair) + .finish()); + } +}; + +/** + * @given two same transactions + * @when nothing to do + * @then checks that transactions are the same + */ +TEST_F(TransactionFixture, checkEqualsOperatorObvious) { + auto tx1 = makeTx(); + auto tx2 = makeTx(); + ASSERT_EQ(*tx1, *tx2); +} + +/** + * @given two same transactions + * @when add same signatures to them + * @then checks that transactions are the same + */ +TEST_F(TransactionFixture, checkEqualsOperatorSameOrder) { + auto tx1 = makeTx(); + auto tx2 = makeTx(); + + tx1->addSignature(shared_model::crypto::Signed("signed_blob"), + shared_model::crypto::PublicKey("pub_key")); + tx2->addSignature(shared_model::crypto::Signed("signed_blob"), + shared_model::crypto::PublicKey("pub_key")); + + ASSERT_EQ(*tx1, *tx2); +} + +/** + * @given two same transactions + * @when add N signatures to first one and same but in reverse order to second + * @then checks that transactions are the same + */ +TEST_F(TransactionFixture, checkEqualsOperatorDifferentOrder) { + auto tx1 = makeTx(); + auto tx2 = makeTx(); + + auto N = 5; + + for (int i = 0; i < N; ++i) { + tx1->addSignature( + shared_model::crypto::Signed("signed_blob_" + std::to_string(i)), + shared_model::crypto::PublicKey("pub_key_" + std::to_string(i))); + } + + for (int i = N - 1; i >= 0; --i) { + tx2->addSignature( + shared_model::crypto::Signed("signed_blob_" + std::to_string(i)), + shared_model::crypto::PublicKey("pub_key_" + std::to_string(i))); + } + + ASSERT_EQ(*tx1, *tx2); +} From 6a8eb631bc5214014ea34f4d5f16f567e70e0171 Mon Sep 17 00:00:00 2001 From: Akvinikym Date: Wed, 25 Jul 2018 14:26:27 +0300 Subject: [PATCH 69/97] Command Service Empty TxList Fix (#1596) Signed-off-by: Akvinikym --- irohad/torii/impl/command_service.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/irohad/torii/impl/command_service.cpp b/irohad/torii/impl/command_service.cpp index 9278e8d337..101e237b86 100644 --- a/irohad/torii/impl/command_service.cpp +++ b/irohad/torii/impl/command_service.cpp @@ -161,6 +161,10 @@ namespace torii { }, [this, &tx_list](auto &error) { auto &txs = tx_list.transactions(); + if (txs.empty()) { + log_->warn("Received empty transaction sequence"); + return; + } // form an error message, shared between all txs in a sequence auto first_tx_blob = shared_model::proto::makeBlob(txs[0].payload()); From 98e95c8b5413ad97e72fef4084234b0a22861931 Mon Sep 17 00:00:00 2001 From: Kitsu Date: Wed, 25 Jul 2018 14:27:39 +0300 Subject: [PATCH 70/97] Add database removal in Storage::dropStorage (#1584) * Add database removal in Storage::dropStorage * Introduce StorageImpl::Deleter * Create queries via safe StorageImpl::setupQuery Signed-off-by: Kitsu --- .../ametsuchi/impl/postgres_block_query.cpp | 23 +- .../ametsuchi/impl/postgres_block_query.hpp | 21 +- irohad/ametsuchi/impl/postgres_wsv_query.cpp | 8 - irohad/ametsuchi/impl/postgres_wsv_query.hpp | 6 - irohad/ametsuchi/impl/storage_impl.cpp | 198 +++++++++++++----- irohad/ametsuchi/impl/storage_impl.hpp | 32 +-- irohad/ametsuchi/impl/temporary_wsv_impl.cpp | 22 +- irohad/ametsuchi/impl/wsv_restorer_impl.cpp | 6 +- irohad/ametsuchi/storage.hpp | 21 +- irohad/main/application.cpp | 3 +- .../integration_framework/iroha_instance.cpp | 2 +- .../irohad/ametsuchi/ametsuchi_fixture.hpp | 53 +---- .../irohad/ametsuchi/ametsuchi_mocks.hpp | 1 + .../irohad/ametsuchi/ametsuchi_test.cpp | 77 +------ 14 files changed, 190 insertions(+), 283 deletions(-) diff --git a/irohad/ametsuchi/impl/postgres_block_query.cpp b/irohad/ametsuchi/impl/postgres_block_query.cpp index c3214348de..50569d631e 100644 --- a/irohad/ametsuchi/impl/postgres_block_query.cpp +++ b/irohad/ametsuchi/impl/postgres_block_query.cpp @@ -1,18 +1,6 @@ /** - * Copyright Soramitsu Co., Ltd. 2018 All Rights Reserved. - * http://soramitsu.co.jp - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 */ #include "ametsuchi/impl/postgres_block_query.hpp" @@ -31,13 +19,6 @@ namespace iroha { block_store_(file_store), log_(logger::log("PostgresBlockIndex")) {} - PostgresBlockQuery::PostgresBlockQuery( - std::unique_ptr sql_ptr, KeyValueStorage &file_store) - : sql_ptr_(std::move(sql_ptr)), - sql_(*sql_ptr_), - block_store_(file_store), - log_(logger::log("PostgresBlockIndex")) {} - rxcpp::observable PostgresBlockQuery::getBlocks( shared_model::interface::types::HeightType height, uint32_t count) { shared_model::interface::types::HeightType last_id = diff --git a/irohad/ametsuchi/impl/postgres_block_query.hpp b/irohad/ametsuchi/impl/postgres_block_query.hpp index 1d6e1cd6ca..1f37435dd9 100644 --- a/irohad/ametsuchi/impl/postgres_block_query.hpp +++ b/irohad/ametsuchi/impl/postgres_block_query.hpp @@ -1,18 +1,6 @@ /** - * Copyright Soramitsu Co., Ltd. 2018 All Rights Reserved. - * http://soramitsu.co.jp - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 */ #ifndef IROHA_POSTGRES_FLAT_BLOCK_QUERY_HPP @@ -22,8 +10,8 @@ #include "ametsuchi/block_query.hpp" #include "ametsuchi/impl/flat_file/flat_file.hpp" -#include "logger/logger.hpp" #include "ametsuchi/impl/soci_utils.hpp" +#include "logger/logger.hpp" namespace iroha { namespace ametsuchi { @@ -37,8 +25,6 @@ namespace iroha { public: explicit PostgresBlockQuery(soci::session &sql, KeyValueStorage &file_store); - explicit PostgresBlockQuery(std::unique_ptr sql_ptr, - KeyValueStorage &file_store); rxcpp::observable getAccountTransactions( const shared_model::interface::types::AccountIdType &account_id) @@ -96,7 +82,6 @@ namespace iroha { std::function &result)> callback( const rxcpp::subscriber &s, uint64_t block_id); - std::unique_ptr sql_ptr_; soci::session &sql_; KeyValueStorage &block_store_; diff --git a/irohad/ametsuchi/impl/postgres_wsv_query.cpp b/irohad/ametsuchi/impl/postgres_wsv_query.cpp index 224da8775f..0f602cb5bb 100644 --- a/irohad/ametsuchi/impl/postgres_wsv_query.cpp +++ b/irohad/ametsuchi/impl/postgres_wsv_query.cpp @@ -68,14 +68,6 @@ namespace iroha { std::shared_ptr factory) : sql_(sql), factory_(factory), log_(logger::log("PostgresWsvQuery")) {} - PostgresWsvQuery::PostgresWsvQuery( - std::unique_ptr sql_ptr, - std::shared_ptr factory) - : sql_ptr_(std::move(sql_ptr)), - sql_(*sql_ptr_), - factory_(factory), - log_(logger::log("PostgresWsvQuery")) {} - bool PostgresWsvQuery::hasAccountGrantablePermission( const AccountIdType &permitee_account_id, const AccountIdType &account_id, diff --git a/irohad/ametsuchi/impl/postgres_wsv_query.hpp b/irohad/ametsuchi/impl/postgres_wsv_query.hpp index 49795a08dd..536e780a62 100644 --- a/irohad/ametsuchi/impl/postgres_wsv_query.hpp +++ b/irohad/ametsuchi/impl/postgres_wsv_query.hpp @@ -34,11 +34,6 @@ namespace iroha { std::shared_ptr factory); - PostgresWsvQuery( - std::unique_ptr sql_ptr, - std::shared_ptr - factory); - boost::optional> getAccountRoles(const shared_model::interface::types::AccountIdType &account_id) override; @@ -92,7 +87,6 @@ namespace iroha { shared_model::interface::permissions::Grantable permission) override; private: - std::unique_ptr sql_ptr_; soci::session &sql_; std::shared_ptr factory_; logger::Logger log_; diff --git a/irohad/ametsuchi/impl/storage_impl.cpp b/irohad/ametsuchi/impl/storage_impl.cpp index 32c6fd4afa..a040bda4c3 100644 --- a/irohad/ametsuchi/impl/storage_impl.cpp +++ b/irohad/ametsuchi/impl/storage_impl.cpp @@ -1,24 +1,12 @@ /** - * Copyright Soramitsu Co., Ltd. 2018 All Rights Reserved. - * http://soramitsu.co.jp - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 */ #include "ametsuchi/impl/storage_impl.hpp" -#include #include +#include #include "ametsuchi/impl/flat_file/flat_file.hpp" #include "ametsuchi/impl/mutable_storage_impl.hpp" @@ -40,20 +28,28 @@ namespace iroha { std::unique_ptr block_store) : block_store(std::move(block_store)) {} - StorageImpl::StorageImpl(std::string block_store_dir, - PostgresOptions postgres_options, - std::unique_ptr block_store, - std::shared_ptr connection, - std::shared_ptr factory) + StorageImpl::StorageImpl( + std::string block_store_dir, + PostgresOptions postgres_options, + std::unique_ptr block_store, + std::shared_ptr connection, + std::shared_ptr factory) : block_store_dir_(std::move(block_store_dir)), postgres_options_(std::move(postgres_options)), block_store_(std::move(block_store)), connection_(connection), factory_(factory), - log_(logger::log("StorageImpl")) {} + log_(logger::log("StorageImpl")) { + soci::session sql(*connection_); + sql << init_; + } expected::Result, std::string> StorageImpl::createTemporaryWsv() { + std::shared_lock lock(drop_mutex); + if (connection_ == nullptr) { + return expected::makeError("Connection was closed"); + } auto sql = std::make_unique(*connection_); return expected::makeValue>( @@ -64,6 +60,11 @@ namespace iroha { StorageImpl::createMutableStorage() { boost::optional top_hash; + std::shared_lock lock(drop_mutex); + if (connection_ == nullptr) { + return expected::makeError("Connection was closed"); + } + auto sql = std::make_unique(*connection_); auto block_result = getBlockQuery()->getTopBlock(); return expected::makeValue>( @@ -128,32 +129,40 @@ namespace iroha { return inserted; } - void StorageImpl::dropStorage() { - log_->info("Drop ledger"); - auto drop = R"( -DROP TABLE IF EXISTS account_has_signatory; -DROP TABLE IF EXISTS account_has_asset; -DROP TABLE IF EXISTS role_has_permissions CASCADE; -DROP TABLE IF EXISTS account_has_roles; -DROP TABLE IF EXISTS account_has_grantable_permissions CASCADE; -DROP TABLE IF EXISTS account; -DROP TABLE IF EXISTS asset; -DROP TABLE IF EXISTS domain; -DROP TABLE IF EXISTS signatory; -DROP TABLE IF EXISTS peer; -DROP TABLE IF EXISTS role; -DROP TABLE IF EXISTS height_by_hash; -DROP TABLE IF EXISTS height_by_account_set; -DROP TABLE IF EXISTS index_by_creator_height; -DROP TABLE IF EXISTS index_by_id_height_asset; -)"; - + void StorageImpl::reset() { // erase db log_->info("drop db"); soci::session sql(*connection_); - sql << drop; - sql << init_; + sql << reset_; + } + + void StorageImpl::dropStorage() { + log_->info("drop storage"); + if (connection_ == nullptr) { + log_->warn("Tried to drop storage without active connection"); + return; + } + + if (auto dbname = postgres_options_.dbname()) { + auto &db = dbname.value(); + std::unique_lock lock(drop_mutex); + log_->info("Drop database {}", db); + connection_.reset(); + soci::session sql(soci::postgresql, + postgres_options_.optionsStringWithoutDbName()); + // kill active connections + sql << R"( +SELECT pg_terminate_backend(pg_stat_activity.pid) +FROM pg_stat_activity +WHERE pg_stat_activity.datname = :dbname + AND pid <> pg_backend_pid();)", + soci::use(dbname.value()); + // perform dropping + sql << "DROP DATABASE " + db; + } else { + soci::session(*connection_) << drop_; + } // erase blocks log_->info("drop block store"); @@ -259,7 +268,6 @@ DROP TABLE IF EXISTS index_by_id_height_asset; } void StorageImpl::commit(std::unique_ptr mutableStorage) { - std::unique_lock write(rw_lock_); auto storage_ptr = std::move(mutableStorage); // get ownership of storage auto storage = static_cast(storage_ptr.get()); for (const auto &block : storage->block_store_) { @@ -275,17 +283,69 @@ DROP TABLE IF EXISTS index_by_id_height_asset; storage->committed = true; } + namespace { + /** + * Deleter for an object which uses connection_pool + * @tparam Query object type to delete + */ + template + class Deleter { + public: + Deleter(std::shared_ptr conn, size_t pool_pos) + : conn_(std::move(conn)), pool_pos_(pool_pos) {} + + void operator()(Query *q) const { + if (conn_ != nullptr) { + conn_->give_back(pool_pos_); + } + delete q; + } + + private: + std::shared_ptr conn_; + const size_t pool_pos_; + }; + + /** + * Factory method for query object creation which uses connection_pool + * @tparam Query object type to create + * @tparam Backend object type to use as a backend for Query + * @param b is a backend obj + * @param conn is pointer to connection pool for getting and releaseing + * the session + * @param log is a logger + * @param drop_mutex is mutex for preventing connection destruction + * during the function + * @return pointer to created query object + * note: blocks untils connection can be leased from the pool + */ + template + std::shared_ptr setupQuery( + Backend &b, + std::shared_ptr conn, + const logger::Logger &log, + std::shared_timed_mutex &drop_mutex) { + std::shared_lock lock(drop_mutex); + if (conn == nullptr) { + log->warn("Storage was deleted, cannot perform setup"); + return nullptr; + } + auto pool_pos = conn->lease(); + soci::session &session = conn->at(pool_pos); + lock.unlock(); + return {new Query(session, b), + Deleter(std::move(conn), pool_pos)}; + } + } // namespace + std::shared_ptr StorageImpl::getWsvQuery() const { - auto sql = std::make_unique(*connection_); - return std::make_shared(std::move(sql), factory_); + return setupQuery( + factory_, connection_, log_, drop_mutex); } std::shared_ptr StorageImpl::getBlockQuery() const { - auto sql = std::make_unique( - soci::postgresql, postgres_options_.optionsString()); - - return std::make_shared(std::move(sql), - *block_store_); + return setupQuery( + *block_store_, connection_, log_, drop_mutex); } rxcpp::observable> @@ -293,6 +353,42 @@ DROP TABLE IF EXISTS index_by_id_height_asset; return notifier_.get_observable(); } + const std::string &StorageImpl::drop_ = R"( +DROP TABLE IF EXISTS account_has_signatory; +DROP TABLE IF EXISTS account_has_asset; +DROP TABLE IF EXISTS role_has_permissions CASCADE; +DROP TABLE IF EXISTS account_has_roles; +DROP TABLE IF EXISTS account_has_grantable_permissions CASCADE; +DROP TABLE IF EXISTS account; +DROP TABLE IF EXISTS asset; +DROP TABLE IF EXISTS domain; +DROP TABLE IF EXISTS signatory; +DROP TABLE IF EXISTS peer; +DROP TABLE IF EXISTS role; +DROP TABLE IF EXISTS height_by_hash; +DROP TABLE IF EXISTS height_by_account_set; +DROP TABLE IF EXISTS index_by_creator_height; +DROP TABLE IF EXISTS index_by_id_height_asset; +)"; + + const std::string &StorageImpl::reset_ = R"( +DELETE FROM account_has_signatory; +DELETE FROM account_has_asset; +DELETE FROM role_has_permissions CASCADE; +DELETE FROM account_has_roles; +DELETE FROM account_has_grantable_permissions CASCADE; +DELETE FROM account; +DELETE FROM asset; +DELETE FROM domain; +DELETE FROM signatory; +DELETE FROM peer; +DELETE FROM role; +DELETE FROM height_by_hash; +DELETE FROM height_by_account_set; +DELETE FROM index_by_creator_height; +DELETE FROM index_by_id_height_asset; +)"; + const std::string &StorageImpl::init_ = R"( CREATE TABLE IF NOT EXISTS role ( diff --git a/irohad/ametsuchi/impl/storage_impl.hpp b/irohad/ametsuchi/impl/storage_impl.hpp index 9a07060aed..62567f64ae 100644 --- a/irohad/ametsuchi/impl/storage_impl.hpp +++ b/irohad/ametsuchi/impl/storage_impl.hpp @@ -1,18 +1,6 @@ /** - * Copyright Soramitsu Co., Ltd. 2017 All Rights Reserved. - * http://soramitsu.co.jp - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 */ #ifndef IROHA_STORAGE_IMPL_HPP @@ -73,19 +61,20 @@ namespace iroha { * @param blocks - block for insertion * @return true if all blocks are inserted */ - virtual bool insertBlock( - const shared_model::interface::Block &block) override; + bool insertBlock(const shared_model::interface::Block &block) override; /** * Insert blocks without validation * @param blocks - collection of blocks for insertion * @return true if inserted */ - virtual bool insertBlocks( + bool insertBlocks( const std::vector> &blocks) override; - virtual void dropStorage() override; + void reset() override; + + void dropStorage() override; void commit(std::unique_ptr mutableStorage) override; @@ -117,9 +106,6 @@ namespace iroha { std::shared_ptr connection_; - // Allows multiple readers and a single writer - std::shared_timed_mutex rw_lock_; - std::shared_ptr factory_; rxcpp::subjects::subject> @@ -127,7 +113,11 @@ namespace iroha { logger::Logger log_; + mutable std::shared_timed_mutex drop_mutex; + protected: + static const std::string &drop_; + static const std::string &reset_; static const std::string &init_; }; } // namespace ametsuchi diff --git a/irohad/ametsuchi/impl/temporary_wsv_impl.cpp b/irohad/ametsuchi/impl/temporary_wsv_impl.cpp index 2ccb61b69b..3ccd781ae2 100644 --- a/irohad/ametsuchi/impl/temporary_wsv_impl.cpp +++ b/irohad/ametsuchi/impl/temporary_wsv_impl.cpp @@ -1,18 +1,6 @@ /** - * Copyright Soramitsu Co., Ltd. 2017 All Rights Reserved. - * http://soramitsu.co.jp - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 */ #include "ametsuchi/impl/temporary_wsv_impl.hpp" @@ -23,9 +11,9 @@ namespace iroha { namespace ametsuchi { - TemporaryWsvImpl::TemporaryWsvImpl(std::unique_ptr sql, - std::shared_ptr - factory) + TemporaryWsvImpl::TemporaryWsvImpl( + std::unique_ptr sql, + std::shared_ptr factory) : sql_(std::move(sql)), wsv_(std::make_shared(*sql_, factory)), executor_(std::make_shared(*sql_)), diff --git a/irohad/ametsuchi/impl/wsv_restorer_impl.cpp b/irohad/ametsuchi/impl/wsv_restorer_impl.cpp index be2e9938f3..1f7ebe6b8a 100644 --- a/irohad/ametsuchi/impl/wsv_restorer_impl.cpp +++ b/irohad/ametsuchi/impl/wsv_restorer_impl.cpp @@ -30,11 +30,9 @@ namespace iroha { // get all blocks starting from the genesis std::vector> blocks; storage.getBlockQuery()->getBlocksFrom(1).as_blocking().subscribe( - [&blocks](auto block) { - blocks.push_back(std::move(block)); - }); + [&blocks](auto block) { blocks.push_back(std::move(block)); }); - storage.dropStorage(); + storage.reset(); if (not storage.insertBlocks(blocks)) return expected::makeError("cannot insert blocks"); diff --git a/irohad/ametsuchi/storage.hpp b/irohad/ametsuchi/storage.hpp index f9d84b2959..5e13301d9a 100644 --- a/irohad/ametsuchi/storage.hpp +++ b/irohad/ametsuchi/storage.hpp @@ -1,18 +1,6 @@ /** - * Copyright Soramitsu Co., Ltd. 2017 All Rights Reserved. - * http://soramitsu.co.jp - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 */ #ifndef IROHA_AMETSUCHI_H @@ -70,6 +58,11 @@ namespace iroha { virtual rxcpp::observable> on_commit() = 0; + /** + * Remove all information without dropping the storage + */ + virtual void reset() = 0; + /** * Remove all information from ledger */ diff --git a/irohad/main/application.cpp b/irohad/main/application.cpp index 25cab42a8c..dc44bfc0f2 100644 --- a/irohad/main/application.cpp +++ b/irohad/main/application.cpp @@ -66,7 +66,6 @@ void Irohad::init() { initWsvRestorer(); restoreWsv(); - initPeerQuery(); initCryptoProvider(); initValidators(); initOrderingGate(); @@ -86,7 +85,7 @@ void Irohad::init() { * Dropping iroha daemon storage */ void Irohad::dropStorage() { - storage->dropStorage(); + storage->reset(); ordering_service_storage_->resetState(); } diff --git a/test/framework/integration_framework/iroha_instance.cpp b/test/framework/integration_framework/iroha_instance.cpp index ec73b6a149..3a4d4dd03d 100644 --- a/test/framework/integration_framework/iroha_instance.cpp +++ b/test/framework/integration_framework/iroha_instance.cpp @@ -43,7 +43,7 @@ namespace integration_framework { is_mst_supported_(mst_support) {} void IrohaInstance::makeGenesis(const shared_model::interface::Block &block) { - instance_->storage->dropStorage(); + instance_->storage->reset(); rawInsertBlock(block); instance_->init(); } diff --git a/test/module/irohad/ametsuchi/ametsuchi_fixture.hpp b/test/module/irohad/ametsuchi/ametsuchi_fixture.hpp index 0ee855250f..aea5b6a570 100644 --- a/test/module/irohad/ametsuchi/ametsuchi_fixture.hpp +++ b/test/module/irohad/ametsuchi/ametsuchi_fixture.hpp @@ -1,18 +1,6 @@ /** - * Copyright Soramitsu Co., Ltd. 2017 All Rights Reserved. - * http://soramitsu.co.jp - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 */ #ifndef IROHA_AMETSUCHI_FIXTURE_HPP @@ -39,20 +27,14 @@ namespace iroha { class AmetsuchiTest : public ::testing::Test { public: AmetsuchiTest() - : pgopt_(integration_framework::getPostgresCredsOrDefault() - + " dbname=" + dbname_) { + : pgopt_("dbname=" + dbname_ + " " + + integration_framework::getPostgresCredsOrDefault()) { auto log = logger::testLog("AmetsuchiTest"); boost::filesystem::create_directory(block_store_path); } protected: - virtual void clear() { - *sql << drop_; - - iroha::remove_dir_contents(block_store_path); - } - virtual void disconnect() { sql->close(); } @@ -70,21 +52,19 @@ namespace iroha { void SetUp() override { connect(); - storage->dropStorage(); } void TearDown() override { - clear(); - disconnect(); + storage->dropStorage(); } std::shared_ptr sql; std::shared_ptr> - factory = - std::make_shared>(); + factory = + std::make_shared>(); std::shared_ptr storage; @@ -100,23 +80,6 @@ namespace iroha { .string(); // TODO(warchant): IR-1019 hide SQLs under some interface - const std::string drop_ = R"( -DROP TABLE IF EXISTS account_has_signatory; -DROP TABLE IF EXISTS account_has_asset; -DROP TABLE IF EXISTS role_has_permissions; -DROP TABLE IF EXISTS account_has_roles; -DROP TABLE IF EXISTS account_has_grantable_permissions; -DROP TABLE IF EXISTS account; -DROP TABLE IF EXISTS asset; -DROP TABLE IF EXISTS domain; -DROP TABLE IF EXISTS signatory; -DROP TABLE IF EXISTS peer; -DROP TABLE IF EXISTS role; -DROP TABLE IF EXISTS height_by_hash; -DROP TABLE IF EXISTS height_by_account_set; -DROP TABLE IF EXISTS index_by_creator_height; -DROP TABLE IF EXISTS index_by_id_height_asset; -)"; const std::string init_ = R"( CREATE TABLE IF NOT EXISTS role ( diff --git a/test/module/irohad/ametsuchi/ametsuchi_mocks.hpp b/test/module/irohad/ametsuchi/ametsuchi_mocks.hpp index 691a2a26be..68202c7cae 100644 --- a/test/module/irohad/ametsuchi/ametsuchi_mocks.hpp +++ b/test/module/irohad/ametsuchi/ametsuchi_mocks.hpp @@ -266,6 +266,7 @@ namespace iroha { MOCK_METHOD1(insertBlocks, bool(const std::vector< std::shared_ptr> &)); + MOCK_METHOD0(reset, void(void)); MOCK_METHOD0(dropStorage, void(void)); rxcpp::observable> diff --git a/test/module/irohad/ametsuchi/ametsuchi_test.cpp b/test/module/irohad/ametsuchi/ametsuchi_test.cpp index 2296b9577e..5b3186c238 100644 --- a/test/module/irohad/ametsuchi/ametsuchi_test.cpp +++ b/test/module/irohad/ametsuchi/ametsuchi_test.cpp @@ -1,18 +1,6 @@ /** - * Copyright Soramitsu Co., Ltd. 2018 All Rights Reserved. - * http://soramitsu.co.jp - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 */ #include @@ -614,10 +602,6 @@ TEST_F(AmetsuchiTest, TestingStorageWhenInsertBlock) { ASSERT_NE(0, wsv->getPeers().value().size()); - log->info("Drop ledger"); - - storage->dropStorage(); - ASSERT_TRUE(wrapper.validate()); } @@ -651,66 +635,9 @@ TEST_F(AmetsuchiTest, TestingStorageWhenCommitBlock) { storage->commit(std::move(mutable_storage)); - storage->dropStorage(); - ASSERT_TRUE(wrapper.validate()); } -TEST_F(AmetsuchiTest, TestingStorageWhenDropAll) { - auto logger = logger::testLog("TestStorage"); - logger->info( - "Test case: create storage " - "=> insert block " - "=> assert that written" - " => drop all " - "=> assert that all deleted "); - - auto log = logger::testLog("TestStorage"); - log->info( - "Test case: create storage " - "=> insert block " - "=> assert that inserted"); - std::shared_ptr storage; - auto storageResult = StorageImpl::create(block_store_path, pgopt_, factory); - storageResult.match( - [&](iroha::expected::Value> &_storage) { - storage = _storage.value; - }, - [](iroha::expected::Error &error) { - FAIL() << "StorageImpl: " << error.error; - }); - ASSERT_TRUE(storage); - auto wsv = storage->getWsvQuery(); - ASSERT_EQ(0, wsv->getPeers().value().size()); - - log->info("Try insert block"); - - auto inserted = storage->insertBlock(getBlock()); - ASSERT_TRUE(inserted); - - log->info("Request ledger information"); - - ASSERT_NE(0, wsv->getPeers().value().size()); - - log->info("Drop ledger"); - - storage->dropStorage(); - - ASSERT_EQ(0, wsv->getPeers().value().size()); - std::shared_ptr new_storage; - auto new_storage_result = - StorageImpl::create(block_store_path, pgopt_, factory); - new_storage_result.match( - [&](iroha::expected::Value> &_storage) { - new_storage = _storage.value; - }, - [](iroha::expected::Error &error) { - FAIL() << "StorageImpl: " << error.error; - }); - ASSERT_EQ(0, wsv->getPeers().value().size()); - new_storage->dropStorage(); -} - /** * @given initialized storage * @when insert block with 2 transactions in From b2a5906d71851aa28d4ec36cd5f45f8c3c8a4656 Mon Sep 17 00:00:00 2001 From: Akvinikym Date: Wed, 25 Jul 2018 14:36:08 +0300 Subject: [PATCH 71/97] Ordering Service Batch Support (#1586) Signed-off-by: Akvinikym --- irohad/network/ordering_service_transport.hpp | 11 ++- .../ordering/impl/ordering_service_impl.cpp | 47 +++++++---- .../ordering/impl/ordering_service_impl.hpp | 30 +++++--- .../impl/ordering_service_transport_grpc.cpp | 59 +++++++++++++- .../impl/ordering_service_transport_grpc.hpp | 4 + .../irohad/ordering/ordering_service_test.cpp | 77 ++++++++++++------- 6 files changed, 167 insertions(+), 61 deletions(-) diff --git a/irohad/network/ordering_service_transport.hpp b/irohad/network/ordering_service_transport.hpp index e5ef06a9cf..98f80b8b97 100644 --- a/irohad/network/ordering_service_transport.hpp +++ b/irohad/network/ordering_service_transport.hpp @@ -19,7 +19,7 @@ #include #include "interfaces/iroha_internal/proposal.hpp" -#include "interfaces/transaction.hpp" +#include "interfaces/iroha_internal/transaction_batch.hpp" namespace iroha { namespace network { @@ -31,12 +31,11 @@ namespace iroha { class OrderingServiceNotification { public: /** - * Callback on receiving transaction - * @param transaction - transaction object itself + * Callback on receiving transaction(s) + * @param batch object, in which transaction(s) are packed */ - virtual void onTransaction( - std::shared_ptr - transaction) = 0; + virtual void onBatch( + shared_model::interface::TransactionBatch &&batch) = 0; virtual ~OrderingServiceNotification() = default; }; diff --git a/irohad/ordering/impl/ordering_service_impl.cpp b/irohad/ordering/impl/ordering_service_impl.cpp index 84bded1186..bd2ade255b 100644 --- a/irohad/ordering/impl/ordering_service_impl.cpp +++ b/irohad/ordering/impl/ordering_service_impl.cpp @@ -4,8 +4,10 @@ */ #include "ordering/impl/ordering_service_impl.hpp" + #include #include + #include "ametsuchi/ordering_service_persistent_state.hpp" #include "ametsuchi/peer_query.hpp" #include "backend/protobuf/proposal.hpp" @@ -25,8 +27,9 @@ namespace iroha { bool is_async) : wsv_(wsv), max_size_(max_size), + current_size_(0), transport_(transport), - persistent_state_(persistent_state) { + persistent_state_(persistent_state){ log_ = logger::log("OrderingServiceImpl"); // restore state of ordering service from persistent storage @@ -43,8 +46,8 @@ namespace iroha { switch (v) { case ProposalEvent::kTimerEvent: return not queue_.empty(); - case ProposalEvent::kTransactionEvent: - return queue_.unsafe_size() >= max_size_; + case ProposalEvent::kBatchEvent: + return current_size_.load() >= max_size_; default: BOOST_ASSERT_MSG(false, "Unknown value"); } @@ -64,29 +67,43 @@ namespace iroha { } } - void OrderingServiceImpl::onTransaction( - std::shared_ptr transaction) { - queue_.push(transaction); - log_->info("Queue size is {}", queue_.unsafe_size()); + void OrderingServiceImpl::onBatch( + shared_model::interface::TransactionBatch &&batch) { + std::shared_lock batch_prop_lock( + batch_prop_mutex_); + + current_size_.fetch_add(batch.transactions().size()); + queue_.push(std::make_unique( + std::move(batch))); + log_->info("Queue size is {}", current_size_.load()); - // on_next calls should not be concurrent - std::lock_guard lk(mutex_); - transactions_.get_subscriber().on_next(ProposalEvent::kTransactionEvent); + batch_prop_lock.unlock(); + + std::lock_guard event_lock(event_mutex_); + transactions_.get_subscriber().on_next(ProposalEvent::kBatchEvent); } void OrderingServiceImpl::generateProposal() { + std::lock_guard lock(batch_prop_mutex_); + // TODO 05/03/2018 andrei IR-1046 Server-side shared model object // factories with move semantics iroha::protocol::Proposal proto_proposal; proto_proposal.set_height(proposal_height_++); proto_proposal.set_created_time(iroha::time::now()); log_->info("Start proposal generation"); - for (std::shared_ptr tx; + for (std::unique_ptr batch; static_cast(proto_proposal.transactions_size()) < max_size_ - and queue_.try_pop(tx);) { - *proto_proposal.add_transactions() = - std::move(static_cast(tx.get()) - ->getTransport()); + and queue_.try_pop(batch);) { + std::for_each( + batch->transactions().begin(), + batch->transactions().end(), + [this, &proto_proposal](auto &tx) { + *proto_proposal.add_transactions() = + std::static_pointer_cast(tx) + ->getTransport(); + current_size_--; + }); } auto proposal = std::make_unique( diff --git a/irohad/ordering/impl/ordering_service_impl.hpp b/irohad/ordering/impl/ordering_service_impl.hpp index 88b3948d7c..10c2fda780 100644 --- a/irohad/ordering/impl/ordering_service_impl.hpp +++ b/irohad/ordering/impl/ordering_service_impl.hpp @@ -6,9 +6,10 @@ #ifndef IROHA_ORDERING_SERVICE_IMPL_HPP #define IROHA_ORDERING_SERVICE_IMPL_HPP -#include #include -#include +#include + +#include #include #include "logger/logger.hpp" @@ -52,12 +53,11 @@ namespace iroha { bool is_async = true); /** - * Process transaction received from network - * Enqueues transaction and publishes corresponding event - * @param transaction + * Process transaction(s) received from network + * Enqueues transactions and publishes corresponding event + * @param batch, in which transactions are packed */ - void onTransaction(std::shared_ptr - transaction) override; + void onBatch(shared_model::interface::TransactionBatch &&batch) override; ~OrderingServiceImpl() override; @@ -73,7 +73,7 @@ namespace iroha { /** * Events for queue check strategy */ - enum class ProposalEvent { kTransactionEvent, kTimerEvent }; + enum class ProposalEvent { kBatchEvent, kTimerEvent }; /** * Collect transactions from queue @@ -84,7 +84,7 @@ namespace iroha { std::shared_ptr wsv_; tbb::concurrent_queue< - std::shared_ptr> + std::unique_ptr> queue_; /** @@ -92,6 +92,11 @@ namespace iroha { */ const size_t max_size_; + /** + * current number of transactions in a queue + */ + std::atomic_ulong current_size_; + std::shared_ptr transport_; /** @@ -115,9 +120,12 @@ namespace iroha { rxcpp::composite_subscription handle_; /** - * Mutex for incoming transactions + * Variables for concurrency */ - std::mutex mutex_; + /// mutex for both batch and proposal generation + std::shared_timed_mutex batch_prop_mutex_; + /// mutex for events activating + std::mutex event_mutex_; logger::Logger log_; }; diff --git a/irohad/ordering/impl/ordering_service_transport_grpc.cpp b/irohad/ordering/impl/ordering_service_transport_grpc.cpp index 7092ff53dc..259bbb5e69 100644 --- a/irohad/ordering/impl/ordering_service_transport_grpc.cpp +++ b/irohad/ordering/impl/ordering_service_transport_grpc.cpp @@ -18,7 +18,9 @@ #include "backend/protobuf/transaction.hpp" #include "builders/protobuf/proposal.hpp" +#include "interfaces/common_objects/transaction_sequence_common.hpp" #include "network/impl/grpc_channel_builder.hpp" +#include "validators/default_validator.hpp" using namespace iroha::ordering; @@ -35,14 +37,65 @@ grpc::Status OrderingServiceTransportGrpc::onTransaction( if (subscriber_.expired()) { log_->error("No subscriber"); } else { - subscriber_.lock()->onTransaction( - std::make_shared( - iroha::protocol::Transaction(*request))); + auto batch_result = + shared_model::interface::TransactionBatch::createTransactionBatch< + shared_model::validation::DefaultTransactionValidator>( + std::make_shared( + iroha::protocol::Transaction(*request))); + batch_result.match( + [this](iroha::expected::Value + &batch) { + subscriber_.lock()->onBatch(std::move(batch.value)); + }, + [this](const iroha::expected::Error &error) { + log_->error( + "Could not create batch from received single transaction: {}", + error.error); + }); } return ::grpc::Status::OK; } +grpc::Status OrderingServiceTransportGrpc::onBatch( + ::grpc::ServerContext *context, + const protocol::TxList *request, + ::google::protobuf::Empty *response) { + log_->info("OrderingServiceTransportGrpc::onBatch"); + if (subscriber_.expired()) { + log_->error("No subscriber"); + } else { + auto txs = + std::vector>( + request->transactions_size()); + std::transform( + std::begin(request->transactions()), + std::end(request->transactions()), + std::begin(txs), + [](const auto &tx) { + return std::make_shared(tx); + }); + + auto batch_result = + shared_model::interface::TransactionBatch::createTransactionBatch( + txs, + shared_model::validation::SignedTransactionsCollectionValidator< + shared_model::validation::DefaultTransactionValidator, + shared_model::validation::BatchOrderValidator>()); + batch_result.match( + [this](iroha::expected::Value + &batch) { + subscriber_.lock()->onBatch(std::move(batch.value)); + }, + [this](const iroha::expected::Error &error) { + log_->error( + "Could not create batch from received transaction list: {}", + error.error); + }); + } + return ::grpc::Status::OK; +} + void OrderingServiceTransportGrpc::publishProposal( std::unique_ptr proposal, const std::vector &peers) { diff --git a/irohad/ordering/impl/ordering_service_transport_grpc.hpp b/irohad/ordering/impl/ordering_service_transport_grpc.hpp index b77ca538ec..f2811ba8eb 100644 --- a/irohad/ordering/impl/ordering_service_transport_grpc.hpp +++ b/irohad/ordering/impl/ordering_service_transport_grpc.hpp @@ -46,6 +46,10 @@ namespace iroha { const protocol::Transaction *request, ::google::protobuf::Empty *response) override; + grpc::Status onBatch(::grpc::ServerContext *context, + const protocol::TxList *request, + ::google::protobuf::Empty *response) override; + ~OrderingServiceTransportGrpc() = default; private: diff --git a/test/module/irohad/ordering/ordering_service_test.cpp b/test/module/irohad/ordering/ordering_service_test.cpp index 83953b3fbb..9bb16e4ffe 100644 --- a/test/module/irohad/ordering/ordering_service_test.cpp +++ b/test/module/irohad/ordering/ordering_service_test.cpp @@ -7,13 +7,13 @@ #include "backend/protobuf/common_objects/peer.hpp" #include "builders/protobuf/common_objects/proto_peer_builder.hpp" -#include "builders/protobuf/transaction.hpp" +#include "framework/batch_helper.hpp" +#include "interfaces/iroha_internal/transaction_batch.hpp" #include "logger/logger.hpp" #include "module/irohad/ametsuchi/ametsuchi_mocks.hpp" #include "module/irohad/network/network_mocks.hpp" #include "module/irohad/ordering/mock_ordering_service_persistent_state.hpp" #include "module/shared_model/builders/protobuf/test_proposal_builder.hpp" -#include "module/shared_model/builders/protobuf/test_transaction_builder.hpp" #include "ordering/impl/ordering_service_impl.hpp" #include "ordering/impl/ordering_service_transport_grpc.hpp" @@ -67,20 +67,6 @@ class OrderingServiceTest : public ::testing::Test { std::make_shared(); } - auto getTx() { - return std::make_unique( - shared_model::proto::TransactionBuilder() - .createdTime(iroha::time::now()) - .creatorAccountId("admin@ru") - .addAssetQuantity("coin#coin", "1.0") - .quorum(1) - .build() - .signAndAddSignature( - shared_model::crypto::DefaultCryptoAlgorithmType:: - generateKeypair()) - .finish()); - } - auto initOs(size_t max_proposal) { return std::make_shared( wsv, @@ -157,7 +143,7 @@ TEST_F(OrderingServiceTest, ValidWhenProposalSizeStrategy) { fake_transport->subscribe(ordering_service); for (size_t i = 0; i < tx_num; ++i) { - ordering_service->onTransaction(getTx()); + ordering_service->onBatch(framework::batch::createValidBatch(1)); } } @@ -188,12 +174,13 @@ TEST_F(OrderingServiceTest, ValidWhenTimerStrategy) { fake_transport->subscribe(ordering_service); for (size_t i = 0; i < 8; ++i) { - ordering_service->onTransaction(getTx()); + ordering_service->onBatch(framework::batch::createValidBatch(1)); } makeProposalTimeout(); - ordering_service->onTransaction(getTx()); - ordering_service->onTransaction(getTx()); + ordering_service->onBatch(framework::batch::createValidBatch(1)); + ordering_service->onBatch(framework::batch::createValidBatch(1)); + makeProposalTimeout(); } @@ -214,7 +201,8 @@ TEST_F(OrderingServiceTest, BrokenPersistentState) { .WillRepeatedly(Return(false)); auto ordering_service = initOs(max_proposal); - ordering_service->onTransaction(getTx()); + ordering_service->onBatch(framework::batch::createValidBatch(1)); + makeProposalTimeout(); } @@ -235,7 +223,7 @@ TEST_F(OrderingServiceTest, ConcurrentGenerateProposal) { auto on_tx = [&]() { for (int i = 0; i < 1000; ++i) { - ordering_service->onTransaction(getTx()); + ordering_service->onBatch(framework::batch::createValidBatch(1)); } }; @@ -261,8 +249,8 @@ TEST_F(OrderingServiceTest, ConcurrentGenerateProposal) { * called after destructor call */ TEST_F(OrderingServiceTest, GenerateProposalDestructor) { - const auto max_proposal = 100000; - const auto commit_delay = 100ms; + const auto max_proposal = 600; + const auto commit_delay = 5s; EXPECT_CALL(*fake_persistent_state, loadProposalHeight()) .Times(1) .WillOnce(Return(boost::optional(1))); @@ -286,8 +274,10 @@ TEST_F(OrderingServiceTest, GenerateProposalDestructor) { true); auto on_tx = [&]() { - for (int i = 0; i < 1000; ++i) { - ordering_service.onTransaction(getTx()); + // create max_proposal+1 txs, so that publish proposal is invoked at least + // once (concurrency!) + for (int i = 0; i < max_proposal + 1; ++i) { + ordering_service.onBatch(framework::batch::createValidBatch(1)); } }; @@ -296,3 +286,38 @@ TEST_F(OrderingServiceTest, GenerateProposalDestructor) { } EXPECT_CALL(*fake_transport, publishProposalProxy(_, _)).Times(0); } + +/** + * Check that batches are processed by ordering service + * @given ordering service up and running + * @when feeding the ordering service two batches, such that number of + * transactions in both summed is greater than maximum number of transactions + * inside proposal + * @then proposal will still contain number of transactions, equal to sum of the + * batches + */ +TEST_F(OrderingServiceTest, BatchesProceed) { + const auto max_proposal = 12; + const auto first_batch_size = 10; + const auto second_batch_size = 5; + + auto batch_one = framework::batch::createValidBatch(first_batch_size); + auto batch_two = framework::batch::createValidBatch(second_batch_size); + + EXPECT_CALL(*fake_persistent_state, saveProposalHeight(_)) + .Times(1) + .WillRepeatedly(Return(true)); + EXPECT_CALL(*fake_persistent_state, loadProposalHeight()) + .Times(1) + .WillOnce(Return( + boost::optional(first_batch_size + second_batch_size))); + EXPECT_CALL(*fake_transport, publishProposalProxy(_, _)).Times(1); + EXPECT_CALL(*wsv, getLedgerPeers()) + .WillRepeatedly(Return(std::vector{peer})); + + auto ordering_service = initOs(max_proposal); + fake_transport->subscribe(ordering_service); + + ordering_service->onBatch(std::move(batch_one)); + ordering_service->onBatch(std::move(batch_two)); +} From 2f621507fd574a05f24bcefbc6507097e4e9a807 Mon Sep 17 00:00:00 2001 From: Kevin McMahon Date: Wed, 25 Jul 2018 08:54:24 -0500 Subject: [PATCH 72/97] Fix link to the docs (#1589) The link to the ReadTheDocs site needs to either be http://iroha.readthedocs.io or http://iroha.readthedocs.io/en/latest/. I've shortened it to the root url in the fix. The way it currently is (http://iroha.readthedocs.io/en/) results in a 404. Signed-off-by: Kevin McMahon --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index cf40ea2a28..b0454fb804 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ Among the non-functional requirements can be noted a high degree of network faul ## Documentation -Our documentation is hosted at ReadTheDocs service here: [http://iroha.readthedocs.io](http://iroha.readthedocs.io/en/). +Our documentation is hosted at ReadTheDocs service here: [http://iroha.readthedocs.io](http://iroha.readthedocs.io). We have documentation in several languages available and you are welcome to contribute on [POEditor website](https://poeditor.com/join/project/SFpZw7o33o)! ### How to explore Iroha really fast? From a806bd1adbb509238848506d7618d073b40988c3 Mon Sep 17 00:00:00 2001 From: Nikolay Yushkevich Date: Wed, 25 Jul 2018 17:34:50 +0300 Subject: [PATCH 73/97] Change invariant for amount in query (#1524) * Change invariant for amount in query Signed-off-by: Nikolay Yushkevich * Correct integration test to reveal more fun than usual Signed-off-by: Nikolay Yushkevich * Revert "Change invariant for amount in query" This reverts commit bafd524 Signed-off-by: Nikolay Yushkevich * Disable failing tests until it does not fail Signed-off-by: Nikolay Yushkevich * Enable previously failing test Signed-off-by: Nikolay Yushkevich --- .../acceptance/get_account_assets_test.cpp | 32 +++++++++++++------ 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/test/integration/acceptance/get_account_assets_test.cpp b/test/integration/acceptance/get_account_assets_test.cpp index ce835e7911..c7f5265c3a 100644 --- a/test/integration/acceptance/get_account_assets_test.cpp +++ b/test/integration/acceptance/get_account_assets_test.cpp @@ -49,14 +49,20 @@ class GetAccountAssets : public AcceptanceFixture { return complete(baseQry().queryCounter(1).getAccountAssets(kUserId)); } - const std::string kNewRole = "rl"; -}; + static auto checkAccountAssets(int quantity) { + return + [quantity](const shared_model::proto::QueryResponse &query_response) { + ASSERT_NO_THROW({ + const auto &resp = boost::apply_visitor( + framework::SpecifiedVisitor(), + query_response.get()); + + ASSERT_EQ(resp.accountAssets().size(), quantity); + }); + }; + } -/// Callback for checking that status is AccountAssetResponse -auto checkValid = [](auto &status) { - ASSERT_NO_THROW(boost::apply_visitor( - framework::SpecifiedVisitor(), - status.get())); + const std::string kNewRole = "rl"; }; /** @@ -65,6 +71,8 @@ auto checkValid = [](auto &status) { * @then there is an AccountAssetResponse */ TEST_F(GetAccountAssets, AddedAssets) { + auto check_single_asset = checkAccountAssets(1); + IntegrationTestFramework(1) .setInitialState(kAdminKeypair) .sendTx(makeUserWithPerms()) @@ -73,7 +81,7 @@ TEST_F(GetAccountAssets, AddedAssets) { .sendTx(addAssets()) .checkBlock( [](auto &block) { ASSERT_EQ(block->transactions().size(), 1); }) - .sendQuery(makeQuery(), checkValid) + .sendQuery(makeQuery(), check_single_asset) .done(); } @@ -83,6 +91,8 @@ TEST_F(GetAccountAssets, AddedAssets) { * @then there is an AccountAssetResponse */ TEST_F(GetAccountAssets, RemovedAssets) { + auto check_single_asset = checkAccountAssets(1); + IntegrationTestFramework(1) .setInitialState(kAdminKeypair) .sendTx(makeUserWithPerms()) @@ -94,7 +104,7 @@ TEST_F(GetAccountAssets, RemovedAssets) { .sendTx(removeAssets()) .checkBlock( [](auto &block) { ASSERT_EQ(block->transactions().size(), 1); }) - .sendQuery(makeQuery(), checkValid) + .sendQuery(makeQuery(), check_single_asset) .done(); } @@ -104,11 +114,13 @@ TEST_F(GetAccountAssets, RemovedAssets) { * @then there is an AccountAssetResponse */ TEST_F(GetAccountAssets, NonAddedAssets) { + auto check_zero_assets = checkAccountAssets(0); + IntegrationTestFramework(1) .setInitialState(kAdminKeypair) .sendTx(makeUserWithPerms()) .skipProposal() .skipBlock() - .sendQuery(makeQuery(), checkValid) + .sendQuery(makeQuery(), check_zero_assets) .done(); } From 58dac341ccaa6a65b0e3be8451ae867e3b234ee7 Mon Sep 17 00:00:00 2001 From: Dumitru Date: Wed, 25 Jul 2018 16:41:19 +0200 Subject: [PATCH 74/97] Add batchMeta to transaction builder (#1595) * Add batchMeta to transaction builder Signed-off-by: Dumitru * Revert primitive parameter passing Signed-off-by: Dumitru * Add dashes to docs Signed-off-by: Dumitru * Remove ref from enum Signed-off-by: Dumitru --- shared_model/bindings/model_transaction_builder.cpp | 6 ++++++ shared_model/bindings/model_transaction_builder.hpp | 10 ++++++++++ 2 files changed, 16 insertions(+) diff --git a/shared_model/bindings/model_transaction_builder.cpp b/shared_model/bindings/model_transaction_builder.cpp index 5211fba63e..728a9b39a4 100644 --- a/shared_model/bindings/model_transaction_builder.cpp +++ b/shared_model/bindings/model_transaction_builder.cpp @@ -38,6 +38,12 @@ namespace shared_model { return ModelTransactionBuilder(builder_.quorum(quorum)); } + ModelTransactionBuilder ModelTransactionBuilder::batchMeta( + interface::types::BatchType type, + const std::vector &hashes) { + return ModelTransactionBuilder(builder_.batchMeta(type, hashes)); + } + ModelTransactionBuilder ModelTransactionBuilder::addAssetQuantity( const interface::types::AssetIdType &asset_id, const std::string &amount) { diff --git a/shared_model/bindings/model_transaction_builder.hpp b/shared_model/bindings/model_transaction_builder.hpp index cdc6a6f209..1b678e1d91 100644 --- a/shared_model/bindings/model_transaction_builder.hpp +++ b/shared_model/bindings/model_transaction_builder.hpp @@ -61,6 +61,16 @@ namespace shared_model { */ ModelTransactionBuilder quorum(interface::types::QuorumType quorum); + /** + * Sets batch meta + * @param type - one of ATOMIC or ORDERED + * @param hashes - vector of hashes of transactions in this batch + * @return builder with batchMeta set + */ + ModelTransactionBuilder batchMeta( + interface::types::BatchType type, + const std::vector &hashes); + /** * Adds given quantity of given asset to account * @param asset_id - asset id From 318a09e34fa9ad023abb9675b111deb582367123 Mon Sep 17 00:00:00 2001 From: Igor Egorov <36847043+igor-egorov@users.noreply.github.com> Date: Wed, 25 Jul 2018 18:32:04 +0300 Subject: [PATCH 75/97] Put correct links to related commands for GetAccountTransactions perms (#1600) Correct links to related commands are used for three permissions. Signed-off-by: Igor Egorov <36847043+igor-egorov@users.noreply.github.com> --- docs/source/maintenance/permissions.rst | 6 +++--- docs/source/permissions/matrix.csv | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/source/maintenance/permissions.rst b/docs/source/maintenance/permissions.rst index cf060e794c..7805e98200 100644 --- a/docs/source/maintenance/permissions.rst +++ b/docs/source/maintenance/permissions.rst @@ -874,7 +874,7 @@ Allows getting all `transactions <../core_concepts/glossary.html#transaction>`__ .. Note:: Incoming asset transfer inside a transaction would NOT lead to an appearance of the transaction in the command output. -| Related API method: `Get Account Asset Transactions <../api/queries.html#get-account-asset-transactions>`__ +| Related API method: `Get Account Transactions <../api/queries.html#get-account-transactions>`__ | Usage in Java bindings: ``Role.kGetAllAccTxs`` | Usage in Python bindings: ``Role_kGetAllAccTxs`` | @@ -896,7 +896,7 @@ Allows getting all `transactions <../core_concepts/glossary.html#transaction>`__ .. Note:: Incoming asset transfer inside a transaction would NOT lead to an appearance of the transaction in the command output. -| Related API method: `Get Account Asset Transactions <../api/queries.html#get-account-asset-transactions>`__ +| Related API method: `Get Account Transactions <../api/queries.html#get-account-transactions>`__ | Usage in Java bindings: ``Role.kGetDomainAccTxs`` | Usage in Python bindings: ``Role_kGetDomainAccTxs`` | @@ -918,7 +918,7 @@ Allows getting all `transactions <../core_concepts/glossary.html#transaction>`__ .. Note:: Incoming asset transfer inside a transaction would NOT lead to an appearance of the transaction in the command output. -| Related API method: `Get Account Asset Transactions <../api/queries.html#get-account-asset-transactions>`__ +| Related API method: `Get Account Transactions <../api/queries.html#get-account-transactions>`__ | Usage in Java bindings: ``Role.kGetMyAccTxs`` | Usage in Python bindings: ``Role_kGetMyAccTxs`` | diff --git a/docs/source/permissions/matrix.csv b/docs/source/permissions/matrix.csv index 2d31545a89..00638a4de6 100644 --- a/docs/source/permissions/matrix.csv +++ b/docs/source/permissions/matrix.csv @@ -43,9 +43,9 @@ Query,Account Asset,can_get_my_acc_ast,FALSE,,Allows getting a balance of specif Query,Account Asset Transaction,can_get_all_acc_ast_txs,FALSE,,Allows getting transactions associated with a specified asset and any account within the system.,,Incoming asset transfers will also appear in the query response.,http://iroha.readthedocs.io/en/latest/api/queries.html#get-account-asset-transactions,"Admin creates Alice account in a different domain that has can_get_all_acc_ast_txs, can_receive and can_transfer permissions. Admin issues some amount of coins and transfers them to Alice. Alice can query all transactions related to coins and Admin account." Query,Account Asset Transaction,can_get_domain_acc_ast_txs,FALSE,,Allows getting transactions associated with a specified asset and an account from the same domain as query creator.,,Incoming asset transfers will also appear in the query response.,http://iroha.readthedocs.io/en/latest/api/queries.html#get-account-asset-transactions,Admin creates Alice in the same domain that has only can_get_domain_acc_ast_txs permission. Admin issues some amount of coins and transfers them to Alice. Alice can query all transactions related to coins and Admin account. Query,Account Asset Transaction,can_get_my_acc_ast_txs,FALSE,,Allows getting transactions associated with the account of query creator and specified asset.,,Incoming asset transfers will also appear in the query response.,http://iroha.readthedocs.io/en/latest/api/queries.html#get-account-asset-transactions,Admin creates Alice account in a domain that has only can_get_my_acc_ast_txs permission. Admin issues some amount of coins and transfers them to Alice. Alice can query all transactions related to coins and own account. -Query,Account Transaction,can_get_all_acc_txs,FALSE,,Allows getting all transactions issued by any account within the system.,,Incoming asset transfer inside a transaction would NOT lead to an appearance of the transaction in the command output.,http://iroha.readthedocs.io/en/latest/api/queries.html#get-account-asset-transactions,Admin creates Alice account in a different domain that has only can_get_all_acc_txs permiison. Alice can request all the transactions issues by Admin. -Query,Account Transaction,can_get_domain_acc_txs,FALSE,,Allows getting all transactions issued by any account from the same domain as query creator.,,Incoming asset transfer inside a transaction would NOT lead to an appearance of the transaction in the command output.,http://iroha.readthedocs.io/en/latest/api/queries.html#get-account-asset-transactions,Admin creates Alice account in the same domain that has only can_get_domain_acc_txs permission. Alice can request all the transactions issued by Admin. -Query,Account Transaction,can_get_my_acc_txs,FALSE,,Allows getting all transactions issued by an account of query creator.,,Incoming asset transfer inside a transaction would NOT lead to an appearance of the transaction in the command output.,http://iroha.readthedocs.io/en/latest/api/queries.html#get-account-asset-transactions,Admin creates Alice account in a domain that has only can_get_my_acc_txs permission. Alice can get all transactions issued by own account. +Query,Account Transaction,can_get_all_acc_txs,FALSE,,Allows getting all transactions issued by any account within the system.,,Incoming asset transfer inside a transaction would NOT lead to an appearance of the transaction in the command output.,http://iroha.readthedocs.io/en/latest/api/queries.html#get-account-transactions,Admin creates Alice account in a different domain that has only can_get_all_acc_txs permiison. Alice can request all the transactions issues by Admin. +Query,Account Transaction,can_get_domain_acc_txs,FALSE,,Allows getting all transactions issued by any account from the same domain as query creator.,,Incoming asset transfer inside a transaction would NOT lead to an appearance of the transaction in the command output.,http://iroha.readthedocs.io/en/latest/api/queries.html#get-account-transactions,Admin creates Alice account in the same domain that has only can_get_domain_acc_txs permission. Alice can request all the transactions issued by Admin. +Query,Account Transaction,can_get_my_acc_txs,FALSE,,Allows getting all transactions issued by an account of query creator.,,Incoming asset transfer inside a transaction would NOT lead to an appearance of the transaction in the command output.,http://iroha.readthedocs.io/en/latest/api/queries.html#get-account-transactions,Admin creates Alice account in a domain that has only can_get_my_acc_txs permission. Alice can get all transactions issued by own account. Query,Asset,can_read_assets,FALSE,,Allows getting information about asset precision.,,,http://iroha.readthedocs.io/en/latest/api/queries.html#get-asset-info,Admin creates Alice account in a domain that has can_read_assets permissions. Alice can query information about any asset. Query,Block Stream,can_get_blocks,FALSE,,Allows subscription to the stream of accepted blocks.,,,, Query,Role,can_get_roles,FALSE,,"Allows getting a list of roles within the system. From 1efb469a53a574413c75538f2592b8a2210237cb Mon Sep 17 00:00:00 2001 From: Kitsu Date: Thu, 26 Jul 2018 00:59:01 +0300 Subject: [PATCH 76/97] Fix tbb feature detection for clang (#1599) Signed-off-by: Kitsu --- cmake/Modules/Findtbb.cmake | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/cmake/Modules/Findtbb.cmake b/cmake/Modules/Findtbb.cmake index e5ffba9380..2a55b1aa52 100644 --- a/cmake/Modules/Findtbb.cmake +++ b/cmake/Modules/Findtbb.cmake @@ -48,3 +48,10 @@ set_target_properties(tbb PROPERTIES INTERFACE_INCLUDE_DIRECTORIES ${tbb_INCLUDE_DIR} IMPORTED_LOCATION ${tbb_LIBRARY} ) + +if (NOT TBB_USE_GLIBCXX_VERSION AND UNIX AND NOT APPLE AND "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") + # https://www.threadingbuildingblocks.org/docs/help/reference/appendices/known_issues/linux_os.html + string(REPLACE "." "0" TBB_USE_GLIBCXX_VERSION ${CMAKE_CXX_COMPILER_VERSION}) + set_target_properties(tbb PROPERTIES + INTERFACE_COMPILE_DEFINITIONS "TBB_USE_GLIBCXX_VERSION=${TBB_USE_GLIBCXX_VERSION}") +endif() From 89ca7d0d61e68d44fe095d25c09957909a3ef422 Mon Sep 17 00:00:00 2001 From: kamilsa Date: Thu, 26 Jul 2018 08:13:49 +0300 Subject: [PATCH 77/97] Check block in mutable storage (#1497) * Add check method Signed-off-by: kamilsa --- .../ametsuchi/impl/mutable_storage_impl.cpp | 13 +++- .../ametsuchi/impl/mutable_storage_impl.hpp | 7 +- irohad/ametsuchi/mutable_storage.hpp | 27 +++++-- test/module/irohad/ametsuchi/CMakeLists.txt | 7 ++ .../irohad/ametsuchi/ametsuchi_mocks.hpp | 8 +- .../irohad/ametsuchi/mutable_storage_test.cpp | 74 +++++++++++++++++++ 6 files changed, 122 insertions(+), 14 deletions(-) create mode 100644 test/module/irohad/ametsuchi/mutable_storage_test.cpp diff --git a/irohad/ametsuchi/impl/mutable_storage_impl.cpp b/irohad/ametsuchi/impl/mutable_storage_impl.cpp index e32905d006..d8016f3a0c 100644 --- a/irohad/ametsuchi/impl/mutable_storage_impl.cpp +++ b/irohad/ametsuchi/impl/mutable_storage_impl.cpp @@ -19,8 +19,8 @@ #include -#include "ametsuchi/impl/postgres_command_executor.hpp" #include "ametsuchi/impl/postgres_block_index.hpp" +#include "ametsuchi/impl/postgres_command_executor.hpp" #include "ametsuchi/impl/postgres_wsv_command.hpp" #include "ametsuchi/impl/postgres_wsv_query.hpp" #include "ametsuchi/wsv_command.hpp" @@ -44,11 +44,16 @@ namespace iroha { *sql_ << "BEGIN"; } + bool MutableStorageImpl::check( + const shared_model::interface::BlockVariant &block, + MutableStorage::MutableStoragePredicateType + predicate) { + return predicate(block, *wsv_, top_hash_); + } + bool MutableStorageImpl::apply( const shared_model::interface::Block &block, - std::function + MutableStoragePredicateType function) { auto execute_transaction = [this](auto &transaction) { command_executor_->setCreatorAccountId(transaction.creatorAccountId()); diff --git a/irohad/ametsuchi/impl/mutable_storage_impl.hpp b/irohad/ametsuchi/impl/mutable_storage_impl.hpp index bed99caa4b..0606eb96fc 100644 --- a/irohad/ametsuchi/impl/mutable_storage_impl.hpp +++ b/irohad/ametsuchi/impl/mutable_storage_impl.hpp @@ -41,13 +41,12 @@ namespace iroha { std::unique_ptr sql, std::shared_ptr factory); + bool check(const shared_model::interface::BlockVariant &block, + MutableStoragePredicateType function) override; bool apply( const shared_model::interface::Block &block, - std::function - function) override; + MutableStoragePredicateType function) override; ~MutableStorageImpl() override; diff --git a/irohad/ametsuchi/mutable_storage.hpp b/irohad/ametsuchi/mutable_storage.hpp index 21bd39f99f..1f0f069ff2 100644 --- a/irohad/ametsuchi/mutable_storage.hpp +++ b/irohad/ametsuchi/mutable_storage.hpp @@ -24,7 +24,8 @@ namespace shared_model { namespace interface { class Block; - } + class BlockVariant; + } // namespace interface } // namespace shared_model namespace iroha { @@ -38,6 +39,25 @@ namespace iroha { */ class MutableStorage { public: + /** + * Predicate type checking type T + */ + template + using MutableStoragePredicateType = + std::function; + + /** + * Checks if block satisfies predicated + * @param block block to be checked + * @param predicate function returning true if predicate satisfied and + * false otherwise + * @return result of predicate + */ + virtual bool check(const shared_model::interface::BlockVariant &block, + MutableStoragePredicateType) = 0; + /** * Applies a block to current mutable state * using logic specified in function @@ -54,10 +74,7 @@ namespace iroha { */ virtual bool apply( const shared_model::interface::Block &block, - std::function - function) = 0; + MutableStoragePredicateType function) = 0; virtual ~MutableStorage() = default; }; diff --git a/test/module/irohad/ametsuchi/CMakeLists.txt b/test/module/irohad/ametsuchi/CMakeLists.txt index 37c021dac0..2c4af25796 100644 --- a/test/module/irohad/ametsuchi/CMakeLists.txt +++ b/test/module/irohad/ametsuchi/CMakeLists.txt @@ -42,6 +42,13 @@ target_link_libraries(kv_storage_test ametsuchi_fixture ) +addtest(mutable_storage_test mutable_storage_test.cpp) +target_link_libraries(mutable_storage_test + ametsuchi + libs_common + ametsuchi_fixture + ) + addtest(storage_init_test storage_init_test.cpp) target_link_libraries(storage_init_test ametsuchi diff --git a/test/module/irohad/ametsuchi/ametsuchi_mocks.hpp b/test/module/irohad/ametsuchi/ametsuchi_mocks.hpp index 68202c7cae..b614a36d3a 100644 --- a/test/module/irohad/ametsuchi/ametsuchi_mocks.hpp +++ b/test/module/irohad/ametsuchi/ametsuchi_mocks.hpp @@ -21,7 +21,6 @@ #include #include #include "ametsuchi/block_query.hpp" -#include "ametsuchi/command_executor.hpp" #include "ametsuchi/key_value_storage.hpp" #include "ametsuchi/mutable_factory.hpp" #include "ametsuchi/mutable_storage.hpp" @@ -210,6 +209,13 @@ namespace iroha { class MockMutableStorage : public MutableStorage { public: + MOCK_METHOD2( + check, + bool(const shared_model::interface::BlockVariant &, + std::function< + bool(const shared_model::interface::BlockVariant &, + WsvQuery &, + const shared_model::interface::types::HashType &)>)); MOCK_METHOD2( apply, bool(const shared_model::interface::Block &, diff --git a/test/module/irohad/ametsuchi/mutable_storage_test.cpp b/test/module/irohad/ametsuchi/mutable_storage_test.cpp new file mode 100644 index 0000000000..d7645e4da1 --- /dev/null +++ b/test/module/irohad/ametsuchi/mutable_storage_test.cpp @@ -0,0 +1,74 @@ +/** + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "module/irohad/ametsuchi/ametsuchi_fixture.hpp" +#include "module/irohad/ametsuchi/ametsuchi_mocks.hpp" +#include "module/shared_model/builders/protobuf/test_block_builder.hpp" + +using namespace iroha::ametsuchi; +using testing::Bool; +using testing::Values; +using testing::WithParamInterface; + +class MutableStorageTest : public AmetsuchiTest, + public WithParamInterface { + protected: + void SetUp() override { + AmetsuchiTest::SetUp(); + + storage->createMutableStorage().match( + [this](iroha::expected::Value> + &mut_storage) { + mutable_storage_ = std::move(mut_storage.value); + }, + [](const auto &) { FAIL() << "Mutable storage cannot be created"; }); + } + + void TearDown() override { + mutable_storage_.reset(); + AmetsuchiTest::TearDown(); + }; + + shared_model::interface::BlockVariant getBlock() { + return std::make_shared( + TestBlockBuilder() + .transactions(std::vector({})) + .height(1) + .prevHash(fake_hash) + .build()); + } + + std::string zero_string{32, '0'}; + shared_model::crypto::Hash fake_hash{zero_string}; + shared_model::crypto::PublicKey fake_pubkey{zero_string}; + std::unique_ptr mutable_storage_; +}; + +/** + * @given mutable storage + * @when check block method takes block and predicated returning boolean value + * @then the same block is processed in predicate + * @and returned value returned by check function is the same as result of + * predicate + */ +TEST_P(MutableStorageTest, TestCheckBlock) { + auto expected_block = getBlock(); + bool expected_res = GetParam(); + ASSERT_EQ(expected_res, + mutable_storage_->check( + expected_block, + [&expected_block, &expected_res]( + const auto &block, const auto &, const auto &) { + EXPECT_EQ(expected_block, block); + return expected_res; + })); +} + +INSTANTIATE_TEST_CASE_P(MutableStorageParameterizedTest, + MutableStorageTest, + // note additional comma is needed to make it compile + // https://github.com/google/googletest/issues/1419 + Bool(), ); From f77fe6091b2ba65fdddb1b9c7e3527f0638fc2ea Mon Sep 17 00:00:00 2001 From: kamilsa Date: Thu, 26 Jul 2018 11:17:25 +0300 Subject: [PATCH 78/97] Rename tx hashes to reduced hashes (#1601) Signed-off-by: kamilsa --- .../impl/stateful_validator_impl.cpp | 2 +- shared_model/backend/protobuf/batch_meta.hpp | 12 +++++------ .../transaction_template.hpp | 2 +- .../interfaces/iroha_internal/batch_meta.hpp | 8 ++++---- .../iroha_internal/transaction_sequence.cpp | 2 +- shared_model/schema/transaction.proto | 5 ++++- .../batch_order_validator.cpp | 20 +++++++++---------- .../validators/field_validator_test.cpp | 2 +- 8 files changed, 28 insertions(+), 25 deletions(-) diff --git a/irohad/validation/impl/stateful_validator_impl.cpp b/irohad/validation/impl/stateful_validator_impl.cpp index 51560f1793..e3cdbd5b9c 100644 --- a/irohad/validation/impl/stateful_validator_impl.cpp +++ b/irohad/validation/impl/stateful_validator_impl.cpp @@ -182,7 +182,7 @@ namespace iroha { } else { // find the batch end in proposal's transactions auto batch_end_hash = - current_tx_it->batchMeta()->get()->transactionHashes().back(); + current_tx_it->batchMeta()->get()->reducedHashes().back(); auto batch_end_it = std::find_if(current_tx_it, txs_end, [&batch_end_hash](auto &tx) { return tx.reducedHash() == batch_end_hash; diff --git a/shared_model/backend/protobuf/batch_meta.hpp b/shared_model/backend/protobuf/batch_meta.hpp index a1c1df5e59..8bf48b70f8 100644 --- a/shared_model/backend/protobuf/batch_meta.hpp +++ b/shared_model/backend/protobuf/batch_meta.hpp @@ -34,10 +34,10 @@ namespace shared_model { ->index(); return static_cast(which); }}, - transaction_hashes_{[this] { + reduced_hashes_{[this] { return boost::accumulate( - proto_->tx_hashes(), - TransactionHashesType{}, + proto_->reduced_hashes(), + ReducedHashesType{}, [](auto &&acc, const auto &hash) { acc.emplace_back(hash); return std::forward(acc); @@ -51,8 +51,8 @@ namespace shared_model { interface::types::BatchType type() const override { return *type_; }; - const TransactionHashesType &transactionHashes() const override { - return *transaction_hashes_; + const ReducedHashesType &reducedHashes() const override { + return *reduced_hashes_; }; private: @@ -61,7 +61,7 @@ namespace shared_model { Lazy type_; - const Lazy transaction_hashes_; + const Lazy reduced_hashes_; }; // namespace proto } // namespace proto } // namespace shared_model diff --git a/shared_model/builders/protobuf/builder_templates/transaction_template.hpp b/shared_model/builders/protobuf/builder_templates/transaction_template.hpp index 815e393c36..d0ef48fe71 100644 --- a/shared_model/builders/protobuf/builder_templates/transaction_template.hpp +++ b/shared_model/builders/protobuf/builder_templates/transaction_template.hpp @@ -119,7 +119,7 @@ namespace shared_model { iroha::protocol::Transaction::Payload::BatchMeta::BatchType>( type)); for (const auto &hash : hashes) { - tx.mutable_payload()->mutable_batch()->add_tx_hashes( + tx.mutable_payload()->mutable_batch()->add_reduced_hashes( crypto::toBinaryString(hash)); } }); diff --git a/shared_model/interfaces/iroha_internal/batch_meta.hpp b/shared_model/interfaces/iroha_internal/batch_meta.hpp index 46a4152bab..270eef47eb 100644 --- a/shared_model/interfaces/iroha_internal/batch_meta.hpp +++ b/shared_model/interfaces/iroha_internal/batch_meta.hpp @@ -24,24 +24,24 @@ namespace shared_model { .init("BatchMeta") .append("Type", type() == types::BatchType::ATOMIC ? "ATOMIC" : "ORDERED") - .appendAll(transactionHashes(), + .appendAll(reducedHashes(), [](auto &hash) { return hash.toString(); }) .finalize(); } /// type of hashes collection - using TransactionHashesType = std::vector; + using ReducedHashesType = std::vector; /** * @return Hashes of transactions to fetch */ - virtual const TransactionHashesType &transactionHashes() const = 0; + virtual const ReducedHashesType &reducedHashes() const = 0; /** * Checks equality of objects inside * @param rhs - other wrapped value * @return true, if wrapped objects are same */ bool operator==(const ModelType &rhs) const override { - return boost::equal(transactionHashes(), rhs.transactionHashes()) + return boost::equal(reducedHashes(), rhs.reducedHashes()) and type() == rhs.type(); } }; diff --git a/shared_model/interfaces/iroha_internal/transaction_sequence.cpp b/shared_model/interfaces/iroha_internal/transaction_sequence.cpp index c988e244cb..ce775752f8 100644 --- a/shared_model/interfaces/iroha_internal/transaction_sequence.cpp +++ b/shared_model/interfaces/iroha_internal/transaction_sequence.cpp @@ -36,7 +36,7 @@ namespace shared_model { validation::Answer result; for (const auto &tx : transactions) { if (auto meta = tx->batchMeta()) { - auto hashes = meta.get()->transactionHashes(); + auto hashes = meta.get()->reducedHashes(); auto batch_hash = TransactionBatch::calculateReducedBatchHash(hashes); extracted_batches[batch_hash].push_back(tx); } else { diff --git a/shared_model/schema/transaction.proto b/shared_model/schema/transaction.proto index 53cd095af0..05742958c9 100644 --- a/shared_model/schema/transaction.proto +++ b/shared_model/schema/transaction.proto @@ -16,7 +16,8 @@ message Transaction { ORDERED = 1; } BatchType type = 1; - repeated bytes tx_hashes = 2; + // array of reduced hashes of all txs from the batch + repeated bytes reduced_hashes = 2; } message ReducedPayload{ repeated Command commands = 1; @@ -24,7 +25,9 @@ message Transaction { uint64 created_time = 3; uint32 quorum = 4; } + // transcation fields ReducedPayload reduced_payload = 1; + // batch meta fields if tx belong to any batch oneof optional_batch_meta{ BatchMeta batch = 5; } diff --git a/shared_model/validators/transactions_collection/batch_order_validator.cpp b/shared_model/validators/transactions_collection/batch_order_validator.cpp index 32eddd46b0..003d33a73d 100644 --- a/shared_model/validators/transactions_collection/batch_order_validator.cpp +++ b/shared_model/validators/transactions_collection/batch_order_validator.cpp @@ -24,18 +24,18 @@ namespace shared_model { } // beginning of a batch if (not batch1) { - if (batch2.get()->transactionHashes().size() == 0) { + if (batch2.get()->reducedHashes().size() == 0) { return (boost::format("Tx %s has a batch of 0 transactions") % tr2.value()->hash().hex()) .str(); } - if (batch2.get()->transactionHashes().front() + if (batch2.get()->reducedHashes().front() != tr2.value()->reducedHash()) { return (boost::format("Tx %s is a first transaction of a batch, but " "it's reduced hash %s doesn't match the first " "reduced hash in batch %s") % tr2.value()->hash().hex() - % batch2.get()->transactionHashes().front().hex() + % batch2.get()->reducedHashes().front().hex() % tr2.value()->reducedHash().hex()) .str(); } @@ -43,26 +43,26 @@ namespace shared_model { } // end of a batch if (not batch2) { - if (batch1.get()->transactionHashes().back() + if (batch1.get()->reducedHashes().back() != tr1.value()->reducedHash()) { return (boost::format("Tx %s is a last transaction of a batch, but " "it's reduced hash %s doesn't match the last " "reduced hash in batch %s") % tr1.value()->hash().hex() - % batch1.get()->transactionHashes().back().hex() + % batch1.get()->reducedHashes().back().hex() % tr1.value()->reducedHash().hex()) .str(); } return ""; } // inside of a batch - auto it1 = boost::find(batch1.get()->transactionHashes(), + auto it1 = boost::find(batch1.get()->reducedHashes(), tr1.value()->reducedHash()); - auto it2 = boost::find(batch2.get()->transactionHashes(), + auto it2 = boost::find(batch2.get()->reducedHashes(), tr2.value()->reducedHash()); - if (it1 == end(batch1.get()->transactionHashes()) - or it2 == end(batch2.get()->transactionHashes()) - or next(it1) == end(batch1.get()->transactionHashes()) + if (it1 == end(batch1.get()->reducedHashes()) + or it2 == end(batch2.get()->reducedHashes()) + or next(it1) == end(batch1.get()->reducedHashes()) or *next(it1) != *it2) { // end of the bach and beginning of the next if (canFollow(tr1, boost::none) == "" diff --git a/test/module/shared_model/validators/field_validator_test.cpp b/test/module/shared_model/validators/field_validator_test.cpp index 51c54dd141..eb45249383 100644 --- a/test/module/shared_model/validators/field_validator_test.cpp +++ b/test/module/shared_model/validators/field_validator_test.cpp @@ -564,7 +564,7 @@ class FieldValidatorTest : public ValidatorsTest { iroha::protocol::Transaction::Payload::BatchMeta meta; meta.set_type(iroha::protocol::Transaction::Payload::BatchMeta::BatchType:: Transaction_Payload_BatchMeta_BatchType_ATOMIC); - meta.add_tx_hashes("tst"); + meta.add_reduced_hashes("tst"); std::vector all_cases; all_cases.push_back(makeTestCase( "batch meta test", &FieldValidatorTest::batch_meta, meta, true, "")); From 19f3cd67dafafd7584b9955e6aaabce53ec5a059 Mon Sep 17 00:00:00 2001 From: Kitsu Date: Thu, 26 Jul 2018 13:35:01 +0300 Subject: [PATCH 79/97] Feature/tx status bus (#1575) - Add StatusBusImpl - Add MockStatusBus - Introduce boost::thread for boost::barrier & update dockerfiles - Use proper synchronization for recieving stateless statuses in ITF::sendTx - Fix cache issue in CommandService - Refactor CommandService with makeResponse Signed-off-by: Kitsu --- cmake/dependencies.cmake | 1 + docker/dependencies/Dockerfile | 2 +- docker/develop/Dockerfile | 2 +- irohad/main/application.cpp | 21 +++- irohad/main/application.hpp | 4 + irohad/torii/CMakeLists.txt | 8 ++ irohad/torii/command_service.hpp | 51 +++------ irohad/torii/impl/command_service.cpp | 85 +++++++-------- irohad/torii/impl/status_bus_impl.cpp | 21 ++++ irohad/torii/impl/status_bus_impl.hpp | 33 ++++++ irohad/torii/processor/CMakeLists.txt | 1 + .../impl/transaction_processor_impl.cpp | 43 +++----- .../torii/processor/transaction_processor.hpp | 8 -- .../processor/transaction_processor_impl.hpp | 11 +- irohad/torii/status_bus.hpp | 40 +++++++ .../integration_test_framework.cpp | 22 ++++ .../integration_framework/test_irohad.hpp | 4 + test/module/iroha-cli/client_test.cpp | 12 +- .../processor/transaction_processor_test.cpp | 103 ++++++++---------- test/module/irohad/torii/torii_mocks.hpp | 29 +++-- .../irohad/torii/torii_service_test.cpp | 31 +++--- 21 files changed, 304 insertions(+), 228 deletions(-) create mode 100644 irohad/torii/impl/status_bus_impl.cpp create mode 100644 irohad/torii/impl/status_bus_impl.hpp create mode 100644 irohad/torii/status_bus.hpp diff --git a/cmake/dependencies.cmake b/cmake/dependencies.cmake index f849b4e5ec..cc869ff288 100644 --- a/cmake/dependencies.cmake +++ b/cmake/dependencies.cmake @@ -71,6 +71,7 @@ find_package(Boost 1.65.0 REQUIRED COMPONENTS filesystem system + thread ) add_library(boost INTERFACE IMPORTED) set_target_properties(boost PROPERTIES diff --git a/docker/dependencies/Dockerfile b/docker/dependencies/Dockerfile index 040e8be607..d9abf7bfb7 100644 --- a/docker/dependencies/Dockerfile +++ b/docker/dependencies/Dockerfile @@ -49,7 +49,7 @@ RUN set -e; \ git clone https://github.com/boostorg/boost /tmp/boost; \ (cd /tmp/boost ; git checkout 436ad1dfcfc7e0246141beddd11c8a4e9c10b146); \ (cd /tmp/boost ; git submodule update --init --recursive); \ - (cd /tmp/boost ; /tmp/boost/bootstrap.sh --with-libraries=system,filesystem); \ + (cd /tmp/boost ; /tmp/boost/bootstrap.sh --with-libraries=system,filesystem,thread); \ (cd /tmp/boost ; /tmp/boost/b2 headers); \ (cd /tmp/boost ; /tmp/boost/b2 cxxflags="-std=c++14" -j ${PARALLELISM} install --prefix=/opt/dependencies/boost); \ ldconfig; \ diff --git a/docker/develop/Dockerfile b/docker/develop/Dockerfile index ab85deeca7..0f584feeaa 100644 --- a/docker/develop/Dockerfile +++ b/docker/develop/Dockerfile @@ -49,7 +49,7 @@ RUN set -e; \ git clone https://github.com/boostorg/boost /tmp/boost; \ (cd /tmp/boost ; git checkout 436ad1dfcfc7e0246141beddd11c8a4e9c10b146); \ (cd /tmp/boost ; git submodule update --init --recursive); \ - (cd /tmp/boost ; /tmp/boost/bootstrap.sh --with-libraries=system,filesystem); \ + (cd /tmp/boost ; /tmp/boost/bootstrap.sh --with-libraries=system,filesystem,thread); \ (cd /tmp/boost ; /tmp/boost/b2 headers); \ (cd /tmp/boost ; /tmp/boost/b2 cxxflags="-std=c++14" -j ${PARALLELISM} install); \ ldconfig; \ diff --git a/irohad/main/application.cpp b/irohad/main/application.cpp index dc44bfc0f2..4a0f56ac2c 100644 --- a/irohad/main/application.cpp +++ b/irohad/main/application.cpp @@ -15,6 +15,7 @@ #include "multi_sig_transactions/mst_time_provider_impl.hpp" #include "multi_sig_transactions/storage/mst_storage_impl.hpp" #include "multi_sig_transactions/transport/mst_transport_grpc.hpp" +#include "torii/impl/status_bus_impl.hpp" #include "validators/field_validator.hpp" using namespace iroha; @@ -74,6 +75,7 @@ void Irohad::init() { initConsensusGate(); initSynchronizer(); initPeerCommunicationService(); + initStatusBus(); initMstProcessor(); // Torii @@ -233,6 +235,11 @@ void Irohad::initPeerCommunicationService() { log_->info("[Init] => pcs"); } +void Irohad::initStatusBus() { + status_bus_ = std::make_shared(); + log_->info("[Init] => Tx status bus"); +} + void Irohad::initMstProcessor() { if (is_mst_supported_) { auto mst_transport = std::make_shared(); @@ -257,11 +264,15 @@ void Irohad::initMstProcessor() { * Initializing transaction command service */ void Irohad::initTransactionCommandService() { - auto tx_processor = - std::make_shared(pcs, mst_processor); - - command_service = std::make_shared<::torii::CommandService>( - tx_processor, storage, std::chrono::seconds(1), 2 * proposal_delay_); + auto tx_processor = std::make_shared( + pcs, mst_processor, status_bus_); + + command_service = + std::make_shared<::torii::CommandService>(tx_processor, + storage, + status_bus_, + std::chrono::seconds(1), + 2 * proposal_delay_); log_->info("[Init] => command service"); } diff --git a/irohad/main/application.hpp b/irohad/main/application.hpp index bcd4e99429..9cafb7ca8d 100644 --- a/irohad/main/application.hpp +++ b/irohad/main/application.hpp @@ -133,6 +133,8 @@ class Irohad { virtual void initPeerCommunicationService(); + virtual void initStatusBus(); + virtual void initMstProcessor(); virtual void initTransactionCommandService(); @@ -188,6 +190,8 @@ class Irohad { // mst std::shared_ptr mst_processor; + std::shared_ptr status_bus_; + // transaction service std::shared_ptr command_service; diff --git a/irohad/torii/CMakeLists.txt b/irohad/torii/CMakeLists.txt index 6f5ad86cd6..88099d588d 100644 --- a/irohad/torii/CMakeLists.txt +++ b/irohad/torii/CMakeLists.txt @@ -38,3 +38,11 @@ target_link_libraries(torii_service shared_model_stateless_validation processors ) + +add_library(status_bus + impl/status_bus_impl.cpp + ) +target_link_libraries(status_bus + rxcpp + shared_model_interfaces + ) diff --git a/irohad/torii/command_service.hpp b/irohad/torii/command_service.hpp index 4c6da721e8..03c2233272 100644 --- a/irohad/torii/command_service.hpp +++ b/irohad/torii/command_service.hpp @@ -1,18 +1,6 @@ /** - * Copyright Soramitsu Co., Ltd. 2018 All Rights Reserved. - * http://soramitsu.co.jp - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 */ #ifndef TORII_COMMAND_SERVICE_HPP @@ -29,6 +17,7 @@ #include "endpoint.pb.h" #include "logger/logger.hpp" #include "torii/processor/transaction_processor.hpp" +#include "torii/status_bus.hpp" namespace torii { /** @@ -40,12 +29,14 @@ namespace torii { * Creates a new instance of CommandService * @param tx_processor - processor of received transactions * @param storage - to query transactions outside the cache + * @param status_bus is a common notifier for tx statuses * @param initial_timeout - streaming timeout when tx is not received * @param nonfinal_timeout - streaming timeout when tx is being processed */ CommandService( std::shared_ptr tx_processor, std::shared_ptr storage, + std::shared_ptr status_bus, std::chrono::milliseconds initial_timeout, std::chrono::milliseconds nonfinal_timeout); @@ -150,9 +141,15 @@ namespace torii { inline void handleEvents(rxcpp::composite_subscription &subscription, rxcpp::schedulers::run_loop &run_loop); - void addTxToCacheAndLog(const std::string &who, - const shared_model::crypto::Hash &hash, - const iroha::protocol::ToriiResponse &response); + /** + * Share tx status and log it + * @param who identifier for the logging + * @param hash of the tx + * @param response to be pushed + */ + void pushStatus(const std::string &who, + const shared_model::crypto::Hash &hash, + const iroha::protocol::ToriiResponse &response); private: using CacheType = iroha::cache::Cache tx_processor_; std::shared_ptr storage_; + std::shared_ptr status_bus_; std::chrono::milliseconds initial_timeout_; std::chrono::milliseconds nonfinal_timeout_; std::shared_ptr cache_; - /** - * Mutex for propagating stateless validation status - */ - std::mutex stateless_tx_status_notifier_mutex_; - - /** - * Subject with stateless validation statuses - */ - rxcpp::subjects::subject< - std::shared_ptr> - stateless_notifier_; - - /** - * Observable with all transaction statuses - */ - rxcpp::observable< - std::shared_ptr> - responses_; - logger::Logger log_; }; diff --git a/irohad/torii/impl/command_service.cpp b/irohad/torii/impl/command_service.cpp index 101e237b86..42e109d6ce 100644 --- a/irohad/torii/impl/command_service.cpp +++ b/irohad/torii/impl/command_service.cpp @@ -36,23 +36,18 @@ namespace torii { CommandService::CommandService( std::shared_ptr tx_processor, std::shared_ptr storage, + std::shared_ptr status_bus, std::chrono::milliseconds initial_timeout, std::chrono::milliseconds nonfinal_timeout) : tx_processor_(tx_processor), storage_(storage), + status_bus_(status_bus), initial_timeout_(initial_timeout), nonfinal_timeout_(nonfinal_timeout), cache_(std::make_shared()), - // merge with mutex, since notifications can be made from different - // threads - // TODO 11.07.2018 andrei rework status handling with event bus IR-1517 - responses_(tx_processor_->transactionNotifier().merge( - rxcpp::serialize_one_worker( - rxcpp::schedulers::make_current_thread()), - stateless_notifier_.get_observable())), log_(logger::log("CommandService")) { // Notifier for all clients - responses_.subscribe([this](auto iroha_response) { + status_bus_->statuses().subscribe([this](auto iroha_response) { // find response for this tx in cache; if status of received response // isn't "greater" than cached one, dismiss received one auto proto_response = @@ -69,6 +64,17 @@ namespace torii { }); } + namespace { + iroha::protocol::ToriiResponse makeResponse( + const shared_model::crypto::Hash &h, + const iroha::protocol::TxStatus &status) { + iroha::protocol::ToriiResponse response; + response.set_tx_hash(shared_model::crypto::toBinaryString(h)); + response.set_tx_status(status); + return response; + } + } // namespace + void CommandService::Torii(const iroha::protocol::Transaction &request) { shared_model::proto::TransportBuilder< shared_model::proto::Transaction, @@ -86,20 +92,17 @@ namespace torii { return; } - // setting response - iroha::protocol::ToriiResponse response; - response.set_tx_hash( - shared_model::crypto::toBinaryString(tx_hash)); - response.set_tx_status( - iroha::protocol::TxStatus::STATELESS_VALIDATION_SUCCESS); - // Send transaction to iroha tx_processor_->transactionHandle( std::make_shared( std::move(iroha_tx.value))); - this->addTxToCacheAndLog( - "Torii", std::move(tx_hash), std::move(response)); + this->pushStatus( + "Torii", + std::move(tx_hash), + makeResponse( + tx_hash, + iroha::protocol::TxStatus::STATELESS_VALIDATION_SUCCESS)); }, [this, &request](const auto &error) { // getting hash from invalid transaction @@ -113,14 +116,12 @@ namespace torii { tx_hash.hex()); // setting response - iroha::protocol::ToriiResponse response; - response.set_tx_hash( - shared_model::crypto::toBinaryString(tx_hash)); - response.set_tx_status( + auto response = makeResponse( + tx_hash, iroha::protocol::TxStatus::STATELESS_VALIDATION_FAILED); response.set_error_message(std::move(error.error)); - this->addTxToCacheAndLog( + this->pushStatus( "Torii", std::move(tx_hash), std::move(response)); }); } @@ -145,18 +146,15 @@ namespace torii { return; } - // setting response - iroha::protocol::ToriiResponse response; - response.set_tx_hash( - shared_model::crypto::toBinaryString(tx_hash)); - response.set_tx_status( - iroha::protocol::TxStatus::STATELESS_VALIDATION_SUCCESS); - // Send transaction to iroha tx_processor_->transactionHandle(tx); - this->addTxToCacheAndLog( - "ToriiList", std::move(tx_hash), std::move(response)); + this->pushStatus( + "ToriiList", + std::move(tx_hash), + makeResponse(tx_hash, + iroha::protocol::TxStatus:: + STATELESS_VALIDATION_SUCCESS)); }); }, [this, &tx_list](auto &error) { @@ -189,14 +187,12 @@ namespace torii { shared_model::crypto::DefaultHashProvider::makeHash( shared_model::proto::makeBlob(tx.payload())); - iroha::protocol::ToriiResponse response; - response.set_tx_hash( - shared_model::crypto::toBinaryString(hash)); - response.set_tx_status( + auto response = makeResponse( + hash, iroha::protocol::TxStatus::STATELESS_VALIDATION_FAILED); response.set_error_message(sequence_error); - this->addTxToCacheAndLog( + this->pushStatus( "ToriiList", std::move(hash), std::move(response)); }); }); @@ -225,16 +221,15 @@ namespace torii { response.CopyFrom(*resp); } else { response.set_tx_hash(request.tx_hash()); - if (storage_->getBlockQuery()->hasTxWithHash( - shared_model::crypto::Hash(request.tx_hash()))) { + auto hash = shared_model::crypto::Hash(request.tx_hash()); + if (storage_->getBlockQuery()->hasTxWithHash(hash)) { response.set_tx_status(iroha::protocol::TxStatus::COMMITTED); + cache_->addItem(std::move(hash), response); } else { log_->warn("Asked non-existing tx: {}", iroha::bytestringToHexstring(request.tx_hash())); response.set_tx_status(iroha::protocol::TxStatus::NOT_RECEIVED); } - this->addTxToCacheAndLog( - "Status", std::move(tx_hash), std::move(response)); } } @@ -274,7 +269,8 @@ namespace torii { .build() .getTransport(); }()))); - return responses_ + return status_bus_ + ->statuses() // prepend initial status .start_with(initial_status) // select statuses with requested hash @@ -368,7 +364,7 @@ namespace torii { } } - void CommandService::addTxToCacheAndLog( + void CommandService::pushStatus( const std::string &who, const shared_model::crypto::Hash &hash, const iroha::protocol::ToriiResponse &response) { @@ -376,10 +372,7 @@ namespace torii { who, hash.hex(), response.tx_status()); - // transactions can be handled from multiple threads, therefore a lock is - // required - std::lock_guard lock(stateless_tx_status_notifier_mutex_); - stateless_notifier_.get_subscriber().on_next( + status_bus_->publish( std::make_shared( std::move(response))); } diff --git a/irohad/torii/impl/status_bus_impl.cpp b/irohad/torii/impl/status_bus_impl.cpp new file mode 100644 index 0000000000..e24a2edf8e --- /dev/null +++ b/irohad/torii/impl/status_bus_impl.cpp @@ -0,0 +1,21 @@ +/** + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "torii/impl/status_bus_impl.hpp" + +namespace iroha { + namespace torii { + StatusBusImpl::StatusBusImpl(rxcpp::observe_on_one_worker worker) + : worker_(worker), subject_(worker_) {} + + void StatusBusImpl::publish(StatusBus::Objects resp) { + subject_.get_subscriber().on_next(resp); + } + + rxcpp::observable StatusBusImpl::statuses() { + return subject_.get_observable(); + } + } // namespace torii +} // namespace iroha diff --git a/irohad/torii/impl/status_bus_impl.hpp b/irohad/torii/impl/status_bus_impl.hpp new file mode 100644 index 0000000000..f56e99405a --- /dev/null +++ b/irohad/torii/impl/status_bus_impl.hpp @@ -0,0 +1,33 @@ +/** + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef TORII_STATUS_BUS_IMPL +#define TORII_STATUS_BUS_IMPL + +#include "torii/status_bus.hpp" + +namespace iroha { + namespace torii { + /** + * StatusBus implementation + */ + class StatusBusImpl : public StatusBus { + public: + StatusBusImpl( + rxcpp::observe_on_one_worker worker = rxcpp::observe_on_new_thread()); + + void publish(StatusBus::Objects) override; + /// Subscribers will be invoked in separate thread + rxcpp::observable statuses() override; + + // Need to create once, otherwise will create thread for each subscriber + rxcpp::observe_on_one_worker worker_; + rxcpp::subjects::synchronize + subject_; + }; + } // namespace torii +} // namespace iroha + +#endif // TORII_STATUS_BUS_IMPL diff --git a/irohad/torii/processor/CMakeLists.txt b/irohad/torii/processor/CMakeLists.txt index 7dc7685717..d87aba6fbc 100644 --- a/irohad/torii/processor/CMakeLists.txt +++ b/irohad/torii/processor/CMakeLists.txt @@ -10,4 +10,5 @@ target_link_libraries(processors PUBLIC mst_processor shared_model_proto_builders query_execution + status_bus ) diff --git a/irohad/torii/processor/impl/transaction_processor_impl.cpp b/irohad/torii/processor/impl/transaction_processor_impl.cpp index 332bec52de..5762d0f37b 100644 --- a/irohad/torii/processor/impl/transaction_processor_impl.cpp +++ b/irohad/torii/processor/impl/transaction_processor_impl.cpp @@ -1,18 +1,6 @@ /** - * Copyright Soramitsu Co., Ltd. 2017 All Rights Reserved. - * http://soramitsu.co.jp - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 */ #include "torii/processor/transaction_processor_impl.hpp" @@ -50,10 +38,12 @@ namespace iroha { TransactionProcessorImpl::TransactionProcessorImpl( std::shared_ptr pcs, - std::shared_ptr mst_processor) - : pcs_(std::move(pcs)), mst_processor_(std::move(mst_processor)) { - log_ = logger::log("TxProcessor"); - + std::shared_ptr mst_processor, + std::shared_ptr status_bus) + : pcs_(std::move(pcs)), + mst_processor_(std::move(mst_processor)), + status_bus_(std::move(status_bus)), + log_(logger::log("TxProcessor")) { // notify about stateless success pcs_->on_proposal().subscribe([this](auto model_proposal) { for (const auto &tx : model_proposal->transactions()) { @@ -61,8 +51,7 @@ namespace iroha { log_->info("on proposal stateless success: {}", hash.hex()); // different on_next() calls (this one and below) can happen in // different threads and we don't expect emitting them concurrently - std::lock_guard lock(notifier_mutex_); - notifier_.get_subscriber().on_next( + status_bus_->publish( shared_model::builder::DefaultTransactionStatusBuilder() .statelessValidationSuccess() .txHash(hash) @@ -80,7 +69,7 @@ namespace iroha { for (const auto &tx_error : errors) { auto error_msg = composeErrorMessage(tx_error); log_->info(error_msg); - notifier_.get_subscriber().on_next( + status_bus_->publish( shared_model::builder::DefaultTransactionStatusBuilder() .statefulValidationFailed() .txHash(tx_error.second) @@ -92,7 +81,7 @@ namespace iroha { proposal_and_errors->first->transactions()) { log_->info("on stateful validation success: {}", successful_tx.hash().hex()); - notifier_.get_subscriber().on_next( + status_bus_->publish( shared_model::builder::DefaultTransactionStatusBuilder() .statefulValidationSuccess() .txHash(successful_tx.hash()) @@ -119,7 +108,7 @@ namespace iroha { std::lock_guard lock(notifier_mutex_); for (const auto &tx_hash : current_txs_hashes_) { log_->info("on commit committed: {}", tx_hash.hex()); - notifier_.get_subscriber().on_next( + status_bus_->publish( shared_model::builder::DefaultTransactionStatusBuilder() .committed() .txHash(tx_hash) @@ -137,7 +126,7 @@ namespace iroha { mst_processor_->onExpiredTransactions().subscribe([this](auto &&tx) { log_->info("MST tx expired"); std::lock_guard lock(notifier_mutex_); - this->notifier_.get_subscriber().on_next( + this->status_bus_->publish( shared_model::builder::DefaultTransactionStatusBuilder() .mstExpired() .txHash(tx->hash()) @@ -176,11 +165,5 @@ namespace iroha { } } - rxcpp::observable< - std::shared_ptr> - TransactionProcessorImpl::transactionNotifier() { - return notifier_.get_observable(); - } - } // namespace torii } // namespace iroha diff --git a/irohad/torii/processor/transaction_processor.hpp b/irohad/torii/processor/transaction_processor.hpp index e40b9dbfc1..2f48408c49 100644 --- a/irohad/torii/processor/transaction_processor.hpp +++ b/irohad/torii/processor/transaction_processor.hpp @@ -54,14 +54,6 @@ namespace iroha { const shared_model::interface::TransactionSequence &transaction_sequence) const = 0; - /** - * Subscribers will be notified with transaction status - * @return observable for subscribing - */ - virtual rxcpp::observable< - std::shared_ptr> - transactionNotifier() = 0; - virtual ~TransactionProcessor() = default; }; } // namespace torii diff --git a/irohad/torii/processor/transaction_processor_impl.hpp b/irohad/torii/processor/transaction_processor_impl.hpp index 7a2ff0c53e..4a249f573c 100644 --- a/irohad/torii/processor/transaction_processor_impl.hpp +++ b/irohad/torii/processor/transaction_processor_impl.hpp @@ -25,6 +25,7 @@ #include "multi_sig_transactions/mst_processor.hpp" #include "network/peer_communication_service.hpp" #include "torii/processor/transaction_processor.hpp" +#include "torii/status_bus.hpp" namespace iroha { namespace torii { @@ -33,10 +34,12 @@ namespace iroha { /** * @param pcs - provide information proposals and commits * @param mst_processor is a handler for multisignature transactions + * @param status_bus is a common notifier for tx statuses */ TransactionProcessorImpl( std::shared_ptr pcs, - std::shared_ptr mst_processor); + std::shared_ptr mst_processor, + std::shared_ptr status_bus); void transactionHandle( std::shared_ptr transaction) @@ -46,10 +49,6 @@ namespace iroha { const shared_model::interface::TransactionSequence &transaction_sequence) const override; - rxcpp::observable< - std::shared_ptr> - transactionNotifier() override; - private: // connections std::shared_ptr pcs_; @@ -58,6 +57,8 @@ namespace iroha { std::shared_ptr mst_processor_; std::vector current_txs_hashes_; + std::shared_ptr status_bus_; + // internal rxcpp::subjects::subject< std::shared_ptr> diff --git a/irohad/torii/status_bus.hpp b/irohad/torii/status_bus.hpp new file mode 100644 index 0000000000..35f8c41bba --- /dev/null +++ b/irohad/torii/status_bus.hpp @@ -0,0 +1,40 @@ +/** + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef TORII_STATUS_BUS +#define TORII_STATUS_BUS + +#include +#include "interfaces/transaction_responses/tx_response.hpp" + +namespace iroha { + namespace torii { + /** + * Interface of bus for transaction statuses + */ + class StatusBus { + public: + virtual ~StatusBus() = default; + + /// Objects that represent status to operate with + using Objects = + std::shared_ptr; + + /** + * Shares object among the bus subscribers + * @param object to share + * note: guaranteed to be non-blocking call + */ + virtual void publish(Objects) = 0; + + /** + * @return observable over objects in bus + */ + virtual rxcpp::observable statuses() = 0; + }; + } // namespace torii +} // namespace iroha + +#endif // TORII_STATUS_BUS diff --git a/test/framework/integration_framework/integration_test_framework.cpp b/test/framework/integration_framework/integration_test_framework.cpp index c89171a81a..412102cb3b 100644 --- a/test/framework/integration_framework/integration_test_framework.cpp +++ b/test/framework/integration_framework/integration_test_framework.cpp @@ -19,6 +19,8 @@ #include +#include + #include "backend/protobuf/block.hpp" #include "backend/protobuf/queries/proto_query.hpp" #include "backend/protobuf/query_responses/proto_query_response.hpp" @@ -179,10 +181,30 @@ namespace integration_framework { std::function validation) { log_->info("send transaction"); + + // Required for StatusBus synchronization + boost::barrier bar1(2); + auto bar2 = std::make_shared(2); + iroha_instance_->instance_->getStatusBus() + ->statuses() + .filter([&](auto s) { return s->transactionHash() == tx.hash(); }) + .take(1) + .subscribe([&bar1, b2 = std::weak_ptr(bar2)](auto s) { + bar1.wait(); + if (auto lock = b2.lock()) { + lock->wait(); + } + }); + iroha_instance_->getIrohaInstance()->getCommandService()->Torii( tx.getTransport()); + // make sure that the first (stateless) status is come + bar1.wait(); // fetch status of transaction shared_model::proto::TransactionResponse status = getTxStatus(tx.hash()); + // make sure that the following statuses (stateful/commited) + // isn't reached the bus yet + bar2->wait(); // check validation function validation(status); diff --git a/test/framework/integration_framework/test_irohad.hpp b/test/framework/integration_framework/test_irohad.hpp index 25c324d9d8..214a7a1735 100644 --- a/test/framework/integration_framework/test_irohad.hpp +++ b/test/framework/integration_framework/test_irohad.hpp @@ -64,6 +64,10 @@ namespace integration_framework { return crypto_signer_; } + auto getStatusBus() { + return status_bus_; + } + void run() override { internal_server = std::make_unique( "127.0.0.1:" + std::to_string(internal_port_)); diff --git a/test/module/iroha-cli/client_test.cpp b/test/module/iroha-cli/client_test.cpp index fe522e5902..0e08a1f9b8 100644 --- a/test/module/iroha-cli/client_test.cpp +++ b/test/module/iroha-cli/client_test.cpp @@ -19,6 +19,7 @@ #include "execution/query_execution_impl.hpp" #include "main/server_runner.hpp" #include "torii/command_service.hpp" +#include "torii/impl/status_bus_impl.hpp" #include "torii/processor/query_processor_impl.hpp" #include "torii/processor/transaction_processor_impl.hpp" #include "torii/query_service.hpp" @@ -74,8 +75,10 @@ class ClientServerTest : public testing::Test { EXPECT_CALL(*mst, onExpiredTransactionsImpl()) .WillRepeatedly(Return(mst_expired_notifier.get_observable())); + auto status_bus = std::make_shared(); auto tx_processor = - std::make_shared(pcsMock, mst); + std::make_shared( + pcsMock, mst, status_bus); auto pb_tx_factory = std::make_shared(); @@ -89,8 +92,11 @@ class ClientServerTest : public testing::Test { //----------- Server run ---------------- runner - ->append(std::make_unique( - tx_processor, storage, initial_timeout, nonfinal_timeout)) + ->append(std::make_unique(tx_processor, + storage, + status_bus, + initial_timeout, + nonfinal_timeout)) .append(std::make_unique(qpi)) .run() .match( diff --git a/test/module/irohad/torii/processor/transaction_processor_test.cpp b/test/module/irohad/torii/processor/transaction_processor_test.cpp index 4765b570a0..69a3d4c15b 100644 --- a/test/module/irohad/torii/processor/transaction_processor_test.cpp +++ b/test/module/irohad/torii/processor/transaction_processor_test.cpp @@ -13,9 +13,11 @@ #include "interfaces/iroha_internal/transaction_sequence.hpp" #include "module/irohad/multi_sig_transactions/mst_mocks.hpp" #include "module/irohad/network/network_mocks.hpp" +#include "module/irohad/torii/torii_mocks.hpp" #include "module/shared_model/builders/protobuf/test_block_builder.hpp" #include "module/shared_model/builders/protobuf/test_proposal_builder.hpp" #include "module/shared_model/builders/protobuf/test_transaction_builder.hpp" +#include "torii/impl/status_bus_impl.hpp" #include "torii/processor/transaction_processor_impl.hpp" using namespace iroha; @@ -45,7 +47,8 @@ class TransactionProcessorTest : public ::testing::Test { EXPECT_CALL(*mp, onExpiredTransactionsImpl()) .WillRepeatedly(Return(mst_expired_notifier.get_observable())); - tp = std::make_shared(pcs, mp); + status_bus = std::make_shared(); + tp = std::make_shared(pcs, mp, status_bus); } auto base_tx() { @@ -82,6 +85,7 @@ class TransactionProcessorTest : public ::testing::Test { rxcpp::subjects::subject mst_expired_notifier; std::shared_ptr pcs; + std::shared_ptr status_bus; std::shared_ptr tp; std::shared_ptr mp; @@ -115,11 +119,11 @@ TEST_F(TransactionProcessorTest, TransactionProcessorOnProposalTest) { status_builder.notReceived().txHash(tx.hash()).build(); } - auto wrapper = - make_test_subscriber(tp->transactionNotifier(), proposal_size); - wrapper.subscribe([this](auto response) { - status_map[response->transactionHash()] = response; - }); + EXPECT_CALL(*status_bus, publish(_)) + .Times(proposal_size) + .WillRepeatedly(testing::Invoke([this](auto response) { + status_map[response->transactionHash()] = response; + })); EXPECT_CALL(*mp, propagateTransactionImpl(_)).Times(0); EXPECT_CALL(*pcs, propagate_transaction(_)).Times(txs.size()); @@ -136,8 +140,6 @@ TEST_F(TransactionProcessorTest, TransactionProcessorOnProposalTest) { prop_notifier.get_subscriber().on_next(proposal); prop_notifier.get_subscriber().on_completed(); - ASSERT_TRUE(wrapper.validate()); - SCOPED_TRACE("Stateless valid status verification"); validateStatuses(txs); } @@ -160,11 +162,11 @@ TEST_F(TransactionProcessorTest, TransactionProcessorOnProposalBatchTest) { auto transactions = framework::batch::createValidBatch(proposal_size).transactions(); - auto wrapper = - make_test_subscriber(tp->transactionNotifier(), proposal_size); - wrapper.subscribe([this](auto response) { - status_map[response->transactionHash()] = response; - }); + EXPECT_CALL(*status_bus, publish(_)) + .Times(proposal_size) + .WillRepeatedly(testing::Invoke([this](auto response) { + status_map[response->transactionHash()] = response; + })); auto transaction_sequence_result = shared_model::interface::TransactionSequence::createTransactionSequence( @@ -195,8 +197,6 @@ TEST_F(TransactionProcessorTest, TransactionProcessorOnProposalBatchTest) { prop_notifier.get_subscriber().on_next(proposal); prop_notifier.get_subscriber().on_completed(); - ASSERT_TRUE(wrapper.validate()); - SCOPED_TRACE("Stateless valid status verification"); validateStatuses( proto_transactions); @@ -217,13 +217,11 @@ TEST_F(TransactionProcessorTest, TransactionProcessorBlockCreatedTest) { status_builder.notReceived().txHash(tx.hash()).build(); } - auto wrapper = make_test_subscriber( - tp->transactionNotifier(), - txs.size() * 2); // every transaction is notified that it is stateless - // valid and then stateful valid - wrapper.subscribe([this](auto response) { - status_map[response->transactionHash()] = response; - }); + EXPECT_CALL(*status_bus, publish(_)) + .Times(txs.size() * 2) + .WillRepeatedly(testing::Invoke([this](auto response) { + status_map[response->transactionHash()] = response; + })); EXPECT_CALL(*mp, propagateTransactionImpl(_)).Times(0); EXPECT_CALL(*pcs, propagate_transaction(_)).Times(txs.size()); @@ -258,8 +256,6 @@ TEST_F(TransactionProcessorTest, TransactionProcessorBlockCreatedTest) { // Note blocks_notifier hasn't invoked on_completed, so // transactions are not commited - ASSERT_TRUE(wrapper.validate()); - SCOPED_TRACE("Stateful valid status verification"); validateStatuses(txs); } @@ -280,14 +276,11 @@ TEST_F(TransactionProcessorTest, TransactionProcessorOnCommitTest) { status_builder.notReceived().txHash(tx.hash()).build(); } - auto wrapper = make_test_subscriber( - tp->transactionNotifier(), - txs.size() * 3); // evey transaction is notified that it is first - // stateless valid, then stateful valid and - // eventually committed - wrapper.subscribe([this](auto response) { - status_map[response->transactionHash()] = response; - }); + EXPECT_CALL(*status_bus, publish(_)) + .Times(txs.size() * 3) + .WillRepeatedly(testing::Invoke([this](auto response) { + status_map[response->transactionHash()] = response; + })); EXPECT_CALL(*mp, propagateTransactionImpl(_)).Times(0); EXPECT_CALL(*pcs, propagate_transaction(_)).Times(txs.size()); @@ -316,8 +309,6 @@ TEST_F(TransactionProcessorTest, TransactionProcessorOnCommitTest) { std::shared_ptr(clone(block))); commit_notifier.get_subscriber().on_next(single_commit); - ASSERT_TRUE(wrapper.validate()); - SCOPED_TRACE("Committed status verification"); validateStatuses(txs); } @@ -349,20 +340,18 @@ TEST_F(TransactionProcessorTest, TransactionProcessorInvalidTxsTest) { status_builder.notReceived().txHash(tx.hash()).build(); } - auto wrapper = make_test_subscriber( - tp->transactionNotifier(), - proposal_size * 2 - + block_size); // For all transactions from proposal - // transaction notifier will notified - // twice (first that they are stateless - // valid and second that they either - // passed or not stateful validation) - // Plus all transactions from block will - // be committed and corresponding status will be sent - - wrapper.subscribe([this](auto response) { - status_map[response->transactionHash()] = response; - }); + // For all transactions from proposal + // transaction will be published twice + // (first that they are stateless + // valid and second that they either + // passed or not stateful validation) + // Plus all transactions from block will + // be committed and corresponding status will be sent + EXPECT_CALL(*status_bus, publish(_)) + .Times(proposal_size * 2 + block_size) + .WillRepeatedly(testing::Invoke([this](auto response) { + status_map[response->transactionHash()] = response; + })); auto proposal = std::make_shared( TestProposalBuilder() @@ -391,7 +380,6 @@ TEST_F(TransactionProcessorTest, TransactionProcessorInvalidTxsTest) { Commit single_commit = rxcpp::observable<>::just( std::shared_ptr(clone(block))); commit_notifier.get_subscriber().on_next(single_commit); - ASSERT_TRUE(wrapper.validate()); { SCOPED_TRACE("Stateful invalid status verification"); @@ -462,16 +450,13 @@ TEST_F(TransactionProcessorTest, MultisigExpired) { shared_model::crypto::DefaultCryptoAlgorithmType:: generateKeypair()) .finish()); - - auto wrapper = make_test_subscriber(tp->transactionNotifier(), 1); - wrapper.subscribe([](auto response) { - ASSERT_NO_THROW( - boost::apply_visitor(framework::SpecifiedVisitor< - shared_model::interface::MstExpiredResponse>(), - response->get())); - }); + EXPECT_CALL(*status_bus, publish(_)) + .WillOnce(testing::Invoke([](auto response) { + ASSERT_NO_THROW(boost::apply_visitor( + framework::SpecifiedVisitor< + shared_model::interface::MstExpiredResponse>(), + response->get())); + })); tp->transactionHandle(tx); mst_expired_notifier.get_subscriber().on_next(tx); - - ASSERT_TRUE(wrapper.validate()); } diff --git a/test/module/irohad/torii/torii_mocks.hpp b/test/module/irohad/torii/torii_mocks.hpp index b30968fbee..cdc4a72123 100644 --- a/test/module/irohad/torii/torii_mocks.hpp +++ b/test/module/irohad/torii/torii_mocks.hpp @@ -1,27 +1,18 @@ /** - * Copyright Soramitsu Co., Ltd. 2017 All Rights Reserved. - * http://soramitsu.co.jp - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 */ #ifndef IROHA_TORII_MOCKS_HPP #define IROHA_TORII_MOCKS_HPP -#include "torii/processor/query_processor.hpp" - #include +#include "interfaces/query_responses/block_query_response.hpp" +#include "interfaces/query_responses/query_response.hpp" +#include "torii/processor/query_processor.hpp" +#include "torii/status_bus.hpp" + namespace iroha { namespace torii { @@ -36,6 +27,12 @@ namespace iroha { std::shared_ptr>( const shared_model::interface::BlocksQuery &)); }; + + class MockStatusBus : public StatusBus { + public: + MOCK_METHOD1(publish, void(StatusBus::Objects)); + MOCK_METHOD0(statuses, rxcpp::observable()); + }; } // namespace torii } // namespace iroha diff --git a/test/module/irohad/torii/torii_service_test.cpp b/test/module/irohad/torii/torii_service_test.cpp index c28deb1aa1..eda4d68ff1 100644 --- a/test/module/irohad/torii/torii_service_test.cpp +++ b/test/module/irohad/torii/torii_service_test.cpp @@ -1,18 +1,7 @@ -/* -Copyright Soramitsu Co., Ltd. 2017 All Rights Reserved. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ +/** + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ #include "builders/protobuf/block.hpp" #include "builders/protobuf/proposal.hpp" @@ -28,6 +17,7 @@ limitations under the License. #include "module/shared_model/builders/protobuf/test_transaction_builder.hpp" #include "torii/command_client.hpp" #include "torii/command_service.hpp" +#include "torii/impl/status_bus_impl.hpp" #include "torii/processor/transaction_processor_impl.hpp" constexpr size_t TimesToriiBlocking = 5; @@ -108,8 +98,10 @@ class ToriiServiceTest : public testing::Test { EXPECT_CALL(*mst, onExpiredTransactionsImpl()) .WillRepeatedly(Return(mst_expired_notifier.get_observable())); + auto status_bus = std::make_shared(); auto tx_processor = - std::make_shared(pcsMock, mst); + std::make_shared( + pcsMock, mst, status_bus); EXPECT_CALL(*block_query, getTxByHashSync(_)) .WillRepeatedly(Return(boost::none)); @@ -117,8 +109,11 @@ class ToriiServiceTest : public testing::Test { //----------- Server run ---------------- runner - ->append(std::make_unique( - tx_processor, storage, initial_timeout, nonfinal_timeout)) + ->append(std::make_unique(tx_processor, + storage, + status_bus, + initial_timeout, + nonfinal_timeout)) .run() .match( [this](iroha::expected::Value port) { From e07143314c3e0e938dd8b8c227deb4f1f39ed34a Mon Sep 17 00:00:00 2001 From: Nikolay Yushkevich Date: Thu, 26 Jul 2018 14:53:51 +0300 Subject: [PATCH 80/97] Delete codecov badge and android demo link (#1603) Signed-off-by: Nikolay Yushkevich --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index b0454fb804..709fd1a46a 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,6 @@ [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) [![CII Best Practices](https://bestpractices.coreinfrastructure.org/projects/960/badge)](https://bestpractices.coreinfrastructure.org/projects/960) -[![codecov](https://codecov.io/gh/hyperledger/iroha/branch/master/graph/badge.svg)](https://codecov.io/gh/hyperledger/iroha) [![Snap Status](https://build.snapcraft.io/badge/hyperledger/iroha.svg)](https://build.snapcraft.io/user/hyperledger/iroha) [![Build Status](https://jenkins.soramitsu.co.jp/buildStatus/icon?job=iroha/iroha-hyperledger/master)](https://jenkins.soramitsu.co.jp/job/iroha/job/iroha-hyperledger/job/master/) [![Throughput Graph](https://graphs.waffle.io/hyperledger/iroha/throughput.svg)](https://waffle.io/hyperledger/iroha/metrics/throughput) @@ -42,7 +41,7 @@ Yes, in [Java](http://iroha.readthedocs.io/en/latest/guides/libraries/java.html) ### Are there any example applications? -[Android point app](https://github.com/soramitsu/iroha-demo-android) and [JavaScript wallet](https://github.com/soramitsu/iroha-wallet-js). +[Android point app](https://github.com/hyperledger/iroha-android/tree/master/iroha-android-sample) and [JavaScript wallet](https://github.com/soramitsu/iroha-wallet-js). ## Need help? From d6384efd50cb67ad532b01ee9ec660dbe2c09c27 Mon Sep 17 00:00:00 2001 From: Kitsu Date: Thu, 26 Jul 2018 14:54:10 +0300 Subject: [PATCH 81/97] Update cmake (#1602) * Use -Wno-error=deprecated-declarations in release build * Use -std=c++14 Signed-off-by: Kitsu --- CMakeLists.txt | 4 ++-- shared_model/CMakeLists.txt | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 56497fa95d..95f04dd5d7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,8 +12,8 @@ endif() PROJECT(iroha C CXX) SET(CMAKE_POSITION_INDEPENDENT_CODE TRUE) -SET(CMAKE_CXX_FLAGS "-std=c++1y -Wall -fdiagnostics-color=always") -SET(CMAKE_CXX_FLAGS_RELEASE "-O3") +SET(CMAKE_CXX_FLAGS "-std=c++14 -Wall -fdiagnostics-color=always") +SET(CMAKE_CXX_FLAGS_RELEASE "-O3 -Wno-error=deprecated-declarations") SET(CMAKE_CXX_FLAGS_DEBUG "-g -Wextra -Wno-unused-parameter -Wno-deprecated-declarations -O0") SET(CMAKE_CXX_OUTPUT_EXTENSION_REPLACE 1) SET(CMAKE_INSTALL_RPATH "../lib") diff --git a/shared_model/CMakeLists.txt b/shared_model/CMakeLists.txt index b07a33ba7b..a567446448 100644 --- a/shared_model/CMakeLists.txt +++ b/shared_model/CMakeLists.txt @@ -16,7 +16,7 @@ if (NOT IROHA_ROOT_PROJECT) set(CMAKE_POSITION_INDEPENDENT_CODE TRUE) if (NOT MSVC) set(CMAKE_CXX_FLAGS "-std=c++14 -Wall -fdiagnostics-color=always") - set(CMAKE_CXX_FLAGS_RELEASE "-O3") + set(CMAKE_CXX_FLAGS_RELEASE "-O3 -Wno-error=deprecated-declarations") set(CMAKE_CXX_FLAGS_DEBUG "-g -Wextra -Wno-unused-parameter -Wno-deprecated-declarations -O0") endif () From 225bffb5a8eb7262b40d66957360fcf8bc65d8fe Mon Sep 17 00:00:00 2001 From: Artyom Bakhtin Date: Thu, 26 Jul 2018 18:42:59 +0300 Subject: [PATCH 82/97] push Docker image on develop base branch Signed-off-by: Artyom Bakhtin --- .jenkinsci/debug-build.groovy | 6 ++++-- .jenkinsci/release-build.groovy | 7 +++++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/.jenkinsci/debug-build.groovy b/.jenkinsci/debug-build.groovy index 47087c07eb..97900d873e 100644 --- a/.jenkinsci/debug-build.groovy +++ b/.jenkinsci/debug-build.groovy @@ -17,14 +17,16 @@ def doDebugBuild(coverageEnabled=false) { if (env.NODE_NAME.contains('arm7')) { parallelism = 1 } + sh "docker network create ${env.IROHA_NETWORK}" def iC = dPullOrBuild.dockerPullOrUpdate("${platform}-develop-build", "${env.GIT_RAW_BASE_URL}/${env.GIT_COMMIT}/docker/develop/Dockerfile", "${env.GIT_RAW_BASE_URL}/${previousCommit}/docker/develop/Dockerfile", "${env.GIT_RAW_BASE_URL}/develop/docker/develop/Dockerfile", ['PARALLELISM': parallelism]) - - if (GIT_LOCAL_BRANCH == 'develop' && manifest.manifestSupportEnabled()) { + // push Docker image in case the current branch is develop, + // or it is a commit into PR which base branch is develop (usually develop -> master) + if ((GIT_LOCAL_BRANCH == 'develop' || CHANGE_BRANCH == 'develop') && manifest.manifestSupportEnabled()) { manifest.manifestCreate("${DOCKER_REGISTRY_BASENAME}:develop-build", ["${DOCKER_REGISTRY_BASENAME}:x86_64-develop-build", "${DOCKER_REGISTRY_BASENAME}:armv7l-develop-build", diff --git a/.jenkinsci/release-build.groovy b/.jenkinsci/release-build.groovy index d24d6cfb31..3c4ebfbfe0 100644 --- a/.jenkinsci/release-build.groovy +++ b/.jenkinsci/release-build.groovy @@ -56,7 +56,10 @@ def doReleaseBuild() { sh "mv /tmp/${GIT_COMMIT}-${BUILD_NUMBER}/iroha.deb /tmp/${env.GIT_COMMIT}" sh "chmod +x /tmp/${env.GIT_COMMIT}/entrypoint.sh" iCRelease = docker.build("${DOCKER_REGISTRY_BASENAME}:${GIT_COMMIT}-${BUILD_NUMBER}-release", "--no-cache -f /tmp/${env.GIT_COMMIT}/Dockerfile /tmp/${env.GIT_COMMIT}") - if (env.GIT_LOCAL_BRANCH == 'develop') { + + // push Docker image in case the current branch is develop, + // or it is a commit into PR which base branch is develop (usually develop -> master) + if (GIT_LOCAL_BRANCH == 'develop' || CHANGE_BRANCH == 'develop') { iCRelease.push("${platform}-develop") if (manifest.manifestSupportEnabled()) { manifest.manifestCreate("${DOCKER_REGISTRY_BASENAME}:develop", @@ -77,7 +80,7 @@ def doReleaseBuild() { } } } - else if (env.GIT_LOCAL_BRANCH == 'master') { + else if (GIT_LOCAL_BRANCH == 'master') { iCRelease.push("${platform}-latest") if (manifest.manifestSupportEnabled()) { manifest.manifestCreate("${DOCKER_REGISTRY_BASENAME}:latest", From 9aa559059bf1289a1e3c01e07b282abe7bf7c603 Mon Sep 17 00:00:00 2001 From: Igor Egorov Date: Fri, 27 Jul 2018 09:42:47 +0300 Subject: [PATCH 83/97] Fix spdlog segfault (#1608) set_pattern could be called simultaneously from several threads affecting global context. The similar issue - https://github.com/gabime/spdlog/issues/419 Signed-off-by: Igor Egorov --- libs/logger/logger.cpp | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/libs/logger/logger.cpp b/libs/logger/logger.cpp index 95453ab8ef..08553e6fa8 100644 --- a/libs/logger/logger.cpp +++ b/libs/logger/logger.cpp @@ -37,25 +37,28 @@ namespace logger { return red("<--- " + string); } - static void setGlobalPattern() { - spdlog::set_pattern("[%Y-%m-%d %H:%M:%S.%F] %n %v"); + static void setGlobalPattern(spdlog::logger &logger) { + logger.set_pattern("[%Y-%m-%d %H:%M:%S.%F] %n %v"); } - static void setDebugPattern() { - spdlog::set_pattern("[%Y-%m-%d %H:%M:%S.%F][th:%t][%l] %n %v"); + static void setDebugPattern(spdlog::logger &logger) { + logger.set_pattern("[%Y-%m-%d %H:%M:%S.%F][th:%t][%l] %n %v"); } static std::shared_ptr createLogger(const std::string &tag, bool debug_mode = true) { + auto logger = spdlog::stdout_color_mt(tag); if (debug_mode) { - setDebugPattern(); + setDebugPattern(*logger); } else { - setGlobalPattern(); + setGlobalPattern(*logger); } - return spdlog::stdout_color_mt(tag); + return logger; } Logger log(const std::string &tag) { + static std::mutex mutex; + std::lock_guard lock(mutex); auto logger = spdlog::get(tag); if (logger == nullptr) { logger = createLogger(tag); From 30d7ee0905f8d43c9e7998236af0510cf2ff321a Mon Sep 17 00:00:00 2001 From: Artyom Bakhtin Date: Fri, 27 Jul 2018 09:55:38 +0300 Subject: [PATCH 84/97] fix missing property CHANGE_BRANCH (#1609) Signed-off-by: Artyom Bakhtin --- .jenkinsci/debug-build.groovy | 37 ++++++++------- .jenkinsci/release-build.groovy | 79 ++++++++++++++++++--------------- 2 files changed, 63 insertions(+), 53 deletions(-) diff --git a/.jenkinsci/debug-build.groovy b/.jenkinsci/debug-build.groovy index 97900d873e..980b3e33f5 100644 --- a/.jenkinsci/debug-build.groovy +++ b/.jenkinsci/debug-build.groovy @@ -26,24 +26,29 @@ def doDebugBuild(coverageEnabled=false) { ['PARALLELISM': parallelism]) // push Docker image in case the current branch is develop, // or it is a commit into PR which base branch is develop (usually develop -> master) - if ((GIT_LOCAL_BRANCH == 'develop' || CHANGE_BRANCH == 'develop') && manifest.manifestSupportEnabled()) { - manifest.manifestCreate("${DOCKER_REGISTRY_BASENAME}:develop-build", - ["${DOCKER_REGISTRY_BASENAME}:x86_64-develop-build", - "${DOCKER_REGISTRY_BASENAME}:armv7l-develop-build", - "${DOCKER_REGISTRY_BASENAME}:aarch64-develop-build"]) - manifest.manifestAnnotate("${DOCKER_REGISTRY_BASENAME}:develop-build", - [ - [manifest: "${DOCKER_REGISTRY_BASENAME}:x86_64-develop-build", - arch: 'amd64', os: 'linux', osfeatures: [], variant: ''], - [manifest: "${DOCKER_REGISTRY_BASENAME}:armv7l-develop-build", - arch: 'arm', os: 'linux', osfeatures: [], variant: 'v7'], - [manifest: "${DOCKER_REGISTRY_BASENAME}:aarch64-develop-build", - arch: 'arm64', os: 'linux', osfeatures: [], variant: ''] - ]) - withCredentials([usernamePassword(credentialsId: 'docker-hub-credentials', usernameVariable: 'login', passwordVariable: 'password')]) { - manifest.manifestPush("${DOCKER_REGISTRY_BASENAME}:develop-build", login, password) + // CHANGE_BRANCH is not defined if this is a branch + try { + if ((GIT_LOCAL_BRANCH == 'develop' || CHANGE_BRANCH == 'develop') && manifest.manifestSupportEnabled()) { + manifest.manifestCreate("${DOCKER_REGISTRY_BASENAME}:develop-build", + ["${DOCKER_REGISTRY_BASENAME}:x86_64-develop-build", + "${DOCKER_REGISTRY_BASENAME}:armv7l-develop-build", + "${DOCKER_REGISTRY_BASENAME}:aarch64-develop-build"]) + manifest.manifestAnnotate("${DOCKER_REGISTRY_BASENAME}:develop-build", + [ + [manifest: "${DOCKER_REGISTRY_BASENAME}:x86_64-develop-build", + arch: 'amd64', os: 'linux', osfeatures: [], variant: ''], + [manifest: "${DOCKER_REGISTRY_BASENAME}:armv7l-develop-build", + arch: 'arm', os: 'linux', osfeatures: [], variant: 'v7'], + [manifest: "${DOCKER_REGISTRY_BASENAME}:aarch64-develop-build", + arch: 'arm64', os: 'linux', osfeatures: [], variant: ''] + ]) + withCredentials([usernamePassword(credentialsId: 'docker-hub-credentials', usernameVariable: 'login', passwordVariable: 'password')]) { + manifest.manifestPush("${DOCKER_REGISTRY_BASENAME}:develop-build", login, password) + } } } + catch(MissingPropertyException e) {} + docker.image('postgres:9.5').withRun("" + " -e POSTGRES_USER=${env.IROHA_POSTGRES_USER}" + " -e POSTGRES_PASSWORD=${env.IROHA_POSTGRES_PASSWORD}" diff --git a/.jenkinsci/release-build.groovy b/.jenkinsci/release-build.groovy index 3c4ebfbfe0..16a83b14b9 100644 --- a/.jenkinsci/release-build.groovy +++ b/.jenkinsci/release-build.groovy @@ -59,48 +59,53 @@ def doReleaseBuild() { // push Docker image in case the current branch is develop, // or it is a commit into PR which base branch is develop (usually develop -> master) - if (GIT_LOCAL_BRANCH == 'develop' || CHANGE_BRANCH == 'develop') { - iCRelease.push("${platform}-develop") - if (manifest.manifestSupportEnabled()) { - manifest.manifestCreate("${DOCKER_REGISTRY_BASENAME}:develop", - ["${DOCKER_REGISTRY_BASENAME}:x86_64-develop", - "${DOCKER_REGISTRY_BASENAME}:armv7l-develop", - "${DOCKER_REGISTRY_BASENAME}:aarch64-develop"]) - manifest.manifestAnnotate("${DOCKER_REGISTRY_BASENAME}:develop", - [ - [manifest: "${DOCKER_REGISTRY_BASENAME}:x86_64-develop", - arch: 'amd64', os: 'linux', osfeatures: [], variant: ''], - [manifest: "${DOCKER_REGISTRY_BASENAME}:armv7l-develop", - arch: 'arm', os: 'linux', osfeatures: [], variant: 'v7'], - [manifest: "${DOCKER_REGISTRY_BASENAME}:aarch64-develop", - arch: 'arm64', os: 'linux', osfeatures: [], variant: ''] - ]) - withCredentials([usernamePassword(credentialsId: 'docker-hub-credentials', usernameVariable: 'login', passwordVariable: 'password')]) { - manifest.manifestPush("${DOCKER_REGISTRY_BASENAME}:develop", login, password) + // CHANGE_BRANCH is not defined if this is a branch + try { + if (GIT_LOCAL_BRANCH == 'develop' || CHANGE_BRANCH == 'develop') { + iCRelease.push("${platform}-develop") + if (manifest.manifestSupportEnabled()) { + manifest.manifestCreate("${DOCKER_REGISTRY_BASENAME}:develop", + ["${DOCKER_REGISTRY_BASENAME}:x86_64-develop", + "${DOCKER_REGISTRY_BASENAME}:armv7l-develop", + "${DOCKER_REGISTRY_BASENAME}:aarch64-develop"]) + manifest.manifestAnnotate("${DOCKER_REGISTRY_BASENAME}:develop", + [ + [manifest: "${DOCKER_REGISTRY_BASENAME}:x86_64-develop", + arch: 'amd64', os: 'linux', osfeatures: [], variant: ''], + [manifest: "${DOCKER_REGISTRY_BASENAME}:armv7l-develop", + arch: 'arm', os: 'linux', osfeatures: [], variant: 'v7'], + [manifest: "${DOCKER_REGISTRY_BASENAME}:aarch64-develop", + arch: 'arm64', os: 'linux', osfeatures: [], variant: ''] + ]) + withCredentials([usernamePassword(credentialsId: 'docker-hub-credentials', usernameVariable: 'login', passwordVariable: 'password')]) { + manifest.manifestPush("${DOCKER_REGISTRY_BASENAME}:develop", login, password) + } } } - } - else if (GIT_LOCAL_BRANCH == 'master') { - iCRelease.push("${platform}-latest") - if (manifest.manifestSupportEnabled()) { - manifest.manifestCreate("${DOCKER_REGISTRY_BASENAME}:latest", - ["${DOCKER_REGISTRY_BASENAME}:x86_64-latest", - "${DOCKER_REGISTRY_BASENAME}:armv7l-latest", - "${DOCKER_REGISTRY_BASENAME}:aarch64-latest"]) - manifest.manifestAnnotate("${DOCKER_REGISTRY_BASENAME}:latest", - [ - [manifest: "${DOCKER_REGISTRY_BASENAME}:x86_64-latest", - arch: 'amd64', os: 'linux', osfeatures: [], variant: ''], - [manifest: "${DOCKER_REGISTRY_BASENAME}:armv7l-latest", - arch: 'arm', os: 'linux', osfeatures: [], variant: 'v7'], - [manifest: "${DOCKER_REGISTRY_BASENAME}:aarch64-latest", - arch: 'arm64', os: 'linux', osfeatures: [], variant: ''] - ]) - withCredentials([usernamePassword(credentialsId: 'docker-hub-credentials', usernameVariable: 'login', passwordVariable: 'password')]) { - manifest.manifestPush("${DOCKER_REGISTRY_BASENAME}:latest", login, password) + else if (GIT_LOCAL_BRANCH == 'master') { + iCRelease.push("${platform}-latest") + if (manifest.manifestSupportEnabled()) { + manifest.manifestCreate("${DOCKER_REGISTRY_BASENAME}:latest", + ["${DOCKER_REGISTRY_BASENAME}:x86_64-latest", + "${DOCKER_REGISTRY_BASENAME}:armv7l-latest", + "${DOCKER_REGISTRY_BASENAME}:aarch64-latest"]) + manifest.manifestAnnotate("${DOCKER_REGISTRY_BASENAME}:latest", + [ + [manifest: "${DOCKER_REGISTRY_BASENAME}:x86_64-latest", + arch: 'amd64', os: 'linux', osfeatures: [], variant: ''], + [manifest: "${DOCKER_REGISTRY_BASENAME}:armv7l-latest", + arch: 'arm', os: 'linux', osfeatures: [], variant: 'v7'], + [manifest: "${DOCKER_REGISTRY_BASENAME}:aarch64-latest", + arch: 'arm64', os: 'linux', osfeatures: [], variant: ''] + ]) + withCredentials([usernamePassword(credentialsId: 'docker-hub-credentials', usernameVariable: 'login', passwordVariable: 'password')]) { + manifest.manifestPush("${DOCKER_REGISTRY_BASENAME}:latest", login, password) + } } } } + catch(MissingPropertyException e) {} + sh "docker rmi ${iCRelease.id}" } return this From 8f508ed1ec8640bfdec43d78e94a1c15586e34d0 Mon Sep 17 00:00:00 2001 From: Andrei Lebedev Date: Fri, 27 Jul 2018 12:33:26 +0300 Subject: [PATCH 85/97] Bump ed25519 version, remove patch, snapcraft fix attempt (#1607) Signed-off-by: Andrei Lebedev --- cmake/Modules/Finded25519.cmake | 7 +---- docker/android/Dockerfile | 2 +- docker/dependencies/Dockerfile | 2 +- docker/develop/Dockerfile | 2 +- patch/close.patch | 29 ------------------- .../packages/android/android-build.sh | 2 +- shared_model/packages/ios/ios-build.sh | 2 +- snap/snapcraft.yaml | 4 +-- 8 files changed, 8 insertions(+), 42 deletions(-) delete mode 100644 patch/close.patch diff --git a/cmake/Modules/Finded25519.cmake b/cmake/Modules/Finded25519.cmake index 57f5b7b054..1705d5dea6 100644 --- a/cmake/Modules/Finded25519.cmake +++ b/cmake/Modules/Finded25519.cmake @@ -12,12 +12,7 @@ find_package_handle_standard_args(ed25519 DEFAULT_MSG ) set(URL https://github.com/hyperledger/iroha-ed25519) -if (MSVC) - # trunk/1.2 with windows-specific changes - set(VERSION 31bb9b50e01b21ea2c21d33929e20934be4665b4) -else() - set(VERSION e7188b8393dbe5ac54378610d53630bd4a180038) -endif() +set(VERSION f42953c631fae93011612f6b1ee33f1f88c3f8af) set_target_description(ed25519 "Digital signature algorithm" ${URL} ${VERSION}) if (NOT ed25519_FOUND) diff --git a/docker/android/Dockerfile b/docker/android/Dockerfile index eb70b69ddb..6f4782ffaa 100644 --- a/docker/android/Dockerfile +++ b/docker/android/Dockerfile @@ -46,7 +46,7 @@ RUN set -ex; \ # ed25519 RUN set -e; \ git clone git://github.com/hyperledger/iroha-ed25519; \ - (cd ./iroha-ed25519 ; git checkout e7188b8393dbe5ac54378610d53630bd4a180038); \ + (cd ./iroha-ed25519 ; git checkout f42953c631fae93011612f6b1ee33f1f88c3f8af); \ cmake -DCMAKE_SYSTEM_NAME=Android -DCMAKE_SYSTEM_VERSION=$VERSION -DCMAKE_ANDROID_ARCH_ABI=$PLATFORM -DANDROID_NDK=$NDK_PATH -DCMAKE_ANDROID_STL_TYPE=c++_static -DCMAKE_INSTALL_PREFIX=$DEPS_DIR -DTESTING=OFF -DCMAKE_BUILD_TYPE=$BUILD_TYPE_A -DBUILD=STATIC -H./iroha-ed25519 -B./iroha-ed25519/build; \ VERBOSE=1 cmake --build ./iroha-ed25519/build --target install -- -j$PARALLELISM; \ mv "$DEPS_DIR"/lib/static/libed25519.a "$DEPS_DIR"/lib; rmdir "$DEPS_DIR"/lib/static/ diff --git a/docker/dependencies/Dockerfile b/docker/dependencies/Dockerfile index d9abf7bfb7..fb5a5a1c2a 100644 --- a/docker/dependencies/Dockerfile +++ b/docker/dependencies/Dockerfile @@ -234,7 +234,7 @@ RUN set -e; \ # install ed25519 RUN set -e; \ git clone git://github.com/hyperledger/iroha-ed25519.git /tmp/ed25519; \ - (cd /tmp/ed25519 ; git checkout e7188b8393dbe5ac54378610d53630bd4a180038); \ + (cd /tmp/ed25519 ; git checkout f42953c631fae93011612f6b1ee33f1f88c3f8af); \ cmake \ -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} \ -DTESTING=OFF \ diff --git a/docker/develop/Dockerfile b/docker/develop/Dockerfile index 0f584feeaa..fb8247e919 100644 --- a/docker/develop/Dockerfile +++ b/docker/develop/Dockerfile @@ -221,7 +221,7 @@ RUN set -e; \ # install ed25519 RUN set -e; \ git clone git://github.com/hyperledger/iroha-ed25519.git /tmp/ed25519; \ - (cd /tmp/ed25519 ; git checkout e7188b8393dbe5ac54378610d53630bd4a180038); \ + (cd /tmp/ed25519 ; git checkout f42953c631fae93011612f6b1ee33f1f88c3f8af); \ cmake \ -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} \ -DTESTING=OFF \ diff --git a/patch/close.patch b/patch/close.patch deleted file mode 100644 index 3138b895b6..0000000000 --- a/patch/close.patch +++ /dev/null @@ -1,29 +0,0 @@ -diff --git a/lib/randombytes/random/random.c b/lib/randombytes/random/random.c -index e5eca79..e6e862e 100644 ---- a/lib/randombytes/random/random.c -+++ b/lib/randombytes/random/random.c -@@ -13,6 +13,7 @@ int randombytes(unsigned char *p, int len) { - while (completed < len) { - ssize_t result = read(source, p + completed, len - completed); - if (result < 0) { -+ close(source); - return ED25519_ERROR; - } - completed += result; -diff --git a/lib/randombytes/urandom/urandom.c b/lib/randombytes/urandom/urandom.c -index ecad2cf..5b4ec0d 100644 ---- a/lib/randombytes/urandom/urandom.c -+++ b/lib/randombytes/urandom/urandom.c -@@ -9,9 +9,12 @@ int randombytes(unsigned char *p, int len) { - } else { - ssize_t result = read(source, p, len); - if (result < 0) { -+ close(source); - return ED25519_ERROR; /* something went wrong */ - } - } - -+ close(source); -+ - return ED25519_SUCCESS; - } diff --git a/shared_model/packages/android/android-build.sh b/shared_model/packages/android/android-build.sh index 3bea98a854..6a3b6783ba 100755 --- a/shared_model/packages/android/android-build.sh +++ b/shared_model/packages/android/android-build.sh @@ -93,7 +93,7 @@ VERBOSE=1 cmake --build ./protobuf/.build --target install -- -j"$CORES" # ed25519 git clone https://github.com/hyperledger/iroha-ed25519.git -(cd ./iroha-ed25519 ; git checkout e7188b8393dbe5ac54378610d53630bd4a180038) +(cd ./iroha-ed25519 ; git checkout f42953c631fae93011612f6b1ee33f1f88c3f8af) cmake "${ANDROID_TOOLCHAIN_ARGS[@]}" "${INSTALL_ARGS[@]}" -DTESTING=OFF -DCMAKE_BUILD_TYPE="$BUILD_TYPE" -DBUILD=STATIC -H./iroha-ed25519 -B./iroha-ed25519/build VERBOSE=1 cmake --build ./iroha-ed25519/build --target install -- -j"$CORES" mv "$DEPS_DIR"/lib/static/libed25519.a "$DEPS_DIR"/lib; rmdir "$DEPS_DIR"/lib/static/ diff --git a/shared_model/packages/ios/ios-build.sh b/shared_model/packages/ios/ios-build.sh index 1b54e3fb35..7875d28317 100755 --- a/shared_model/packages/ios/ios-build.sh +++ b/shared_model/packages/ios/ios-build.sh @@ -92,7 +92,7 @@ VERBOSE=1 cmake --build ./protobuf/.build --target install -- -j"$CORES" # ed25519 git clone https://github.com/hyperledger/iroha-ed25519.git (cd ./iroha-ed25519; -git checkout e7188b8393dbe5ac54378610d53630bd4a180038) +git checkout f42953c631fae93011612f6b1ee33f1f88c3f8af) cmake -DCMAKE_BUILD_TYPE="$BUILD_TYPE" "${IOS_TOOLCHAIN_ARGS[@]}" "${INSTALL_ARGS[@]}" -DTESTING=OFF -DBUILD=STATIC -H./iroha-ed25519 -B./iroha-ed25519/build VERBOSE=1 cmake --build ./iroha-ed25519/build --target install -- -j"$CORES" mv "$DEPS_DIR"/lib/static/libed25519.a "$DEPS_DIR"/lib; diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml index 129e10252f..27c93e962b 100644 --- a/snap/snapcraft.yaml +++ b/snap/snapcraft.yaml @@ -88,7 +88,7 @@ parts: - -DgRPC_PROTOBUF_PROVIDER=package - -DgRPC_GFLAGS_PROVIDER=package - -DBUILD_SHARED_LIBS=ON - after: [cmake, protobuf, c-ares] + after: [cmake, protobuf, c-ares, gflags] spdlog: source: https://github.com/gabime/spdlog.git source-commit: f85a08622e20b74bff34381cafcb8ef8167b29d0 @@ -150,7 +150,7 @@ parts: mv $SNAPCRAFT_PART_INSTALL/include $SNAPCRAFT_PART_INSTALL/usr/local/include ed25519: source: https://github.com/hyperledger/iroha-ed25519.git - source-commit: e7188b8393dbe5ac54378610d53630bd4a180038 + source-commit: f42953c631fae93011612f6b1ee33f1f88c3f8af plugin: cmake configflags: [-DTESTING=OFF] after: [cmake] From 18ad61d2f0ea5128486d3de42247533fac30def1 Mon Sep 17 00:00:00 2001 From: Artyom Bakhtin Date: Fri, 27 Jul 2018 17:12:14 +0300 Subject: [PATCH 86/97] Fix CI docker push (#1610) * fix docker push Signed-off-by: Artyom Bakhtin * handle exception earlier Signed-off-by: Artyom Bakhtin * fix missing property Signed-off-by: Artyom Bakhtin --- .jenkinsci/debug-build.groovy | 36 ++++++------ .jenkinsci/docker-pull-or-build.groovy | 4 +- .jenkinsci/release-build.groovy | 78 ++++++++++++-------------- Jenkinsfile | 9 ++- 4 files changed, 63 insertions(+), 64 deletions(-) diff --git a/.jenkinsci/debug-build.groovy b/.jenkinsci/debug-build.groovy index 980b3e33f5..b210b893dd 100644 --- a/.jenkinsci/debug-build.groovy +++ b/.jenkinsci/debug-build.groovy @@ -26,28 +26,24 @@ def doDebugBuild(coverageEnabled=false) { ['PARALLELISM': parallelism]) // push Docker image in case the current branch is develop, // or it is a commit into PR which base branch is develop (usually develop -> master) - // CHANGE_BRANCH is not defined if this is a branch - try { - if ((GIT_LOCAL_BRANCH == 'develop' || CHANGE_BRANCH == 'develop') && manifest.manifestSupportEnabled()) { - manifest.manifestCreate("${DOCKER_REGISTRY_BASENAME}:develop-build", - ["${DOCKER_REGISTRY_BASENAME}:x86_64-develop-build", - "${DOCKER_REGISTRY_BASENAME}:armv7l-develop-build", - "${DOCKER_REGISTRY_BASENAME}:aarch64-develop-build"]) - manifest.manifestAnnotate("${DOCKER_REGISTRY_BASENAME}:develop-build", - [ - [manifest: "${DOCKER_REGISTRY_BASENAME}:x86_64-develop-build", - arch: 'amd64', os: 'linux', osfeatures: [], variant: ''], - [manifest: "${DOCKER_REGISTRY_BASENAME}:armv7l-develop-build", - arch: 'arm', os: 'linux', osfeatures: [], variant: 'v7'], - [manifest: "${DOCKER_REGISTRY_BASENAME}:aarch64-develop-build", - arch: 'arm64', os: 'linux', osfeatures: [], variant: ''] - ]) - withCredentials([usernamePassword(credentialsId: 'docker-hub-credentials', usernameVariable: 'login', passwordVariable: 'password')]) { - manifest.manifestPush("${DOCKER_REGISTRY_BASENAME}:develop-build", login, password) - } + if ((GIT_LOCAL_BRANCH == 'develop' || CHANGE_BRANCH_LOCAL == 'develop') && manifest.manifestSupportEnabled()) { + manifest.manifestCreate("${DOCKER_REGISTRY_BASENAME}:develop-build", + ["${DOCKER_REGISTRY_BASENAME}:x86_64-develop-build", + "${DOCKER_REGISTRY_BASENAME}:armv7l-develop-build", + "${DOCKER_REGISTRY_BASENAME}:aarch64-develop-build"]) + manifest.manifestAnnotate("${DOCKER_REGISTRY_BASENAME}:develop-build", + [ + [manifest: "${DOCKER_REGISTRY_BASENAME}:x86_64-develop-build", + arch: 'amd64', os: 'linux', osfeatures: [], variant: ''], + [manifest: "${DOCKER_REGISTRY_BASENAME}:armv7l-develop-build", + arch: 'arm', os: 'linux', osfeatures: [], variant: 'v7'], + [manifest: "${DOCKER_REGISTRY_BASENAME}:aarch64-develop-build", + arch: 'arm64', os: 'linux', osfeatures: [], variant: ''] + ]) + withCredentials([usernamePassword(credentialsId: 'docker-hub-credentials', usernameVariable: 'login', passwordVariable: 'password')]) { + manifest.manifestPush("${DOCKER_REGISTRY_BASENAME}:develop-build", login, password) } } - catch(MissingPropertyException e) {} docker.image('postgres:9.5').withRun("" + " -e POSTGRES_USER=${env.IROHA_POSTGRES_USER}" diff --git a/.jenkinsci/docker-pull-or-build.groovy b/.jenkinsci/docker-pull-or-build.groovy index d699ac40bb..26660c33bc 100644 --- a/.jenkinsci/docker-pull-or-build.groovy +++ b/.jenkinsci/docker-pull-or-build.groovy @@ -38,7 +38,7 @@ def dockerPullOrUpdate(imageName, currentDockerfileURL, previousDockerfileURL, r def testExitCode = sh(script: "docker pull ${DOCKER_REGISTRY_BASENAME}:${imageName}", returnStatus: true) if (testExitCode != 0) { // image does not (yet) exist on Dockerhub. Build it - iC = docker.build("${DOCKER_REGISTRY_BASENAME}:${commit}-${BUILD_NUMBER}", "$buildOptions --no-cache -f /tmp/${env.GIT_COMMIT}/f1 /tmp/${env.GIT_COMMIT}") + iC = docker.build("${DOCKER_REGISTRY_BASENAME}:${commit}-${BUILD_NUMBER}", "$buildOptions --no-cache -f /tmp/${env.GIT_COMMIT}/f1 /tmp/${env.GIT_COMMIT}") } else { // no difference found compared to both previous and reference Dockerfile @@ -46,7 +46,7 @@ def dockerPullOrUpdate(imageName, currentDockerfileURL, previousDockerfileURL, r } } } - if (GIT_LOCAL_BRANCH ==~ /develop|master/) { + if (GIT_LOCAL_BRANCH ==~ /develop|master/ || CHANGE_BRANCH_LOCAL == 'develop') { docker.withRegistry('https://registry.hub.docker.com', 'docker-hub-credentials') { iC.push(imageName) } diff --git a/.jenkinsci/release-build.groovy b/.jenkinsci/release-build.groovy index 16a83b14b9..e9675dba54 100644 --- a/.jenkinsci/release-build.groovy +++ b/.jenkinsci/release-build.groovy @@ -59,52 +59,48 @@ def doReleaseBuild() { // push Docker image in case the current branch is develop, // or it is a commit into PR which base branch is develop (usually develop -> master) - // CHANGE_BRANCH is not defined if this is a branch - try { - if (GIT_LOCAL_BRANCH == 'develop' || CHANGE_BRANCH == 'develop') { - iCRelease.push("${platform}-develop") - if (manifest.manifestSupportEnabled()) { - manifest.manifestCreate("${DOCKER_REGISTRY_BASENAME}:develop", - ["${DOCKER_REGISTRY_BASENAME}:x86_64-develop", - "${DOCKER_REGISTRY_BASENAME}:armv7l-develop", - "${DOCKER_REGISTRY_BASENAME}:aarch64-develop"]) - manifest.manifestAnnotate("${DOCKER_REGISTRY_BASENAME}:develop", - [ - [manifest: "${DOCKER_REGISTRY_BASENAME}:x86_64-develop", - arch: 'amd64', os: 'linux', osfeatures: [], variant: ''], - [manifest: "${DOCKER_REGISTRY_BASENAME}:armv7l-develop", - arch: 'arm', os: 'linux', osfeatures: [], variant: 'v7'], - [manifest: "${DOCKER_REGISTRY_BASENAME}:aarch64-develop", - arch: 'arm64', os: 'linux', osfeatures: [], variant: ''] - ]) - withCredentials([usernamePassword(credentialsId: 'docker-hub-credentials', usernameVariable: 'login', passwordVariable: 'password')]) { - manifest.manifestPush("${DOCKER_REGISTRY_BASENAME}:develop", login, password) - } + if (GIT_LOCAL_BRANCH == 'develop' || CHANGE_BRANCH_LOCAL == 'develop') { + iCRelease.push("${platform}-develop") + if (manifest.manifestSupportEnabled()) { + manifest.manifestCreate("${DOCKER_REGISTRY_BASENAME}:develop", + ["${DOCKER_REGISTRY_BASENAME}:x86_64-develop", + "${DOCKER_REGISTRY_BASENAME}:armv7l-develop", + "${DOCKER_REGISTRY_BASENAME}:aarch64-develop"]) + manifest.manifestAnnotate("${DOCKER_REGISTRY_BASENAME}:develop", + [ + [manifest: "${DOCKER_REGISTRY_BASENAME}:x86_64-develop", + arch: 'amd64', os: 'linux', osfeatures: [], variant: ''], + [manifest: "${DOCKER_REGISTRY_BASENAME}:armv7l-develop", + arch: 'arm', os: 'linux', osfeatures: [], variant: 'v7'], + [manifest: "${DOCKER_REGISTRY_BASENAME}:aarch64-develop", + arch: 'arm64', os: 'linux', osfeatures: [], variant: ''] + ]) + withCredentials([usernamePassword(credentialsId: 'docker-hub-credentials', usernameVariable: 'login', passwordVariable: 'password')]) { + manifest.manifestPush("${DOCKER_REGISTRY_BASENAME}:develop", login, password) } } - else if (GIT_LOCAL_BRANCH == 'master') { - iCRelease.push("${platform}-latest") - if (manifest.manifestSupportEnabled()) { - manifest.manifestCreate("${DOCKER_REGISTRY_BASENAME}:latest", - ["${DOCKER_REGISTRY_BASENAME}:x86_64-latest", - "${DOCKER_REGISTRY_BASENAME}:armv7l-latest", - "${DOCKER_REGISTRY_BASENAME}:aarch64-latest"]) - manifest.manifestAnnotate("${DOCKER_REGISTRY_BASENAME}:latest", - [ - [manifest: "${DOCKER_REGISTRY_BASENAME}:x86_64-latest", - arch: 'amd64', os: 'linux', osfeatures: [], variant: ''], - [manifest: "${DOCKER_REGISTRY_BASENAME}:armv7l-latest", - arch: 'arm', os: 'linux', osfeatures: [], variant: 'v7'], - [manifest: "${DOCKER_REGISTRY_BASENAME}:aarch64-latest", - arch: 'arm64', os: 'linux', osfeatures: [], variant: ''] - ]) - withCredentials([usernamePassword(credentialsId: 'docker-hub-credentials', usernameVariable: 'login', passwordVariable: 'password')]) { - manifest.manifestPush("${DOCKER_REGISTRY_BASENAME}:latest", login, password) - } + } + else if (GIT_LOCAL_BRANCH == 'master') { + iCRelease.push("${platform}-latest") + if (manifest.manifestSupportEnabled()) { + manifest.manifestCreate("${DOCKER_REGISTRY_BASENAME}:latest", + ["${DOCKER_REGISTRY_BASENAME}:x86_64-latest", + "${DOCKER_REGISTRY_BASENAME}:armv7l-latest", + "${DOCKER_REGISTRY_BASENAME}:aarch64-latest"]) + manifest.manifestAnnotate("${DOCKER_REGISTRY_BASENAME}:latest", + [ + [manifest: "${DOCKER_REGISTRY_BASENAME}:x86_64-latest", + arch: 'amd64', os: 'linux', osfeatures: [], variant: ''], + [manifest: "${DOCKER_REGISTRY_BASENAME}:armv7l-latest", + arch: 'arm', os: 'linux', osfeatures: [], variant: 'v7'], + [manifest: "${DOCKER_REGISTRY_BASENAME}:aarch64-latest", + arch: 'arm64', os: 'linux', osfeatures: [], variant: ''] + ]) + withCredentials([usernamePassword(credentialsId: 'docker-hub-credentials', usernameVariable: 'login', passwordVariable: 'password')]) { + manifest.manifestPush("${DOCKER_REGISTRY_BASENAME}:latest", login, password) } } } - catch(MissingPropertyException e) {} sh "docker rmi ${iCRelease.id}" } diff --git a/Jenkinsfile b/Jenkinsfile index 14b2b4de7f..38dfe6e3c5 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -35,6 +35,7 @@ pipeline { IROHA_POSTGRES_USER = "pguser${GIT_COMMIT}" IROHA_POSTGRES_PASSWORD = "${GIT_COMMIT}" IROHA_POSTGRES_PORT = 5432 + CHANGE_BRANCH_LOCAL = '' } options { @@ -48,7 +49,13 @@ pipeline { agent { label 'master' } steps { script { - if (GIT_LOCAL_BRANCH != "develop") { + // need this for develop->master PR cases + // CHANGE_BRANCH is not defined if this is a branch build + try { + CHANGE_BRANCH_LOCAL = env.CHANGE_BRANCH + } + catch(MissingPropertyException e) { } + if (GIT_LOCAL_BRANCH != "develop" && CHANGE_BRANCH_LOCAL != "develop") { def builds = load ".jenkinsci/cancel-builds-same-job.groovy" builds.cancelSameJobBuilds() } From d1a47e6c1963ade495d7725fbd05ac5d063da484 Mon Sep 17 00:00:00 2001 From: Andrei Lebedev Date: Fri, 27 Jul 2018 21:08:34 +0300 Subject: [PATCH 87/97] Remove patch call in ed25519 (#1613) Signed-off-by: Andrei Lebedev --- cmake/Modules/Finded25519.cmake | 9 --------- 1 file changed, 9 deletions(-) diff --git a/cmake/Modules/Finded25519.cmake b/cmake/Modules/Finded25519.cmake index 1705d5dea6..e82fc30aca 100644 --- a/cmake/Modules/Finded25519.cmake +++ b/cmake/Modules/Finded25519.cmake @@ -16,15 +16,6 @@ set(VERSION f42953c631fae93011612f6b1ee33f1f88c3f8af) set_target_description(ed25519 "Digital signature algorithm" ${URL} ${VERSION}) if (NOT ed25519_FOUND) - if (NOT WIN32) - find_package(Git REQUIRED) - set(PATCH_RANDOM ${GIT_EXECUTABLE} apply ${PROJECT_SOURCE_DIR}) - if (NOT IROHA_ROOT_PROJECT) - set(PATCH_RANDOM ${PATCH_RANDOM}/..) - endif () - set(PATCH_RANDOM ${PATCH_RANDOM}/patch/close.patch || true) - endif () - externalproject_add(hyperledger_ed25519 GIT_REPOSITORY ${URL} GIT_TAG ${VERSION} From 8ea9f487a1498dcdf496e439bcb16413c09a7b43 Mon Sep 17 00:00:00 2001 From: Pavel Kotov Date: Sun, 29 Jul 2018 15:34:01 +0300 Subject: [PATCH 88/97] Single Pointer Cache (#1604) Signed-off-by: Akvinikym --- libs/cache/single_pointer_cache.hpp | 76 +++++++++++++ test/module/libs/cache/CMakeLists.txt | 2 + .../libs/cache/single_pointer_cache_test.cpp | 107 ++++++++++++++++++ 3 files changed, 185 insertions(+) create mode 100644 libs/cache/single_pointer_cache.hpp create mode 100644 test/module/libs/cache/single_pointer_cache_test.cpp diff --git a/libs/cache/single_pointer_cache.hpp b/libs/cache/single_pointer_cache.hpp new file mode 100644 index 0000000000..dc75fb606b --- /dev/null +++ b/libs/cache/single_pointer_cache.hpp @@ -0,0 +1,76 @@ +/** + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef IROHA_SINGLE_POINTER_CACHE_HPP +#define IROHA_SINGLE_POINTER_CACHE_HPP + +#include +#include + +namespace iroha { + namespace cache { + + /** + * Thread-safely stores and returns shared pointer to an element of template + * type + */ + template + class SinglePointerCache { + public: + /** + * Pointer to data type + */ + using DataPointer = std::shared_ptr; + + /** + * Insert data to the cache + * @param pointer to the data to be inserted + */ + void insert(DataPointer data); + + /** + * Get data from the cache + * @return pointer to the stored data + */ + DataPointer get() const; + + /** + * Delete data inside the cache + */ + void release(); + + private: + DataPointer stored_data_; + + mutable std::mutex mutex_; + }; + + template + void SinglePointerCache::insert( + SinglePointerCache::DataPointer data) { + std::lock_guard lock(mutex_); + + stored_data_ = std::move(data); + } + + template + typename SinglePointerCache::DataPointer + SinglePointerCache::get() const { + std::lock_guard lock(mutex_); + + return stored_data_; + } + + template + void SinglePointerCache::release() { + std::lock_guard lock(mutex_); + + stored_data_.reset(); + } + + } // namespace cache +} // namespace iroha + +#endif // IROHA_SINGLE_POINTER_CACHE_HPP diff --git a/test/module/libs/cache/CMakeLists.txt b/test/module/libs/cache/CMakeLists.txt index b64871bb31..40879455bd 100644 --- a/test/module/libs/cache/CMakeLists.txt +++ b/test/module/libs/cache/CMakeLists.txt @@ -16,3 +16,5 @@ addtest(cache_test cache_test.cpp) target_link_libraries(cache_test torii_service ) + +addtest(single_pointer_cache_test single_pointer_cache_test.cpp) diff --git a/test/module/libs/cache/single_pointer_cache_test.cpp b/test/module/libs/cache/single_pointer_cache_test.cpp new file mode 100644 index 0000000000..c4d5f75f19 --- /dev/null +++ b/test/module/libs/cache/single_pointer_cache_test.cpp @@ -0,0 +1,107 @@ +/** + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#include + +#include "cache/single_pointer_cache.hpp" + +using namespace iroha::cache; + +class SinglePointerCacheTest : public ::testing::Test { + using SinglePointerIntCache = SinglePointerCache; + + protected: + void SetUp() override { + int_cache.release(); + } + + SinglePointerIntCache int_cache; + const int default_int_value = 5; +}; + +/** + * @given empty int cache + * @when trying to get the value inside + * @then cache will return nullptr + */ +TEST_F(SinglePointerCacheTest, GetWhenEmpty) { + ASSERT_FALSE(int_cache.get()); +} + +/** + * @given empty int cache + * @when inserting some value into it @and trying to get it + * @then cache will return the inserted value + */ +TEST_F(SinglePointerCacheTest, Insert) { + int_cache.insert(std::make_shared(default_int_value)); + ASSERT_EQ(*int_cache.get(), default_int_value); +} + +/** + * @given empty int cache + * @when inserting some value into it @and releasing the cache @and trying to + * get value inside + * @then cache will return nullptr + */ +TEST_F(SinglePointerCacheTest, Release) { + int_cache.insert(std::make_shared(default_int_value)); + ASSERT_TRUE(int_cache.get()); + + int_cache.release(); + ASSERT_FALSE(int_cache.get()); +} + +/** + * @given empty int cache + * @when several readers reading values from cache @and several writers writing + * values to the cache @and releaser emptyfying the cache are spawned + * @then the system must not crash + */ +TEST_F(SinglePointerCacheTest, MultithreadedCache) { + constexpr std::chrono::milliseconds sleep_interval{100}; + constexpr int run_times{10}; + + auto read = [this, &sleep_interval] { + // if cache is not empty, read the value; otherwise do nothing + for (auto i = 0; i < run_times; ++i) { + auto value_ptr = int_cache.get(); + if (value_ptr) { + ASSERT_NO_THROW(*value_ptr); + } + std::this_thread::sleep_for(sleep_interval); + } + }; + auto write_one = [this, &sleep_interval] { + // just write to cache + for (auto i = 0; i < run_times; i++) { + std::this_thread::sleep_for(sleep_interval); + int_cache.insert(std::make_shared(i)); + } + }; + auto write_two = [this, &sleep_interval] { + // just write to cache + for (auto i = run_times; i > 0; --i) { + std::this_thread::sleep_for(sleep_interval); + int_cache.insert(std::make_shared(i)); + } + }; + auto release = [this, &sleep_interval] { + // release the cache + for (auto i = 0; i < run_times; ++i) { + int_cache.release(); + std::this_thread::sleep_for(sleep_interval); + } + }; + + std::thread writer_one{write_one}, reader{read}, releaser{release}, writer_two{write_two}; + writer_one.join(); + reader.join(); + releaser.join(); + writer_two.join(); +} From c4b95f6880a3720025e022eea277f2fdb514d3aa Mon Sep 17 00:00:00 2001 From: Nikita Alekseev Date: Sun, 29 Jul 2018 21:52:01 +0300 Subject: [PATCH 89/97] Implement Proposal Factory and use it in various components (#1587) Components where the factory is used: - ordering service, simulator Signed-off-by: Nikita Alekseev --- irohad/main/application.cpp | 5 +- irohad/main/impl/ordering_init.cpp | 5 +- .../impl/ordering_gate_transport_grpc.cpp | 36 ++++---- .../impl/ordering_gate_transport_grpc.hpp | 5 ++ .../ordering/impl/ordering_service_impl.cpp | 69 ++++++++-------- .../ordering/impl/ordering_service_impl.hpp | 5 ++ irohad/validation/CMakeLists.txt | 1 - .../impl/stateful_validator_impl.cpp | 27 +++--- .../impl/stateful_validator_impl.hpp | 6 +- .../protobuf/proto_proposal_factory.hpp | 82 +++++++++++++++++++ .../iroha_internal/proposal_factory.hpp | 36 ++++++++ .../unsafe_proposal_factory.hpp | 32 ++++++++ .../transport/ordering_gate_service_test.cpp | 8 +- test/module/irohad/ordering/CMakeLists.txt | 1 + .../irohad/ordering/ordering_service_test.cpp | 6 ++ test/module/irohad/validation/CMakeLists.txt | 1 + .../validation/stateful_validator_test.cpp | 6 +- .../shared_model/backend_proto/CMakeLists.txt | 10 ++- .../proto_common_objects_factory_test.cpp | 1 - .../proto_proposal_factory_test.cpp | 74 +++++++++++++++++ .../shared_model/validators/validators.hpp | 2 + 21 files changed, 342 insertions(+), 76 deletions(-) create mode 100644 shared_model/backend/protobuf/proto_proposal_factory.hpp create mode 100644 shared_model/interfaces/iroha_internal/proposal_factory.hpp create mode 100644 shared_model/interfaces/iroha_internal/unsafe_proposal_factory.hpp create mode 100644 test/module/shared_model/backend_proto/proto_proposal_factory_test.cpp diff --git a/irohad/main/application.cpp b/irohad/main/application.cpp index 4a0f56ac2c..95e3d6e56a 100644 --- a/irohad/main/application.cpp +++ b/irohad/main/application.cpp @@ -149,7 +149,10 @@ void Irohad::initCryptoProvider() { * Initializing validators */ void Irohad::initValidators() { - stateful_validator = std::make_shared(); + auto factory = std::make_unique>(); + stateful_validator = + std::make_shared(std::move(factory)); chain_validator = std::make_shared( std::make_shared()); diff --git a/irohad/main/impl/ordering_init.cpp b/irohad/main/impl/ordering_init.cpp index 12ce15790b..3c47aea65b 100644 --- a/irohad/main/impl/ordering_init.cpp +++ b/irohad/main/impl/ordering_init.cpp @@ -40,13 +40,16 @@ namespace iroha { std::shared_ptr transport, std::shared_ptr persistent_state) { + auto factory = std::make_unique>(); return std::make_shared( wsv, max_size, rxcpp::observable<>::interval(delay_milliseconds, rxcpp::observe_on_new_thread()), transport, - persistent_state); + persistent_state, + std::move(factory)); } std::shared_ptr OrderingInit::initOrderingGate( diff --git a/irohad/ordering/impl/ordering_gate_transport_grpc.cpp b/irohad/ordering/impl/ordering_gate_transport_grpc.cpp index a961f22b0f..54fd427f11 100644 --- a/irohad/ordering/impl/ordering_gate_transport_grpc.cpp +++ b/irohad/ordering/impl/ordering_gate_transport_grpc.cpp @@ -30,24 +30,22 @@ grpc::Status OrderingGateTransportGrpc::onProposal( ::google::protobuf::Empty *response) { log_->info("receive proposal"); - std::vector transactions; - for (const auto &tx : request->transactions()) { - transactions.emplace_back(tx); - } - log_->info("transactions in proposal: {}", transactions.size()); - - auto proposal = std::make_shared( - shared_model::proto::ProposalBuilder() - .transactions(transactions) - .height(request->height()) - .createdTime(request->created_time()) - .build()); + auto proposal_res = factory_->createProposal(*request); + proposal_res.match( + [this](iroha::expected::Value< + std::unique_ptr> &v) { + log_->info("transactions in proposal: {}", + v.value->transactions().size()); - if (not subscriber_.expired()) { - subscriber_.lock()->onProposal(std::move(proposal)); - } else { - log_->error("(onProposal) No subscriber"); - } + if (not subscriber_.expired()) { + subscriber_.lock()->onProposal(std::move(v.value)); + } else { + log_->error("(onProposal) No subscriber"); + } + }, + [this](const iroha::expected::Error &e) { + log_->error("Received invalid proposal: {}", e.error); + }); return grpc::Status::OK; } @@ -57,7 +55,9 @@ OrderingGateTransportGrpc::OrderingGateTransportGrpc( : network::AsyncGrpcClient( logger::log("OrderingGate")), client_(network::createClient( - server_address)) {} + server_address)), + factory_(std::make_unique>()) {} void OrderingGateTransportGrpc::propagateTransaction( std::shared_ptr transaction) { diff --git a/irohad/ordering/impl/ordering_gate_transport_grpc.hpp b/irohad/ordering/impl/ordering_gate_transport_grpc.hpp index 2f10045484..40fa9514dd 100644 --- a/irohad/ordering/impl/ordering_gate_transport_grpc.hpp +++ b/irohad/ordering/impl/ordering_gate_transport_grpc.hpp @@ -19,11 +19,13 @@ #include +#include "backend/protobuf/proto_proposal_factory.hpp" #include "interfaces/iroha_internal/transaction_batch.hpp" #include "logger/logger.hpp" #include "network/impl/async_grpc_client.hpp" #include "network/ordering_gate_transport.hpp" #include "ordering.grpc.pb.h" +#include "validators/default_validator.hpp" namespace shared_model { namespace interface { @@ -57,6 +59,9 @@ namespace iroha { private: std::weak_ptr subscriber_; std::unique_ptr client_; + std::unique_ptr> + factory_; }; } // namespace ordering diff --git a/irohad/ordering/impl/ordering_service_impl.cpp b/irohad/ordering/impl/ordering_service_impl.cpp index bd2ade255b..15938d369d 100644 --- a/irohad/ordering/impl/ordering_service_impl.cpp +++ b/irohad/ordering/impl/ordering_service_impl.cpp @@ -8,10 +8,10 @@ #include #include +#include + #include "ametsuchi/ordering_service_persistent_state.hpp" #include "ametsuchi/peer_query.hpp" -#include "backend/protobuf/proposal.hpp" -#include "backend/protobuf/transaction.hpp" #include "datetime/time.hpp" #include "network/ordering_service_transport.hpp" @@ -24,14 +24,15 @@ namespace iroha { std::shared_ptr transport, std::shared_ptr persistent_state, + std::unique_ptr factory, bool is_async) : wsv_(wsv), max_size_(max_size), current_size_(0), transport_(transport), - persistent_state_(persistent_state){ - log_ = logger::log("OrderingServiceImpl"); - + persistent_state_(persistent_state), + factory_(std::move(factory)), + log_(logger::log("OrderingServiceImpl")) { // restore state of ordering service from persistent storage proposal_height_ = persistent_state_->loadProposalHeight().value(); @@ -85,40 +86,38 @@ namespace iroha { void OrderingServiceImpl::generateProposal() { std::lock_guard lock(batch_prop_mutex_); - - // TODO 05/03/2018 andrei IR-1046 Server-side shared model object - // factories with move semantics - iroha::protocol::Proposal proto_proposal; - proto_proposal.set_height(proposal_height_++); - proto_proposal.set_created_time(iroha::time::now()); log_->info("Start proposal generation"); + std::vector> txs; for (std::unique_ptr batch; - static_cast(proto_proposal.transactions_size()) < max_size_ - and queue_.try_pop(batch);) { - std::for_each( - batch->transactions().begin(), - batch->transactions().end(), - [this, &proto_proposal](auto &tx) { - *proto_proposal.add_transactions() = - std::static_pointer_cast(tx) - ->getTransport(); - current_size_--; - }); + txs.size() < max_size_ and queue_.try_pop(batch);) { + auto batch_size = batch->transactions().size(); + txs.insert(std::end(txs), + std::make_move_iterator(std::begin(batch->transactions())), + std::make_move_iterator(std::end(batch->transactions()))); + current_size_ -= batch_size; } - auto proposal = std::make_unique( - std::move(proto_proposal)); - - // Save proposal height to the persistent storage. - // In case of restart it reloads state. - if (persistent_state_->saveProposalHeight(proposal_height_)) { - publishProposal(std::move(proposal)); - } else { - // TODO(@l4l) 23/03/18: publish proposal independent of psql status - // IR-1162 - log_->warn( - "Proposal height cannot be saved. Skipping proposal publish"); - } + auto tx_range = txs | boost::adaptors::indirected; + auto proposal = factory_->createProposal( + proposal_height_++, iroha::time::now(), tx_range); + + proposal.match( + [this](expected::Value< + std::unique_ptr> &v) { + // Save proposal height to the persistent storage. + // In case of restart it reloads state. + if (persistent_state_->saveProposalHeight(proposal_height_)) { + publishProposal(std::move(v.value)); + } else { + // TODO(@l4l) 23/03/18: publish proposal independent of psql + // status IR-1162 + log_->warn( + "Proposal height cannot be saved. Skipping proposal publish"); + } + }, + [this](expected::Error &e) { + log_->warn("Failed to initialize proposal: {}", e.error); + }); } void OrderingServiceImpl::publishProposal( diff --git a/irohad/ordering/impl/ordering_service_impl.hpp b/irohad/ordering/impl/ordering_service_impl.hpp index 10c2fda780..0d55aa3042 100644 --- a/irohad/ordering/impl/ordering_service_impl.hpp +++ b/irohad/ordering/impl/ordering_service_impl.hpp @@ -15,6 +15,7 @@ #include "logger/logger.hpp" #include "network/ordering_service.hpp" #include "ordering.grpc.pb.h" +#include "interfaces/iroha_internal/proposal_factory.hpp" namespace iroha { @@ -41,6 +42,7 @@ namespace iroha { * @param proposal_timeout observable timeout for proposal creation * @param transport receive transactions and publish proposals * @param persistent_state storage for auxiliary information + * @param factory is used to generate proposals * @param is_async whether proposals are generated in a separate thread */ OrderingServiceImpl( @@ -50,6 +52,7 @@ namespace iroha { std::shared_ptr transport, std::shared_ptr persistent_state, + std::unique_ptr factory, bool is_async = true); /** @@ -127,6 +130,8 @@ namespace iroha { /// mutex for events activating std::mutex event_mutex_; + std::unique_ptr factory_; + logger::Logger log_; }; } // namespace ordering diff --git a/irohad/validation/CMakeLists.txt b/irohad/validation/CMakeLists.txt index ce6587ad11..4b2e2b34e5 100644 --- a/irohad/validation/CMakeLists.txt +++ b/irohad/validation/CMakeLists.txt @@ -22,7 +22,6 @@ target_link_libraries(stateful_validator rxcpp shared_model_interfaces logger - shared_model_proto_backend ) add_library(chain_validator diff --git a/irohad/validation/impl/stateful_validator_impl.cpp b/irohad/validation/impl/stateful_validator_impl.cpp index e3cdbd5b9c..d1fabca75c 100644 --- a/irohad/validation/impl/stateful_validator_impl.cpp +++ b/irohad/validation/impl/stateful_validator_impl.cpp @@ -20,13 +20,12 @@ #include #include -#include "builders/protobuf/proposal.hpp" +#include "backend/protobuf/transaction.hpp" #include "common/result.hpp" #include "validation/utils.hpp" namespace iroha { namespace validation { - /** * Forms a readable error string from transaction signatures and account * signatories @@ -234,9 +233,9 @@ namespace iroha { return valid_proto_txs; } - StatefulValidatorImpl::StatefulValidatorImpl() { - log_ = logger::log("SFV"); - } + StatefulValidatorImpl::StatefulValidatorImpl( + std::unique_ptr factory) + : factory_(std::move(factory)), log_(logger::log("SFV")) {} validation::VerifiedProposalAndErrors StatefulValidatorImpl::validate( const shared_model::interface::Proposal &proposal, @@ -247,19 +246,17 @@ namespace iroha { auto transactions_errors_log = validation::TransactionsErrors{}; auto valid_proto_txs = validateTransactions( proposal.transactions(), temporaryWsv, transactions_errors_log, log_); - auto validated_proposal = shared_model::proto::ProposalBuilder() - .createdTime(proposal.createdTime()) - .height(proposal.height()) - .transactions(valid_proto_txs) - .createdTime(proposal.createdTime()) - .build(); + + // Since proposal came from ordering gate it was already validated. + // All transactions has been validated as well + // This allows for unsafe construction of proposal + auto validated_proposal = factory_->unsafeCreateProposal( + proposal.height(), proposal.createdTime(), valid_proto_txs); log_->info("transactions in verified proposal: {}", - validated_proposal.transactions().size()); - return std::make_pair(std::make_shared( - validated_proposal.getTransport()), + validated_proposal->transactions().size()); + return std::make_pair(std::move(validated_proposal), transactions_errors_log); } - } // namespace validation } // namespace iroha diff --git a/irohad/validation/impl/stateful_validator_impl.hpp b/irohad/validation/impl/stateful_validator_impl.hpp index 0fc9c0f4d2..0cc36d07eb 100644 --- a/irohad/validation/impl/stateful_validator_impl.hpp +++ b/irohad/validation/impl/stateful_validator_impl.hpp @@ -17,6 +17,7 @@ #ifndef IROHA_STATEFUL_VALIDATIOR_IMPL_HPP #define IROHA_STATEFUL_VALIDATIOR_IMPL_HPP +#include "interfaces/iroha_internal/unsafe_proposal_factory.hpp" #include "validation/stateful_validator.hpp" #include "logger/logger.hpp" @@ -29,12 +30,15 @@ namespace iroha { */ class StatefulValidatorImpl : public StatefulValidator { public: - StatefulValidatorImpl(); + explicit StatefulValidatorImpl( + std::unique_ptr + factory); VerifiedProposalAndErrors validate( const shared_model::interface::Proposal &proposal, ametsuchi::TemporaryWsv &temporaryWsv) override; + std::unique_ptr factory_; logger::Logger log_; }; diff --git a/shared_model/backend/protobuf/proto_proposal_factory.hpp b/shared_model/backend/protobuf/proto_proposal_factory.hpp new file mode 100644 index 0000000000..84047b232c --- /dev/null +++ b/shared_model/backend/protobuf/proto_proposal_factory.hpp @@ -0,0 +1,82 @@ +/** + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef IROHA_PROTO_PROPOSAL_FACTORY_HPP +#define IROHA_PROTO_PROPOSAL_FACTORY_HPP + +#include "backend/protobuf/proposal.hpp" +#include "interfaces/iroha_internal/proposal_factory.hpp" +#include "interfaces/iroha_internal/unsafe_proposal_factory.hpp" +#include "proposal.pb.h" + +namespace shared_model { + namespace proto { + template + class ProtoProposalFactory : public interface::ProposalFactory, + public interface::UnsafeProposalFactory { + public: + FactoryResult> createProposal( + interface::types::HeightType height, + interface::types::TimestampType created_time, + const interface::types::TransactionsCollectionType &transactions) + override { + return createProposal( + createProtoProposal(height, created_time, transactions)); + } + + std::unique_ptr unsafeCreateProposal( + interface::types::HeightType height, + interface::types::TimestampType created_time, + const interface::types::TransactionsCollectionType &transactions) + override { + return std::make_unique( + createProtoProposal(height, created_time, transactions)); + } + + /** + * Create and validate proposal using protobuf object + */ + FactoryResult> createProposal( + const iroha::protocol::Proposal &proposal) { + return validate(std::make_unique(proposal)); + } + + private: + iroha::protocol::Proposal createProtoProposal( + interface::types::HeightType height, + interface::types::TimestampType created_time, + const interface::types::TransactionsCollectionType &transactions) { + iroha::protocol::Proposal proposal; + + proposal.set_height(height); + proposal.set_created_time(created_time); + + for (const auto &tx : transactions) { + *proposal.add_transactions() = + static_cast(tx) + .getTransport(); + } + + return proposal; + } + + FactoryResult> validate( + std::unique_ptr proposal) { + auto errors = validator_.validate(*proposal); + + if (errors) { + return iroha::expected::makeError(errors.reason()); + } + + return iroha::expected::makeValue>( + std::move(proposal)); + } + + Validator validator_; + }; + } // namespace proto +} // namespace shared_model + +#endif // IROHA_PROTO_PROPOSAL_FACTORY_HPP diff --git a/shared_model/interfaces/iroha_internal/proposal_factory.hpp b/shared_model/interfaces/iroha_internal/proposal_factory.hpp new file mode 100644 index 0000000000..fc62eb34ba --- /dev/null +++ b/shared_model/interfaces/iroha_internal/proposal_factory.hpp @@ -0,0 +1,36 @@ +/** + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef IROHA_PROPOSAL_FACTORY_HPP +#define IROHA_PROPOSAL_FACTORY_HPP + +#include + +#include "common/result.hpp" +#include "interfaces/common_objects/types.hpp" + +namespace shared_model { + namespace interface { + class Proposal; + + /** + * ProposalFactory creates proposal + */ + class ProposalFactory { + public: + template + using FactoryResult = iroha::expected::Result; + + virtual FactoryResult> createProposal( + types::HeightType height, + types::TimestampType created_time, + const types::TransactionsCollectionType &transactions) = 0; + + virtual ~ProposalFactory() = default; + }; + } // namespace interface +} // namespace shared_model + +#endif // IROHA_PROPOSAL_FACTORY_HPP diff --git a/shared_model/interfaces/iroha_internal/unsafe_proposal_factory.hpp b/shared_model/interfaces/iroha_internal/unsafe_proposal_factory.hpp new file mode 100644 index 0000000000..fad05f0007 --- /dev/null +++ b/shared_model/interfaces/iroha_internal/unsafe_proposal_factory.hpp @@ -0,0 +1,32 @@ +/** + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef IROHA_UNSAFE_PROPOSAL_FACTORY_HPP +#define IROHA_UNSAFE_PROPOSAL_FACTORY_HPP + +#include + +#include "interfaces/common_objects/types.hpp" + +namespace shared_model { + namespace interface { + class Proposal; + + /** + * UnsafeProposalFactory creates proposal without stateless validation + */ + class UnsafeProposalFactory { + public: + virtual std::unique_ptr unsafeCreateProposal( + types::HeightType height, + types::TimestampType created_time, + const types::TransactionsCollectionType &transactions) = 0; + + virtual ~UnsafeProposalFactory() = default; + }; + } // namespace interface +} // namespace shared_model + +#endif // IROHA_UNSAFE_PROPOSAL_FACTORY_HPP diff --git a/test/integration/transport/ordering_gate_service_test.cpp b/test/integration/transport/ordering_gate_service_test.cpp index e9da39d719..ad3c7fe073 100644 --- a/test/integration/transport/ordering_gate_service_test.cpp +++ b/test/integration/transport/ordering_gate_service_test.cpp @@ -17,6 +17,7 @@ #include +#include "backend/protobuf/proto_proposal_factory.hpp" #include "builders/protobuf/common_objects/proto_peer_builder.hpp" #include "builders/protobuf/transaction.hpp" #include "framework/test_subscriber.hpp" @@ -44,6 +45,8 @@ using wPeer = std::shared_ptr; class OrderingGateServiceTest : public ::testing::Test { public: OrderingGateServiceTest() { + factory_ = std::make_unique>(); pcs_ = std::make_shared(); EXPECT_CALL(*pcs_, on_commit()) .WillRepeatedly(Return(commit_subject_.get_observable())); @@ -64,7 +67,8 @@ class OrderingGateServiceTest : public ::testing::Test { max_proposal, proposal_timeout.get_observable(), service_transport, - fake_persistent_state); + fake_persistent_state, + std::move(factory_)); service_transport->subscribe(service); } @@ -190,6 +194,8 @@ class OrderingGateServiceTest : public ::testing::Test { std::shared_ptr gate_transport; std::shared_ptr service_transport; + std::unique_ptr factory_; + const std::string kAddress = "127.0.0.1"; }; diff --git a/test/module/irohad/ordering/CMakeLists.txt b/test/module/irohad/ordering/CMakeLists.txt index 6193e3b988..44b5b51bb1 100644 --- a/test/module/irohad/ordering/CMakeLists.txt +++ b/test/module/irohad/ordering/CMakeLists.txt @@ -16,6 +16,7 @@ addtest(ordering_service_test ordering_service_test.cpp) target_link_libraries(ordering_service_test ordering_service shared_model_stateless_validation + shared_model_proto_backend ) addtest(ordering_gate_test ordering_gate_test.cpp) diff --git a/test/module/irohad/ordering/ordering_service_test.cpp b/test/module/irohad/ordering/ordering_service_test.cpp index 9bb16e4ffe..ed9e5aa224 100644 --- a/test/module/irohad/ordering/ordering_service_test.cpp +++ b/test/module/irohad/ordering/ordering_service_test.cpp @@ -6,6 +6,7 @@ #include #include "backend/protobuf/common_objects/peer.hpp" +#include "backend/protobuf/proto_proposal_factory.hpp" #include "builders/protobuf/common_objects/proto_peer_builder.hpp" #include "framework/batch_helper.hpp" #include "interfaces/iroha_internal/transaction_batch.hpp" @@ -65,6 +66,8 @@ class OrderingServiceTest : public ::testing::Test { fake_transport = std::make_shared(); fake_persistent_state = std::make_shared(); + factory = std::make_unique>(); } auto initOs(size_t max_proposal) { @@ -74,6 +77,7 @@ class OrderingServiceTest : public ::testing::Test { proposal_timeout.get_observable(), fake_transport, fake_persistent_state, + std::move(factory), false); } @@ -88,6 +92,7 @@ class OrderingServiceTest : public ::testing::Test { std::string address{"0.0.0.0:50051"}; std::shared_ptr peer; std::shared_ptr wsv; + std::unique_ptr factory; rxcpp::subjects::subject proposal_timeout; }; @@ -271,6 +276,7 @@ TEST_F(OrderingServiceTest, GenerateProposalDestructor) { rxcpp::observe_on_new_thread()), fake_transport, fake_persistent_state, + std::move(factory), true); auto on_tx = [&]() { diff --git a/test/module/irohad/validation/CMakeLists.txt b/test/module/irohad/validation/CMakeLists.txt index e556b73057..4c927941a9 100644 --- a/test/module/irohad/validation/CMakeLists.txt +++ b/test/module/irohad/validation/CMakeLists.txt @@ -11,4 +11,5 @@ addtest(stateful_validator_test stateful_validator_test.cpp) target_link_libraries(stateful_validator_test stateful_validator shared_model_default_builders + shared_model_proto_backend ) diff --git a/test/module/irohad/validation/stateful_validator_test.cpp b/test/module/irohad/validation/stateful_validator_test.cpp index 2f0cf995a1..63c9713c54 100644 --- a/test/module/irohad/validation/stateful_validator_test.cpp +++ b/test/module/irohad/validation/stateful_validator_test.cpp @@ -18,6 +18,7 @@ #include "validation/impl/stateful_validator_impl.hpp" #include "validation/stateful_validator.hpp" #include "validation/utils.hpp" +#include "backend/protobuf/proto_proposal_factory.hpp" using namespace iroha::validation; using namespace shared_model::crypto; @@ -97,7 +98,9 @@ TEST_F(SignaturesSubset, PublickeyUniqueness) { class Validator : public testing::Test { public: void SetUp() override { - sfv = std::make_shared(); + factory = std::make_unique>(); + sfv = std::make_shared(std::move(factory)); temp_wsv_mock = std::make_shared(); } @@ -130,6 +133,7 @@ class Validator : public testing::Test { } std::shared_ptr sfv; + std::unique_ptr factory; std::shared_ptr temp_wsv_mock; }; diff --git a/test/module/shared_model/backend_proto/CMakeLists.txt b/test/module/shared_model/backend_proto/CMakeLists.txt index d06d9e80da..c06c3dac7a 100644 --- a/test/module/shared_model/backend_proto/CMakeLists.txt +++ b/test/module/shared_model/backend_proto/CMakeLists.txt @@ -94,5 +94,13 @@ addtest(proto_common_objects_factory_test target_link_libraries(proto_common_objects_factory_test shared_model_cryptography shared_model_stateless_validation - schema + ) + +addtest(proto_proposal_factory_test + proto_proposal_factory_test.cpp + ) + +target_link_libraries(proto_proposal_factory_test + shared_model_stateless_validation + shared_model_proto_backend ) diff --git a/test/module/shared_model/backend_proto/common_objects/proto_common_objects_factory_test.cpp b/test/module/shared_model/backend_proto/common_objects/proto_common_objects_factory_test.cpp index e3a76e3f6b..843d056c0f 100644 --- a/test/module/shared_model/backend_proto/common_objects/proto_common_objects_factory_test.cpp +++ b/test/module/shared_model/backend_proto/common_objects/proto_common_objects_factory_test.cpp @@ -6,7 +6,6 @@ #include #include "backend/protobuf/common_objects/proto_common_objects_factory.hpp" -#include "builders/default_builders.hpp" #include "cryptography/crypto_provider/crypto_defaults.hpp" #include "framework/result_fixture.hpp" #include "validators/field_validator.hpp" diff --git a/test/module/shared_model/backend_proto/proto_proposal_factory_test.cpp b/test/module/shared_model/backend_proto/proto_proposal_factory_test.cpp new file mode 100644 index 0000000000..bb00bfc828 --- /dev/null +++ b/test/module/shared_model/backend_proto/proto_proposal_factory_test.cpp @@ -0,0 +1,74 @@ +/** + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#include "backend/protobuf/proto_proposal_factory.hpp" +#include "framework/result_fixture.hpp" +#include "module/shared_model/validators/validators.hpp" +#include "validators/default_validator.hpp" +#include "validators/field_validator.hpp" + +using namespace shared_model; +using namespace framework::expected; + +class ProposalFactoryTest : public ::testing::Test { + public: + shared_model::proto::ProtoProposalFactory< + validation::AlwaysValidValidator> + valid_factory; + shared_model::proto::ProtoProposalFactory< + validation::DefaultProposalValidator> + factory; + + interface::types::HeightType height{1}; + iroha::time::time_t time{iroha::time::now()}; + + std::vector txs; + + void SetUp() override { + txs.emplace_back(iroha::protocol::Transaction{}); + } + + void TearDown() override { + txs.clear(); + } +}; + + +/** + * @given proposal factory and valid data + * @when proposal is created using factory + * @then proposal is successfully created + */ +TEST_F(ProposalFactoryTest, ValidProposalTest) { + std::vector txs; + iroha::protocol::Transaction proto_tx; + txs.emplace_back(proto_tx); + auto proposal = valid_factory.createProposal(height, time, txs); + + proposal.match( + [&](const ValueOf &v) { + ASSERT_EQ(txs, v.value->transactions()); + ASSERT_EQ(height, v.value->height()); + ASSERT_EQ(time, v.value->createdTime()); + }, + [](const ErrorOf &e) { FAIL() << e.error; }); +} + +/** + * @given proposal factory and invalid data (empty transaction) + * @when proposal is created using factory + * @then proposal is not created successfully + */ +TEST_F(ProposalFactoryTest, InvalidProposalTest) { + auto proposal = factory.createProposal(height, time, txs); + + proposal.match( + [&](const ValueOf &) { + FAIL() << "unexpected value case"; + }, + [](const ErrorOf &) { SUCCEED(); }); +} diff --git a/test/module/shared_model/validators/validators.hpp b/test/module/shared_model/validators/validators.hpp index 3ceefbca90..3d83e35d0f 100644 --- a/test/module/shared_model/validators/validators.hpp +++ b/test/module/shared_model/validators/validators.hpp @@ -18,6 +18,8 @@ #ifndef IROHA_VALIDATOR_MOCKS_HPP #define IROHA_VALIDATOR_MOCKS_HPP +#include "validators/answer.hpp" + namespace shared_model { namespace validation { From 6b4cacc08ebe586de181d31859d5bc2584637e08 Mon Sep 17 00:00:00 2001 From: Igor Egorov Date: Mon, 30 Jul 2018 12:40:47 +0300 Subject: [PATCH 90/97] Fix ToriiService Streaming Full Pipeline Test (#1615) The test (StreamingFullPipelineTest) was failing on slow machines. Investigation showed that the root cause was the way thread scheduler works, so the code was generally valid. Streaming client resubscription is a fully valid use case of streaming service. The client can resubscribe to the stream if the previous stream was ended unexpectedly for the client. Unexpectedly means that client requests the hash of a transaction that was definitely sent to Iroha and the stream reaches its end with one of the following statuses: `NOT_RECEIVED`, `STATELESS_VALIDATION_SUCCESS`, `STATEFUL_VALIDATION_SUCCESS`. Before the fix test client was not able to resubscribe to the stream. Now it can. ```diff cmake -H. -Bbuild cd build make torii_service_test while test_bin/torii_service_test --gtest_filter=*ull* ; do :; done +this loop should not be broken up by ASSERT_EQ(last_status, COMMITTED) error message ``` Signed-off-by: Igor Egorov --- test/module/irohad/torii/torii_service_test.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/test/module/irohad/torii/torii_service_test.cpp b/test/module/irohad/torii/torii_service_test.cpp index eda4d68ff1..7af07249d6 100644 --- a/test/module/irohad/torii/torii_service_test.cpp +++ b/test/module/irohad/torii/torii_service_test.cpp @@ -34,6 +34,7 @@ using namespace iroha::torii; using namespace std::chrono_literals; constexpr std::chrono::milliseconds initial_timeout = 1s; constexpr std::chrono::milliseconds nonfinal_timeout = 2 * 10s; +constexpr unsigned resubscribe_attempts = 3; using iroha::Commit; @@ -408,9 +409,14 @@ TEST_F(ToriiServiceTest, StreamingFullPipelineTest) { // (Committed in this case) will be received. We start request before // transaction sending so we need in a separate thread for it. std::thread t([&] { + unsigned resub_counter(resubscribe_attempts); iroha::protocol::TxStatusRequest tx_request; tx_request.set_tx_hash(txhash); - client.StatusStream(tx_request, torii_response); + do { + client.StatusStream(tx_request, torii_response); + } while (torii_response.back().tx_status() + != iroha::protocol::TxStatus::COMMITTED + and --resub_counter); }); client.Torii(iroha_tx.getTransport()); From 433fc8a07b701f13f7245faa5f881b3e6568a177 Mon Sep 17 00:00:00 2001 From: Victor Drobny Date: Mon, 30 Jul 2018 14:18:58 +0300 Subject: [PATCH 91/97] Feature/remove observables block query (#1591) * remove observables in getBlocks and getTransaction methods Signed-off-by: Victor Drobny --- irohad/ametsuchi/block_query.hpp | 12 +- .../ametsuchi/impl/postgres_block_query.cpp | 171 ++++++++---------- .../ametsuchi/impl/postgres_block_query.hpp | 14 +- irohad/ametsuchi/impl/wsv_restorer_impl.cpp | 5 +- .../execution/impl/query_execution_impl.cpp | 24 ++- irohad/network/impl/block_loader_service.cpp | 28 ++- .../irohad/ametsuchi/ametsuchi_mocks.hpp | 12 +- .../irohad/ametsuchi/ametsuchi_test.cpp | 68 +++---- .../irohad/ametsuchi/block_query_test.cpp | 150 ++++++--------- .../ametsuchi/block_query_transfer_test.cpp | 36 ++-- .../irohad/execution/query_execution_test.cpp | 48 +++-- .../irohad/network/block_loader_test.cpp | 10 +- .../irohad/torii/torii_queries_test.cpp | 21 +-- 13 files changed, 259 insertions(+), 340 deletions(-) diff --git a/irohad/ametsuchi/block_query.hpp b/irohad/ametsuchi/block_query.hpp index 87ca9329d3..70e8fe0ba0 100644 --- a/irohad/ametsuchi/block_query.hpp +++ b/irohad/ametsuchi/block_query.hpp @@ -46,7 +46,7 @@ namespace iroha { * @param account_id - account_id (accountName@domainName) * @return observable of Model Transaction */ - virtual rxcpp::observable getAccountTransactions( + virtual std::vector getAccountTransactions( const shared_model::interface::types::AccountIdType &account_id) = 0; /** @@ -55,7 +55,7 @@ namespace iroha { * @param asset_id - asset_id (assetName#domainName) * @return observable of Model Transaction */ - virtual rxcpp::observable getAccountAssetTransactions( + virtual std::vector getAccountAssetTransactions( const shared_model::interface::types::AccountIdType &account_id, const shared_model::interface::types::AssetIdType &asset_id) = 0; @@ -64,7 +64,7 @@ namespace iroha { * @param tx_hashes - transactions' hashes to retrieve * @return observable of Model Transaction */ - virtual rxcpp::observable> getTransactions( + virtual std::vector> getTransactions( const std::vector &tx_hashes) = 0; /** @@ -73,7 +73,7 @@ namespace iroha { * @param count - number of blocks to retrieve * @return observable of Model Block */ - virtual rxcpp::observable getBlocks( + virtual std::vector getBlocks( shared_model::interface::types::HeightType height, uint32_t count) = 0; @@ -82,7 +82,7 @@ namespace iroha { * @param from - starting height * @return observable of Model Block */ - virtual rxcpp::observable getBlocksFrom( + virtual std::vector getBlocksFrom( shared_model::interface::types::HeightType height) = 0; /** @@ -90,7 +90,7 @@ namespace iroha { * @param count - number of blocks to retrieve * @return observable of Model Block */ - virtual rxcpp::observable getTopBlocks(uint32_t count) = 0; + virtual std::vector getTopBlocks(uint32_t count) = 0; /** * Get height of the top block. diff --git a/irohad/ametsuchi/impl/postgres_block_query.cpp b/irohad/ametsuchi/impl/postgres_block_query.cpp index 50569d631e..fb6682f096 100644 --- a/irohad/ametsuchi/impl/postgres_block_query.cpp +++ b/irohad/ametsuchi/impl/postgres_block_query.cpp @@ -19,37 +19,33 @@ namespace iroha { block_store_(file_store), log_(logger::log("PostgresBlockIndex")) {} - rxcpp::observable PostgresBlockQuery::getBlocks( + std::vector PostgresBlockQuery::getBlocks( shared_model::interface::types::HeightType height, uint32_t count) { shared_model::interface::types::HeightType last_id = block_store_.last_id(); auto to = std::min(last_id, height + count - 1); + std::vector result; if (height > to or count == 0) { - return rxcpp::observable<>::empty(); + return result; } - return rxcpp::observable<>::range(height, to) - .flat_map([this](const auto &i) { - return rxcpp::observable<>::create( - [i, this](const auto &block_subscriber) { - block_store_.get(i) | [](const auto &bytes) { - return shared_model::converters::protobuf::jsonToModel< - shared_model::proto::Block>(bytesToString(bytes)); - } | [&block_subscriber](auto &&block) { - block_subscriber.on_next( - std::make_shared( - std::move(block))); - }; - block_subscriber.on_completed(); - }); - }); + for (auto i = height; i <= to; i++) { + block_store_.get(i) | [](const auto &bytes) { + return shared_model::converters::protobuf::jsonToModel< + shared_model::proto::Block>(bytesToString(bytes)); + } | [&result](auto &&block) { + result.push_back( + std::make_shared(std::move(block))); + }; + } + return result; } - rxcpp::observable PostgresBlockQuery::getBlocksFrom( + std::vector PostgresBlockQuery::getBlocksFrom( shared_model::interface::types::HeightType height) { return getBlocks(height, block_store_.last_id()); } - rxcpp::observable PostgresBlockQuery::getTopBlocks( + std::vector PostgresBlockQuery::getTopBlocks( uint32_t count) { auto last_id = block_store_.last_id(); count = std::min(count, last_id); @@ -92,9 +88,9 @@ namespace iroha { } std::function &result)> - PostgresBlockQuery::callback( - const rxcpp::subscriber &subscriber, uint64_t block_id) { - return [this, &subscriber, block_id](std::vector &result) { + PostgresBlockQuery::callback(std::vector &blocks, + uint64_t block_id) { + return [this, &blocks, block_id](std::vector &result) { auto block = block_store_.get(block_id) | [](const auto &bytes) { return shared_model::converters::protobuf::jsonToModel< shared_model::proto::Block>(bytesToString(bytes)); @@ -112,93 +108,82 @@ namespace iroha { return size; }), [&](const auto &x) { - subscriber.on_next(PostgresBlockQuery::wTransaction( + blocks.push_back(PostgresBlockQuery::wTransaction( clone(block->transactions()[x]))); }); }; } - rxcpp::observable + std::vector PostgresBlockQuery::getAccountTransactions( const shared_model::interface::types::AccountIdType &account_id) { - return rxcpp::observable<>::create( - [this, account_id](const auto &subscriber) { - auto block_ids = this->getBlockIds(account_id); - if (block_ids.empty()) { - subscriber.on_completed(); - return; - } - - for (const auto &block_id : block_ids) { - std::vector index; - soci::indicator ind; - std::string row; - soci::statement st = - (sql_.prepare - << "SELECT DISTINCT index FROM index_by_creator_height " - "WHERE creator_id = :id AND height = :height", - soci::into(row, ind), - soci::use(account_id), - soci::use(block_id)); - st.execute(); - - processSoci(st, ind, row, [&index](std::string &r) { - index.push_back(r); - }); - this->callback(subscriber, block_id)(index); - } - subscriber.on_completed(); - }); + std::vector result; + auto block_ids = this->getBlockIds(account_id); + if (block_ids.empty()) { + return result; + } + for (const auto &block_id : block_ids) { + std::vector index; + soci::indicator ind; + std::string row; + soci::statement st = + (sql_.prepare + << "SELECT DISTINCT index FROM index_by_creator_height " + "WHERE creator_id = :id AND height = :height", + soci::into(row, ind), + soci::use(account_id), + soci::use(block_id)); + st.execute(); + + processSoci( + st, ind, row, [&index](std::string &r) { index.push_back(r); }); + this->callback(result, block_id)(index); + } + return result; } - rxcpp::observable + std::vector PostgresBlockQuery::getAccountAssetTransactions( const shared_model::interface::types::AccountIdType &account_id, const shared_model::interface::types::AssetIdType &asset_id) { - return rxcpp::observable<>::create( - [this, account_id, asset_id](auto subscriber) { - auto block_ids = this->getBlockIds(account_id); - if (block_ids.empty()) { - subscriber.on_completed(); - return; - } - - for (const auto &block_id : block_ids) { - std::vector index; - soci::indicator ind; - std::string row; - soci::statement st = - (sql_.prepare - << "SELECT DISTINCT index FROM index_by_id_height_asset " - "WHERE id = :id AND height = :height AND asset_id = " - ":asset_id", - soci::into(row, ind), - soci::use(account_id), - soci::use(block_id), - soci::use(asset_id)); - st.execute(); - - processSoci(st, ind, row, [&index](std::string &r) { - index.push_back(r); - }); - this->callback(subscriber, block_id)(index); - } - subscriber.on_completed(); - }); + std::vector result; + auto block_ids = this->getBlockIds(account_id); + if (block_ids.empty()) { + return result; + } + + for (const auto &block_id : block_ids) { + std::vector index; + soci::indicator ind; + std::string row; + soci::statement st = + (sql_.prepare + << "SELECT DISTINCT index FROM index_by_id_height_asset " + "WHERE id = :id AND height = :height AND asset_id = " + ":asset_id", + soci::into(row, ind), + soci::use(account_id), + soci::use(block_id), + soci::use(asset_id)); + st.execute(); + + processSoci( + st, ind, row, [&index](std::string &r) { index.push_back(r); }); + this->callback(result, block_id)(index); + } + return result; } - rxcpp::observable> + std::vector> PostgresBlockQuery::getTransactions( const std::vector &tx_hashes) { - return rxcpp::observable<>::create>( - [this, tx_hashes](const auto &subscriber) { - std::for_each(tx_hashes.begin(), - tx_hashes.end(), - [that = this, &subscriber](const auto &tx_hash) { - subscriber.on_next(that->getTxByHashSync(tx_hash)); - }); - subscriber.on_completed(); - }); + std::vector> result; + std::for_each(tx_hashes.begin(), + tx_hashes.end(), + [this, &result](const auto &tx_hash) { + result.push_back(this->getTxByHashSync(tx_hash)); + }); + return result; } boost::optional diff --git a/irohad/ametsuchi/impl/postgres_block_query.hpp b/irohad/ametsuchi/impl/postgres_block_query.hpp index 1f37435dd9..f6efb53a50 100644 --- a/irohad/ametsuchi/impl/postgres_block_query.hpp +++ b/irohad/ametsuchi/impl/postgres_block_query.hpp @@ -26,28 +26,28 @@ namespace iroha { explicit PostgresBlockQuery(soci::session &sql, KeyValueStorage &file_store); - rxcpp::observable getAccountTransactions( + std::vector getAccountTransactions( const shared_model::interface::types::AccountIdType &account_id) override; - rxcpp::observable getAccountAssetTransactions( + std::vector getAccountAssetTransactions( const shared_model::interface::types::AccountIdType &account_id, const shared_model::interface::types::AssetIdType &asset_id) override; - rxcpp::observable> getTransactions( + std::vector> getTransactions( const std::vector &tx_hashes) override; boost::optional getTxByHashSync( const shared_model::crypto::Hash &hash) override; - rxcpp::observable getBlocks( + std::vector getBlocks( shared_model::interface::types::HeightType height, uint32_t count) override; - rxcpp::observable getBlocksFrom( + std::vector getBlocksFrom( shared_model::interface::types::HeightType height) override; - rxcpp::observable getTopBlocks(uint32_t count) override; + std::vector getTopBlocks(uint32_t count) override; uint32_t getTopBlockHeight() override; @@ -80,7 +80,7 @@ namespace iroha { * @return */ std::function &result)> callback( - const rxcpp::subscriber &s, uint64_t block_id); + std::vector &s, uint64_t block_id); soci::session &sql_; diff --git a/irohad/ametsuchi/impl/wsv_restorer_impl.cpp b/irohad/ametsuchi/impl/wsv_restorer_impl.cpp index 1f7ebe6b8a..3b30d9fac8 100644 --- a/irohad/ametsuchi/impl/wsv_restorer_impl.cpp +++ b/irohad/ametsuchi/impl/wsv_restorer_impl.cpp @@ -28,9 +28,8 @@ namespace iroha { expected::Result WsvRestorerImpl::restoreWsv( Storage &storage) { // get all blocks starting from the genesis - std::vector> blocks; - storage.getBlockQuery()->getBlocksFrom(1).as_blocking().subscribe( - [&blocks](auto block) { blocks.push_back(std::move(block)); }); + std::vector> blocks= + storage.getBlockQuery()->getBlocksFrom(1); storage.reset(); diff --git a/irohad/execution/impl/query_execution_impl.cpp b/irohad/execution/impl/query_execution_impl.cpp index ef0991e81e..54eea0b3b3 100644 --- a/irohad/execution/impl/query_execution_impl.cpp +++ b/irohad/execution/impl/query_execution_impl.cpp @@ -290,10 +290,13 @@ QueryExecutionImpl::executeGetAccountAssetTransactions( bq.getAccountAssetTransactions(query.accountId(), query.assetId()); std::vector txs; - acc_asset_tx.subscribe([&](const auto &tx) { - txs.push_back( - *std::static_pointer_cast(tx)); - }); + std::transform( + acc_asset_tx.begin(), + acc_asset_tx.end(), + std::back_inserter(txs), + [](const auto &tx) { + return *std::static_pointer_cast(tx); + }); auto response = QueryResponseBuilder().transactionsResponse(txs); return response; @@ -307,10 +310,13 @@ QueryExecutionImpl::executeGetAccountTransactions( auto acc_tx = bq.getAccountTransactions(query.accountId()); std::vector txs; - acc_tx.subscribe([&](const auto &tx) { - txs.push_back( - *std::static_pointer_cast(tx)); - }); + std::transform( + acc_tx.begin(), + acc_tx.end(), + std::back_inserter(txs), + [](const auto &tx) { + return *std::static_pointer_cast(tx); + }); auto response = QueryResponseBuilder().transactionsResponse(txs); return response; @@ -329,7 +335,7 @@ QueryExecutionImpl::executeGetTransactions( std::vector txs; bool can_get_all = checkAccountRolePermission(accountId, wq, Role::kGetAllTxs); - transactions.subscribe([&](const auto &tx) { + std::for_each(transactions.begin(), transactions.end(), [&](const auto &tx) { if (tx) { auto proto_tx = *std::static_pointer_cast(*tx); diff --git a/irohad/network/impl/block_loader_service.cpp b/irohad/network/impl/block_loader_service.cpp index a0afac026d..2f93a64130 100644 --- a/irohad/network/impl/block_loader_service.cpp +++ b/irohad/network/impl/block_loader_service.cpp @@ -31,13 +31,11 @@ grpc::Status BlockLoaderService::retrieveBlocks( ::grpc::ServerContext *context, const proto::BlocksRequest *request, ::grpc::ServerWriter<::iroha::protocol::Block> *writer) { - storage_->getBlocksFrom(request->height()) - .map([](auto block) { - return std::dynamic_pointer_cast(block) - ->getTransport(); - }) - .as_blocking() - .subscribe([writer](auto block) { writer->Write(block); }); + auto blocks = storage_->getBlocksFrom(request->height()); + std::for_each(blocks.begin(), blocks.end(), [&writer](const auto &block) { + writer->Write(std::dynamic_pointer_cast(block) + ->getTransport()); + }); return grpc::Status::OK; } @@ -53,14 +51,14 @@ grpc::Status BlockLoaderService::retrieveBlock( } boost::optional result; - storage_->getBlocksFrom(1) - .filter([&hash](auto block) { return block->hash() == hash; }) - .map([](auto block) { - return std::dynamic_pointer_cast(block) - ->getTransport(); - }) - .as_blocking() - .subscribe([&result](auto block) { result = block; }); + auto blocks = storage_->getBlocksFrom(1); + std::for_each( + blocks.begin(), blocks.end(), [&result, &hash](const auto &block) { + if (block->hash() == hash) { + result = std::dynamic_pointer_cast(block) + ->getTransport(); + } + }); if (not result) { log_->info("Cannot find block with requested hash"); return grpc::Status(grpc::StatusCode::NOT_FOUND, "Block not found"); diff --git a/test/module/irohad/ametsuchi/ametsuchi_mocks.hpp b/test/module/irohad/ametsuchi/ametsuchi_mocks.hpp index b614a36d3a..49456315cc 100644 --- a/test/module/irohad/ametsuchi/ametsuchi_mocks.hpp +++ b/test/module/irohad/ametsuchi/ametsuchi_mocks.hpp @@ -156,27 +156,27 @@ namespace iroha { public: MOCK_METHOD1( getAccountTransactions, - rxcpp::observable( + std::vector( const shared_model::interface::types::AccountIdType &account_id)); MOCK_METHOD1(getTxByHashSync, boost::optional( const shared_model::crypto::Hash &hash)); MOCK_METHOD2( getAccountAssetTransactions, - rxcpp::observable( + std::vector( const shared_model::interface::types::AccountIdType &account_id, const shared_model::interface::types::AssetIdType &asset_id)); MOCK_METHOD1( getTransactions, - rxcpp::observable>( + std::vector>( const std::vector &tx_hashes)); MOCK_METHOD2(getBlocks, - rxcpp::observable( + std::vector( shared_model::interface::types::HeightType, uint32_t)); MOCK_METHOD1(getBlocksFrom, - rxcpp::observable( + std::vector( shared_model::interface::types::HeightType)); - MOCK_METHOD1(getTopBlocks, rxcpp::observable(uint32_t)); + MOCK_METHOD1(getTopBlocks, std::vector(uint32_t)); MOCK_METHOD0(getTopBlock, expected::Result(void)); MOCK_METHOD1(hasTxWithHash, bool(const shared_model::crypto::Hash &hash)); MOCK_METHOD0(getTopBlockHeight, uint32_t(void)); diff --git a/test/module/irohad/ametsuchi/ametsuchi_test.cpp b/test/module/irohad/ametsuchi/ametsuchi_test.cpp index 5b3186c238..ca32c6ee06 100644 --- a/test/module/irohad/ametsuchi/ametsuchi_test.cpp +++ b/test/module/irohad/ametsuchi/ametsuchi_test.cpp @@ -26,25 +26,6 @@ auto zero_string = std::string(32, '0'); auto fake_hash = shared_model::crypto::Hash(zero_string); auto fake_pubkey = shared_model::crypto::PublicKey(zero_string); -/** - * Shortcut to create CallExact observable wrapper, subscribe with given lambda, - * and validate the number of calls with optional custom output - * @tparam O observable type - * @tparam F on_next function type - * @param o observable object - * @param f function object - * @param call_count number of expected calls - * @param msg custom validation failure message - */ -template -void validateCalls(O &&o, - F &&f, - uint64_t call_count, - const std::string &msg = {}) { - auto wrap = make_test_subscriber(std::forward(o), call_count); - wrap.subscribe(std::forward(f)); - ASSERT_TRUE(wrap.validate()) << "Expected " << call_count << " calls" << msg; -} /** * Validate getAccountTransaction with given parameters @@ -59,11 +40,11 @@ void validateAccountTransactions(B &&blocks, const std::string &account, int call_count, int command_count) { - validateCalls( - blocks->getAccountTransactions(account), - [&](const auto &tx) { EXPECT_EQ(tx->commands().size(), command_count); }, - call_count, - " for " + account); + auto txs = blocks->getAccountTransactions(account); + ASSERT_EQ(txs.size(), call_count); + std::for_each(txs.begin(), txs.end(), [&](const auto &tx) { + EXPECT_EQ(tx->commands().size(), command_count); + }); } /** @@ -81,11 +62,11 @@ void validateAccountAssetTransactions(B &&blocks, const std::string &asset, int call_count, int command_count) { - validateCalls( - blocks->getAccountAssetTransactions(account, asset), - [&](const auto &tx) { EXPECT_EQ(tx->commands().size(), command_count); }, - call_count, - " for " + account + " " + asset); + auto txs = blocks->getAccountAssetTransactions(account, asset); + ASSERT_EQ(txs.size(), call_count); + std::for_each(txs.begin(), txs.end(), [&](const auto &tx) { + EXPECT_EQ(tx->commands().size(), command_count); + }); } /** @@ -155,10 +136,7 @@ TEST_F(AmetsuchiTest, GetBlocksCompletedWhenCalled) { apply(storage, block); - auto completed_wrapper = - make_test_subscriber(blocks->getBlocks(1, 1)); - completed_wrapper.subscribe(); - ASSERT_TRUE(completed_wrapper.validate()); + ASSERT_EQ(*blocks->getBlocks(1, 1)[0], block); } TEST_F(AmetsuchiTest, SampleTest) { @@ -214,12 +192,12 @@ TEST_F(AmetsuchiTest, SampleTest) { // Block store tests auto hashes = {block1.hash(), block2.hash()}; - validateCalls(blocks->getBlocks(1, 2), - [i = 0, &hashes](auto eachBlock) mutable { - EXPECT_EQ(*(hashes.begin() + i), eachBlock->hash()); - ++i; - }, - 2); + + auto stored_blocks = blocks->getBlocks(1, 2); + ASSERT_EQ(2, stored_blocks.size()); + for (size_t i = 0; i < stored_blocks.size(); i++) { + EXPECT_EQ(*(hashes.begin() + i), stored_blocks[i]->hash()); + } validateAccountTransactions(blocks, "admin1", 1, 3); validateAccountTransactions(blocks, user1id, 1, 4); @@ -357,12 +335,12 @@ TEST_F(AmetsuchiTest, queryGetAccountAssetTransactionsTest) { // Block store test auto hashes = {block1.hash(), block2.hash(), block3.hash()}; - validateCalls(blocks->getBlocks(1, 3), - [i = 0, &hashes](auto eachBlock) mutable { - EXPECT_EQ(*(hashes.begin() + i), eachBlock->hash()); - ++i; - }, - 3); + + auto stored_blocks = blocks->getBlocks(1, 3); + ASSERT_EQ(3, stored_blocks.size()); + for (size_t i = 0; i < stored_blocks.size(); i++) { + EXPECT_EQ(*(hashes.begin() + i), stored_blocks[i]->hash()); + } validateAccountTransactions(blocks, admin, 1, 7); validateAccountTransactions(blocks, user3id, 0, 0); diff --git a/test/module/irohad/ametsuchi/block_query_test.cpp b/test/module/irohad/ametsuchi/block_query_test.cpp index 12464cf971..45e0feccd1 100644 --- a/test/module/irohad/ametsuchi/block_query_test.cpp +++ b/test/module/irohad/ametsuchi/block_query_test.cpp @@ -20,7 +20,6 @@ #include "ametsuchi/impl/postgres_block_index.hpp" #include "ametsuchi/impl/postgres_block_query.hpp" #include "converters/protobuf/json_proto_converter.hpp" -#include "framework/test_subscriber.hpp" #include "framework/result_fixture.hpp" #include "module/irohad/ametsuchi/ametsuchi_fixture.hpp" #include "module/irohad/ametsuchi/ametsuchi_mocks.hpp" @@ -28,7 +27,6 @@ #include "module/shared_model/builders/protobuf/test_transaction_builder.hpp" using namespace iroha::ametsuchi; -using namespace framework::test_subscriber; using testing::Return; @@ -45,8 +43,7 @@ class BlockQueryTest : public AmetsuchiTest { index = std::make_shared(*sql); blocks = std::make_shared(*sql, *file); - empty_blocks = - std::make_shared(*sql, *mock_file); + empty_blocks = std::make_shared(*sql, *mock_file); *sql << init_; @@ -113,11 +110,11 @@ class BlockQueryTest : public AmetsuchiTest { */ TEST_F(BlockQueryTest, GetAccountTransactionsFromSeveralBlocks) { // Check that creator1 has created 3 transactions - auto getCreator1TxWrapper = make_test_subscriber( - blocks->getAccountTransactions(creator1), 3); - getCreator1TxWrapper.subscribe( - [this](auto val) { EXPECT_EQ(val->creatorAccountId(), creator1); }); - ASSERT_TRUE(getCreator1TxWrapper.validate()); + auto txs = blocks->getAccountTransactions(creator1); + ASSERT_EQ(txs.size(), 3); + std::for_each(txs.begin(), txs.end(), [&](const auto &tx) { + EXPECT_EQ(tx->creatorAccountId(), creator1); + }); } /** @@ -129,11 +126,11 @@ TEST_F(BlockQueryTest, GetAccountTransactionsFromSeveralBlocks) { */ TEST_F(BlockQueryTest, GetAccountTransactionsFromSingleBlock) { // Check that creator1 has created 1 transaction - auto getCreator2TxWrapper = make_test_subscriber( - blocks->getAccountTransactions(creator2), 1); - getCreator2TxWrapper.subscribe( - [this](auto val) { EXPECT_EQ(val->creatorAccountId(), creator2); }); - ASSERT_TRUE(getCreator2TxWrapper.validate()); + auto txs = blocks->getAccountTransactions(creator2); + ASSERT_EQ(txs.size(), 1); + std::for_each(txs.begin(), txs.end(), [&](const auto &tx) { + EXPECT_EQ(tx->creatorAccountId(), creator2); + }); } /** @@ -144,10 +141,8 @@ TEST_F(BlockQueryTest, GetAccountTransactionsFromSingleBlock) { */ TEST_F(BlockQueryTest, GetAccountTransactionsNonExistingUser) { // Check that "nonexisting" user has no transaction - auto getNonexistingTxWrapper = make_test_subscriber( - blocks->getAccountTransactions("nonexisting user"), 0); - getNonexistingTxWrapper.subscribe(); - ASSERT_TRUE(getNonexistingTxWrapper.validate()); + auto txs = blocks->getAccountTransactions("nonexisting user"); + ASSERT_EQ(txs.size(), 0); } /** @@ -158,20 +153,12 @@ TEST_F(BlockQueryTest, GetAccountTransactionsNonExistingUser) { * @then queried transactions */ TEST_F(BlockQueryTest, GetTransactionsExistingTxHashes) { - auto wrapper = make_test_subscriber( - blocks->getTransactions({tx_hashes[1], tx_hashes[3]}), 2); - wrapper.subscribe([this](auto tx) { - static auto subs_cnt = 0; - subs_cnt++; - if (subs_cnt == 1) { - ASSERT_TRUE(tx); - EXPECT_EQ(tx_hashes[1], (*tx)->hash()); - } else { - ASSERT_TRUE(tx); - EXPECT_EQ(tx_hashes[3], (*tx)->hash()); - } - }); - ASSERT_TRUE(wrapper.validate()); + auto txs = blocks->getTransactions({tx_hashes[1], tx_hashes[3]}); + ASSERT_EQ(txs.size(), 2); + ASSERT_TRUE(txs[0]); + ASSERT_TRUE(txs[1]); + ASSERT_EQ(txs[0].get()->hash(), tx_hashes[1]); + ASSERT_EQ(txs[1].get()->hash(), tx_hashes[3]); } /** @@ -185,11 +172,11 @@ TEST_F(BlockQueryTest, GetTransactionsIncludesNonExistingTxHashes) { shared_model::crypto::Hash invalid_tx_hash_1(zero_string), invalid_tx_hash_2(std::string( shared_model::crypto::DefaultCryptoAlgorithmType::kHashLength, '9')); - auto wrapper = make_test_subscriber( - blocks->getTransactions({invalid_tx_hash_1, invalid_tx_hash_2}), 2); - wrapper.subscribe( - [](auto transaction) { EXPECT_EQ(boost::none, transaction); }); - ASSERT_TRUE(wrapper.validate()); + + auto txs = blocks->getTransactions({invalid_tx_hash_1, invalid_tx_hash_2}); + ASSERT_EQ(txs.size(), 2); + ASSERT_FALSE(txs[0]); + ASSERT_FALSE(txs[1]); } /** @@ -201,10 +188,8 @@ TEST_F(BlockQueryTest, GetTransactionsIncludesNonExistingTxHashes) { */ TEST_F(BlockQueryTest, GetTransactionsWithEmpty) { // transactions' hashes are empty. - auto wrapper = - make_test_subscriber(blocks->getTransactions({}), 0); - wrapper.subscribe(); - ASSERT_TRUE(wrapper.validate()); + auto txs = blocks->getTransactions({}); + ASSERT_EQ(txs.size(), 0); } /** @@ -217,19 +202,11 @@ TEST_F(BlockQueryTest, GetTransactionsWithEmpty) { TEST_F(BlockQueryTest, GetTransactionsWithInvalidTxAndValidTx) { // TODO 15/11/17 motxx - Use EqualList VerificationStrategy shared_model::crypto::Hash invalid_tx_hash_1(zero_string); - auto wrapper = make_test_subscriber( - blocks->getTransactions({invalid_tx_hash_1, tx_hashes[0]}), 2); - wrapper.subscribe([this](auto tx) { - static auto subs_cnt = 0; - subs_cnt++; - if (subs_cnt == 1) { - EXPECT_EQ(boost::none, tx); - } else { - EXPECT_TRUE(tx); - EXPECT_EQ(tx_hashes[0], (*tx)->hash()); - } - }); - ASSERT_TRUE(wrapper.validate()); + auto txs = blocks->getTransactions({invalid_tx_hash_1, tx_hashes[0]}); + ASSERT_EQ(txs.size(), 2); + ASSERT_FALSE(txs[0]); + ASSERT_TRUE(txs[1]); + ASSERT_EQ(txs[1].get()->hash(), tx_hashes[0]); } /** @@ -239,9 +216,8 @@ TEST_F(BlockQueryTest, GetTransactionsWithInvalidTxAndValidTx) { * @then nothing is returned */ TEST_F(BlockQueryTest, GetNonExistentBlock) { - auto wrapper = make_test_subscriber(blocks->getBlocks(1000, 1), 0); - wrapper.subscribe(); - ASSERT_TRUE(wrapper.validate()); + auto stored_blocks = blocks->getBlocks(1000, 1); + ASSERT_TRUE(stored_blocks.empty()); } /** @@ -251,9 +227,8 @@ TEST_F(BlockQueryTest, GetNonExistentBlock) { * @then returned exactly 1 block */ TEST_F(BlockQueryTest, GetExactlyOneBlock) { - auto wrapper = make_test_subscriber(blocks->getBlocks(1, 1), 1); - wrapper.subscribe(); - ASSERT_TRUE(wrapper.validate()); + auto stored_blocks = blocks->getBlocks(1, 1); + ASSERT_EQ(stored_blocks.size(), 1); } /** @@ -263,9 +238,8 @@ TEST_F(BlockQueryTest, GetExactlyOneBlock) { * @then no blocks returned */ TEST_F(BlockQueryTest, GetBlocks_Count0) { - auto wrapper = make_test_subscriber(blocks->getBlocks(1, 0), 0); - wrapper.subscribe(); - ASSERT_TRUE(wrapper.validate()); + auto stored_blocks = blocks->getBlocks(1, 0); + ASSERT_TRUE(stored_blocks.empty()); } /** @@ -275,9 +249,8 @@ TEST_F(BlockQueryTest, GetBlocks_Count0) { * @then no blocks returned */ TEST_F(BlockQueryTest, GetZeroBlock) { - auto wrapper = make_test_subscriber(blocks->getBlocks(0, 1), 0); - wrapper.subscribe(); - ASSERT_TRUE(wrapper.validate()); + auto stored_blocks = blocks->getBlocks(0, 1); + ASSERT_TRUE(stored_blocks.empty()); } /** @@ -287,15 +260,13 @@ TEST_F(BlockQueryTest, GetZeroBlock) { * @then returned all blocks (2) */ TEST_F(BlockQueryTest, GetBlocksFrom1) { - auto wrapper = - make_test_subscriber(blocks->getBlocksFrom(1), blocks_total); - size_t counter = 1; - wrapper.subscribe([&counter](const auto &b) { - // wrapper returns blocks 1 and 2 - ASSERT_EQ(b->height(), counter++) - << "block height: " << b->height() << "counter: " << counter; - }); - ASSERT_TRUE(wrapper.validate()); + auto stored_blocks = blocks->getBlocksFrom(1); + ASSERT_EQ(stored_blocks.size(), blocks_total); + for (size_t i = 0; i < stored_blocks.size(); i++) { + auto b = stored_blocks[i]; + ASSERT_EQ(b->height(), i + 1) + << "block height: " << b->height() << "counter: " << i; + } } /** @@ -316,11 +287,8 @@ TEST_F(BlockQueryTest, GetBlockButItIsNotJSON) { block_file << content; block_file.close(); - auto wrapper = - make_test_subscriber(blocks->getBlocks(block_n, 1), 0); - wrapper.subscribe(); - - ASSERT_TRUE(wrapper.validate()); + auto stored_blocks = blocks->getBlocks(block_n, 1); + ASSERT_TRUE(stored_blocks.empty()); } /** @@ -344,11 +312,8 @@ TEST_F(BlockQueryTest, GetBlockButItIsInvalidBlock) { block_file << content; block_file.close(); - auto wrapper = - make_test_subscriber(blocks->getBlocks(block_n, 1), 0); - wrapper.subscribe(); - - ASSERT_TRUE(wrapper.validate()); + auto stored_blocks = blocks->getBlocks(block_n, 1); + ASSERT_TRUE(stored_blocks.empty()); } /** @@ -359,14 +324,14 @@ TEST_F(BlockQueryTest, GetBlockButItIsInvalidBlock) { */ TEST_F(BlockQueryTest, GetTop2Blocks) { size_t blocks_n = 2; // top 2 blocks - auto wrapper = - make_test_subscriber(blocks->getTopBlocks(blocks_n), blocks_n); - size_t counter = blocks_total - blocks_n + 1; - wrapper.subscribe( - [&counter](const auto &b) { ASSERT_EQ(b->height(), counter++); }); + auto stored_blocks = blocks->getTopBlocks(blocks_n); + ASSERT_EQ(stored_blocks.size(), blocks_n); - ASSERT_TRUE(wrapper.validate()); + for (size_t i = 0; i < blocks_n; i++) { + auto b = stored_blocks[i]; + ASSERT_EQ(b->height(), i + 1); + } } /** @@ -414,5 +379,6 @@ TEST_F(BlockQueryTest, GetTopBlockFail) { auto top_block_error = framework::expected::err(empty_blocks->getTopBlock()); ASSERT_TRUE(top_block_error); - ASSERT_EQ(top_block_error.value().error, "error while fetching the last block"); + ASSERT_EQ(top_block_error.value().error, + "error while fetching the last block"); } diff --git a/test/module/irohad/ametsuchi/block_query_transfer_test.cpp b/test/module/irohad/ametsuchi/block_query_transfer_test.cpp index 906acddc50..caa7c764aa 100644 --- a/test/module/irohad/ametsuchi/block_query_transfer_test.cpp +++ b/test/module/irohad/ametsuchi/block_query_transfer_test.cpp @@ -127,11 +127,9 @@ namespace iroha { tx_hashes.push_back(block.transactions().back().hash()); insert(block); - auto wrapper = make_test_subscriber( - blocks->getAccountAssetTransactions(creator1, asset), 1); - wrapper.subscribe( - [this](auto val) { ASSERT_EQ(tx_hashes.at(0), val->hash()); }); - ASSERT_TRUE(wrapper.validate()); + auto txs = blocks->getAccountAssetTransactions(creator1, asset); + ASSERT_EQ(txs.size(), 1); + ASSERT_EQ(txs[0]->hash(), tx_hashes[0]); } /** @@ -145,11 +143,9 @@ namespace iroha { tx_hashes.push_back(block.transactions().back().hash()); insert(block); - auto wrapper = make_test_subscriber( - blocks->getAccountAssetTransactions(creator2, asset), 1); - wrapper.subscribe( - [this](auto val) { ASSERT_EQ(tx_hashes.at(0), val->hash()); }); - ASSERT_TRUE(wrapper.validate()); + auto txs = blocks->getAccountAssetTransactions(creator2, asset); + ASSERT_EQ(txs.size(), 1); + ASSERT_EQ(txs[0]->hash(), tx_hashes[0]); } /** @@ -163,11 +159,9 @@ namespace iroha { tx_hashes.push_back(block.transactions().back().hash()); insert(block); - auto wrapper = make_test_subscriber( - blocks->getAccountAssetTransactions(creator3, asset), 1); - wrapper.subscribe( - [this](auto val) { ASSERT_EQ(tx_hashes.at(0), val->hash()); }); - ASSERT_TRUE(wrapper.validate()); + auto txs = blocks->getAccountAssetTransactions(creator3, asset); + ASSERT_EQ(txs.size(), 1); + ASSERT_EQ(txs[0]->hash(), tx_hashes[0]); } /** @@ -187,13 +181,11 @@ namespace iroha { tx_hashes.push_back(block.transactions().back().hash()); insert(block2); - auto wrapper = make_test_subscriber( - blocks->getAccountAssetTransactions(creator1, asset), 2); - wrapper.subscribe([ i = 0, this ](auto val) mutable { - ASSERT_EQ(tx_hashes.at(i), val->hash()); - ++i; - }); - ASSERT_TRUE(wrapper.validate()); + auto txs = blocks->getAccountAssetTransactions(creator1, asset); + ASSERT_EQ(txs.size(), 2); + for (size_t i = 0; i < txs.size(); i++) { + ASSERT_EQ(txs[i]->hash(), tx_hashes[i]); + } } } // namespace ametsuchi } // namespace iroha diff --git a/test/module/irohad/execution/query_execution_test.cpp b/test/module/irohad/execution/query_execution_test.cpp index f7c9fc19bc..d96ee3b70c 100644 --- a/test/module/irohad/execution/query_execution_test.cpp +++ b/test/module/irohad/execution/query_execution_test.cpp @@ -77,16 +77,14 @@ class QueryValidateExecuteTest : public ::testing::Test { * @param N * @return observable with transactions */ - rxcpp::observable getDefaultTransactions( - const std::string &creator, size_t N) { - return rxcpp::observable<>::iterate([&creator, &N, this] { - std::vector result; - for (size_t i = 0; i < N; ++i) { - auto current = makeTransaction(creator); - result.push_back(current); - } - return result; - }()); + std::vector getDefaultTransactions( + const std::string &creator, size_t N) { + std::vector result; + for (size_t i = 0; i < N; ++i) { + auto current = makeTransaction(creator); + result.push_back(current); + } + return result; } std::string admin_id = "admin@test", account_id = "test@test", @@ -645,10 +643,10 @@ class GetAccountTransactionsTest : public QueryValidateExecuteTest { void SetUp() override { QueryValidateExecuteTest::SetUp(); role_permissions = {Role::kGetMyAccTxs}; - txs_observable = getDefaultTransactions(account_id, N); + txs = getDefaultTransactions(account_id, N); } - rxcpp::observable txs_observable; + std::vector txs; size_t N = 3; }; @@ -668,10 +666,10 @@ TEST_F(GetAccountTransactionsTest, MyAccountValidCase) { EXPECT_CALL(*wsv_query, getRolePermissions(admin_role)) .WillOnce(Return(role_permissions)); - txs_observable = getDefaultTransactions(admin_id, N); + txs = getDefaultTransactions(admin_id, N); EXPECT_CALL(*block_query, getAccountTransactions(admin_id)) - .WillOnce(Return(txs_observable)); + .WillOnce(Return(txs)); auto response = validateAndExecute(query); ASSERT_NO_THROW({ @@ -706,7 +704,7 @@ TEST_F(GetAccountTransactionsTest, AllAccountValidCase) { .WillOnce(Return(role_permissions)); EXPECT_CALL(*block_query, getAccountTransactions(account_id)) - .WillOnce(Return(txs_observable)); + .WillOnce(Return(txs)); auto response = validateAndExecute(query); ASSERT_NO_THROW({ @@ -741,7 +739,7 @@ TEST_F(GetAccountTransactionsTest, DomainAccountValidCase) { .WillOnce(Return(role_permissions)); EXPECT_CALL(*block_query, getAccountTransactions(account_id)) - .WillOnce(Return(txs_observable)); + .WillOnce(Return(txs)); auto response = validateAndExecute(query); ASSERT_NO_THROW({ @@ -802,7 +800,7 @@ TEST_F(GetAccountTransactionsTest, NoAccountExist) { .WillOnce(Return(role_permissions)); EXPECT_CALL(*block_query, getAccountTransactions("none")) - .WillOnce(Return(rxcpp::observable<>::empty())); + .WillOnce(Return(std::vector())); auto response = validateAndExecute(query); ASSERT_NO_THROW( @@ -817,10 +815,10 @@ class GetAccountAssetsTransactionsTest : public QueryValidateExecuteTest { void SetUp() override { QueryValidateExecuteTest::SetUp(); role_permissions = {Role::kGetMyAccAstTxs}; - txs_observable = getDefaultTransactions(account_id, N); + txs = getDefaultTransactions(account_id, N); } - rxcpp::observable txs_observable; + std::vector txs; size_t N = 3; }; @@ -840,10 +838,10 @@ TEST_F(GetAccountAssetsTransactionsTest, MyAccountValidCase) { EXPECT_CALL(*wsv_query, getRolePermissions(admin_role)) .WillOnce(Return(role_permissions)); - txs_observable = getDefaultTransactions(admin_id, N); + txs = getDefaultTransactions(admin_id, N); EXPECT_CALL(*block_query, getAccountAssetTransactions(admin_id, asset_id)) - .WillOnce(Return(txs_observable)); + .WillOnce(Return(txs)); auto response = validateAndExecute(query); ASSERT_NO_THROW({ @@ -878,7 +876,7 @@ TEST_F(GetAccountAssetsTransactionsTest, AllAccountValidCase) { .WillOnce(Return(role_permissions)); EXPECT_CALL(*block_query, getAccountAssetTransactions(account_id, asset_id)) - .WillOnce(Return(txs_observable)); + .WillOnce(Return(txs)); auto response = validateAndExecute(query); ASSERT_NO_THROW({ @@ -913,7 +911,7 @@ TEST_F(GetAccountAssetsTransactionsTest, DomainAccountValidCase) { .WillOnce(Return(role_permissions)); EXPECT_CALL(*block_query, getAccountAssetTransactions(account_id, asset_id)) - .WillOnce(Return(txs_observable)); + .WillOnce(Return(txs)); auto response = validateAndExecute(query); ASSERT_NO_THROW({ @@ -974,7 +972,7 @@ TEST_F(GetAccountAssetsTransactionsTest, NoAccountExist) { .WillOnce(Return(role_permissions)); EXPECT_CALL(*block_query, getAccountAssetTransactions("none", asset_id)) - .WillOnce(Return(rxcpp::observable<>::empty())); + .WillOnce(Return(std::vector())); auto response = validateAndExecute(query); ASSERT_NO_THROW( @@ -1002,7 +1000,7 @@ TEST_F(GetAccountAssetsTransactionsTest, NoAssetExist) { .WillOnce(Return(role_permissions)); EXPECT_CALL(*block_query, getAccountAssetTransactions(account_id, "none")) - .WillOnce(Return(rxcpp::observable<>::empty())); + .WillOnce(Return(std::vector())); auto response = validateAndExecute(query); ASSERT_NO_THROW( diff --git a/test/module/irohad/network/block_loader_test.cpp b/test/module/irohad/network/block_loader_test.cpp index 44af03020a..101fddc2bf 100644 --- a/test/module/irohad/network/block_loader_test.cpp +++ b/test/module/irohad/network/block_loader_test.cpp @@ -114,7 +114,7 @@ TEST_F(BlockLoaderTest, ValidWhenSameTopBlock) { EXPECT_CALL(*storage, getTopBlock()) .WillOnce(Return(iroha::expected::makeValue(wBlock(clone(block))))); EXPECT_CALL(*storage, getBlocksFrom(block.height() + 1)) - .WillOnce(Return(rxcpp::observable<>::empty())); + .WillOnce(Return(std::vector())); auto wrapper = make_test_subscriber( loader->retrieveBlocks(peer->pubkey()), 0); wrapper.subscribe(); @@ -149,7 +149,7 @@ TEST_F(BlockLoaderTest, ValidWhenOneBlock) { EXPECT_CALL(*storage, getTopBlock()) .WillOnce(Return(iroha::expected::makeValue(wBlock(clone(block))))); EXPECT_CALL(*storage, getBlocksFrom(block.height() + 1)) - .WillOnce(Return(rxcpp::observable<>::just(wBlock(clone(top_block))))); + .WillOnce(Return(std::vector{clone(top_block)})); auto wrapper = make_test_subscriber(loader->retrieveBlocks(peer_key), 1); wrapper.subscribe( @@ -191,7 +191,7 @@ TEST_F(BlockLoaderTest, ValidWhenMultipleBlocks) { EXPECT_CALL(*storage, getTopBlock()) .WillOnce(Return(iroha::expected::makeValue(wBlock(clone(block))))); EXPECT_CALL(*storage, getBlocksFrom(next_height)) - .WillOnce(Return(rxcpp::observable<>::iterate(blocks))); + .WillOnce(Return(blocks)); auto wrapper = make_test_subscriber( loader->retrieveBlocks(peer_key), num_blocks); auto height = next_height; @@ -214,7 +214,7 @@ TEST_F(BlockLoaderTest, ValidWhenBlockPresent) { EXPECT_CALL(*peer_query, getLedgerPeers()) .WillOnce(Return(std::vector{peer})); EXPECT_CALL(*storage, getBlocksFrom(1)) - .WillOnce(Return(rxcpp::observable<>::just(wBlock(clone(requested))))); + .WillOnce(Return(std::vector{clone(requested)})); auto block = loader->retrieveBlock(peer_key, requested.hash()); ASSERT_TRUE(block); @@ -234,7 +234,7 @@ TEST_F(BlockLoaderTest, ValidWhenBlockMissing) { EXPECT_CALL(*peer_query, getLedgerPeers()) .WillOnce(Return(std::vector{peer})); EXPECT_CALL(*storage, getBlocksFrom(1)) - .WillOnce(Return(rxcpp::observable<>::just(wBlock(clone(present))))); + .WillOnce(Return(std::vector{clone(present)})); auto block = loader->retrieveBlock(peer_key, kPrevHash); ASSERT_FALSE(block); diff --git a/test/module/irohad/torii/torii_queries_test.cpp b/test/module/irohad/torii/torii_queries_test.cpp index fc0b845d77..13ee2673e3 100644 --- a/test/module/irohad/torii/torii_queries_test.cpp +++ b/test/module/irohad/torii/torii_queries_test.cpp @@ -476,17 +476,14 @@ TEST_F(ToriiQueriesTest, FindTransactionsWhenValid) { auto account = shared_model::proto::AccountBuilder().accountId("accountA").build(); auto creator = "a@domain"; - auto txs_observable = rxcpp::observable<>::iterate([&account] { - std::vector result; - for (size_t i = 0; i < 3; ++i) { - std::shared_ptr current = - clone(TestTransactionBuilder() - .creatorAccountId(account.accountId()) - .build()); - result.push_back(current); - } - return result; - }()); + std::vector txs; + for (size_t i = 0; i < 3; ++i) { + std::shared_ptr current = + clone(TestTransactionBuilder() + .creatorAccountId(account.accountId()) + .build()); + txs.push_back(current); + } EXPECT_CALL(*wsv_query, getSignatories(creator)) .WillRepeatedly(Return(signatories)); @@ -496,7 +493,7 @@ TEST_F(ToriiQueriesTest, FindTransactionsWhenValid) { perm.set(Role::kGetMyAccTxs); EXPECT_CALL(*wsv_query, getRolePermissions("test")).WillOnce(Return(perm)); EXPECT_CALL(*block_query, getAccountTransactions(creator)) - .WillOnce(Return(txs_observable)); + .WillOnce(Return(txs)); iroha::protocol::QueryResponse response; From 742d21170c81cc5d6b1925358c1e7c6c7dd0f282 Mon Sep 17 00:00:00 2001 From: Alexey R Date: Mon, 30 Jul 2018 19:52:37 +0700 Subject: [PATCH 92/97] Feature/doxygen pipeline (#1594) * Doxygen docs Signed-off-by: Alexey R --- .jenkinsci/doxygen.groovy | 19 ++++++++++++----- Doxyfile | 2 +- Jenkinsfile | 25 +++++++++++++++-------- docker/develop/Dockerfile | 2 +- docs/image_assets/iroha_logo_doxygen.png | Bin 0 -> 59095 bytes 5 files changed, 33 insertions(+), 15 deletions(-) create mode 100644 docs/image_assets/iroha_logo_doxygen.png diff --git a/.jenkinsci/doxygen.groovy b/.jenkinsci/doxygen.groovy index 7389b55c07..65d330f013 100644 --- a/.jenkinsci/doxygen.groovy +++ b/.jenkinsci/doxygen.groovy @@ -1,11 +1,20 @@ #!/usr/bin/env groovy def doDoxygen() { - - sh """ - doxygen Doxyfile - #rsync docs/doxygen - """ + if (env.GIT_LOCAL_BRANCH in ["master","develop"] || env.CHANGE_BRANCH_LOCAL == 'develop') { + def branch = env.CHANGE_BRANCH_LOCAL == 'develop' ? env.CHANGE_BRANCH_LOCAL : env.GIT_LOCAL_BRANCH + sh "doxygen Doxyfile" + sshagent(['jenkins-artifact']) { + sh "ssh-agent" + sh """ + rsync \ + -e 'ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no' \ + -rzcv --delete \ + docs/doxygen/html/* \ + ubuntu@docs.iroha.tech:/var/nexus-efs/doxygen/${branch}/ + """ + } + } } return this diff --git a/Doxyfile b/Doxyfile index 03d7940060..bfa2075c9e 100644 --- a/Doxyfile +++ b/Doxyfile @@ -51,7 +51,7 @@ PROJECT_BRIEF = "Iroha - A simple, decentralized ledger http://iroha.te # pixels and the maximum width should not exceed 200 pixels. Doxygen will copy # the logo to the output directory. -PROJECT_LOGO = ./docs/ +PROJECT_LOGO = ./docs/image_assets/iroha_logo_doxygen.png # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path # into which the generated documentation will be written. If a relative path is diff --git a/Jenkinsfile b/Jenkinsfile index 38dfe6e3c5..61e7001f11 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -17,7 +17,7 @@ properties([parameters([ choice(choices: '26\n25\n24\n23\n22\n21\n20\n19\n18\n17\n16\n15\n14', description: 'Android Bindings ABI Version', name: 'ABABIVersion'), choice(choices: 'Release\nDebug', description: 'Android bindings build type', name: 'ABBuildType'), choice(choices: 'arm64-v8a\narmeabi-v7a\narmeabi\nx86_64\nx86', description: 'Android bindings platform', name: 'ABPlatform'), - booleanParam(defaultValue: false, description: 'Build docs', name: 'Doxygen'), + booleanParam(defaultValue: true, description: 'Build docs', name: 'Doxygen'), string(defaultValue: '4', description: 'How much parallelism should we exploit. "4" is optimal for machines with modest amount of memory and at least 4 cores', name: 'PARALLELISM')])]) @@ -371,23 +371,32 @@ pipeline { stage('Build docs') { when { beforeAgent true - allOf { - expression { return params.Doxygen } - expression { GIT_LOCAL_BRANCH ==~ /(master|develop)/ } - } + expression { return params.Doxygen } } // build docs on any vacant node. Prefer `x86_64` over // others as nodes are more powerful - agent { label 'x86_64 || arm' } + agent { label 'x86_64' } steps { script { def doxygen = load ".jenkinsci/doxygen.groovy" - docker.image("${env.DOCKER_IMAGE}").inside { - def scmVars = checkout scm + def dPullOrBuild = load ".jenkinsci/docker-pull-or-build.groovy" + def platform = sh(script: 'uname -m', returnStdout: true).trim() + def iC = dPullOrBuild.dockerPullOrUpdate( + "$platform-develop-build", + "${env.GIT_RAW_BASE_URL}/${env.GIT_COMMIT}/docker/develop/Dockerfile", + "${env.GIT_RAW_BASE_URL}/${env.GIT_PREVIOUS_COMMIT}/docker/develop/Dockerfile", + "${env.GIT_RAW_BASE_URL}/develop/docker/develop/Dockerfile", + ['PARALLELISM': params.PARALLELISM]) + iC.inside() { doxygen.doDoxygen() } } } + post { + cleanup { + cleanWs() + } + } } stage('Build bindings') { when { diff --git a/docker/develop/Dockerfile b/docker/develop/Dockerfile index fb8247e919..5c156fe706 100644 --- a/docker/develop/Dockerfile +++ b/docker/develop/Dockerfile @@ -31,7 +31,7 @@ RUN set -e; \ libpcre3-dev autoconf bison \ # other wget curl file gdb ccache \ - gcovr cppcheck doxygen graphviz graphviz-dev unzip zip; \ + gcovr cppcheck doxygen rsync graphviz graphviz-dev unzip zip; \ apt-get -y clean # install cmake 3.10.2 diff --git a/docs/image_assets/iroha_logo_doxygen.png b/docs/image_assets/iroha_logo_doxygen.png new file mode 100644 index 0000000000000000000000000000000000000000..d59f0eedd002c87e618d74ea487ffeeb48812ff1 GIT binary patch literal 59095 zcmeIb2b`Q$+4p~@m+d9VZh9k?bOIqjq$wcMK|v7-DG&(>gdm8h2v||Ep@Irn@Igcc zeCSo06zK#KNblK_ZL`zozTfZf%*%p=Y0T>d<2rFPS%V`ox1L%$YK6>d-?Doj7Isp;KoZ zbl}jblcyX!Y4SmnrW`t9%CwpMKWOsMLK!|Q%BRDZ%$+~;^kY9!=#Fnk48LgU(o1Jf znsnurS5CZg+Qh|6E~NUQhfbP2b<)(S6L@068f+- z6-yS{q0OB$Y2Lzlm&{wV^m6J=*=@Z`FIv2G@#PmS-b0sNxA)jFYjV;_U48F%yq8?E z+paHP`tdITf&xf)Tl4ZWzkKPuNvF@deDM`a=FI!}mzc_k-OYaKg84fZXQ!5QXOCl_ zch!PDRMVY&HH!-Q6*`=`&OBzxyxB|V9m{JL7&K$TEYiO!of?eIyvyWFjo#kQFzEV_K@ z>_v0top9_dE=*joVD8N6^QTQec>d&rCQP5kpM$1Mn>XQtsne%TIC$Q?gJ#d4J9oys z8Pn~^Pdcf4^-$8a!fnkqEokS{K(+9ZSnIXU`FTn|1l@FU^~K_@te#?HE*7y+Wl0 zyDsLw0U+Ogx9WTD;}7pWaGbL!M0spXrpPwlyp%NNgIdgbgT^NzX@{Mc7o z$+x_7)jfAH=c3t*E}S=a)}$iziWhoTIee0Y$ifBxH?H2kk>dYw^|~|4|BtL*eoc2K z$s(EWO2UUrvdx72LB`pZ>|i^)T-$*+`RC6T885uTK7Yv6$&^V`^tVv8`|X}p&*{Bt z_uD{Z?tXCf07&m&_k*i<`1H8DA6z{E()-u_;OZSdJ?`!Y zR}X;n{&hdNdWTPsyZgb_10cPB-4Cwb;nU;pesJ{wNbg_wgR6J=^tihpTs;8N``7*8 z>K#5k?(PRy4}kRkbw9Xzhfj~Y`@z)%AiaOx53b(f)8p=baPto?gv*7fb{-#Ke&2_PmjC%!PNsGy?@;guHNC({Z?tXCf z07&m&_k*i<`1H8DA6z{E()-u_;OZSdJ?`!YR}X;n{eToWuQGl4<1Cfyn{xm3!S4&FV+c=(rY>oDbG4=`1;4mKv%X^czr zdnes35jW5(ccK>aGLl4Fk~omaEzxUVBA>D_=gNLo%>hA5rV~;&y z&O84CQ`fIAk206Sls%pub9Z%7UxAbH@^bj^I3@-Bjy>i`Gj{A~vth$V^YqhCn_Mny zJYK)aWO#@=Sto18aKB+ogK6I0Vgdm_4dm+wg8@Ey0W;5f96otzSKo6~&DY;C3R|sf z9KnFcWLVE!4v=KiX5hfVyl2h!?ag+K4)>~Ss!dBvtC@B9p=RakwdR8lR+(rdTxe*= zoB9YabuV+%lDkaQaXfssGdG^Xd$ESDheON*cqEd{*CXt4nRC|k>)X#{vmVph)?k7G zHbDbNO)r(n0OLNUe_gd%xvJE}V&F$$8L*1eSHSrF9+Rbhj<#ve_$L%zyH=dTzY7{c_rO>EEIuxhPs5Mk+_AK8CrKLq}V5ZEUI z@xaltt?efJ-YUyQEIZjt#~vuT{U86lu5I(C`JsOG;C@bA;I~27IVJtbQzqTqc#FTR z+;}RhOzY2najWNdcdm(qqmR3t@x8RnZMX=G%w&UpU+KWK8}4N5MSTECbi-N6T9*nmumivYU&IeR@`lSY+sJUJ5%M68OXDxpxwL?RK? z1=IoNueJg~pH(~5l++OwEm+TzzozR_P>N;O=ZbfBdf(|Wq&Cb%Fz#Obtf3dZNn_YC z$L>#j_@Av>wqr7O-g!lB_k9&%;5cqS$91KD_W>B$TrRiC_4{AsehgISGAeC41&qCA zT&pOwCMf7;@m-%$*39}`&WUrig2{uJJKYyRIvCSRVB`3_Xh5oD|L*0FM~D&*n^Z{z zL|31B?Wq6-kS3eX-sMH-^MD+-fC#zfW)u9*jGwkM@p!cL zxyQd5>f3iwenI!thuZg;S`U= zu30;wva;eVJ_20ldEOt=v)vX|hQe3VnRHosMa4V8VDN#)#)ca~$QJ5*8yXsJs;G#r zl6?`4)>F}I5~=pXa!&jC*-XyQVl?%wt^L-PO6&0sZ@$_uv1(xZ%(}%!dUr=&c5O)}Tvkd9Wi?PhX64G~M1*9Ba=paNU>9 zxi|3O?-M4$S2;U&U@X znRdfGXUCAuIwtNUn?2t2eK{xnt5mA)#o9h-C{F8Tot=rViy=|+@W~H4-?UoxdX7$} z(oeZb_Y`c!->OZO{9~*v41@n0J7Td%9pi93lxf{30|2RUE@4pr=8F>w2eC zsQjpmRJ4;)?t^J7b--dJ|A*SvQv z-E7yj-4%~4M(mUFGcX%`s z{VE)c^>Xf_k|mG=G}VTV+xn`EW~4~W>+#*WZvDCg`t|91NLhKgd1u*sGn<;4r-Bfs zq9RBeNWIPJzad%sKul)Ts8NfSzxRGyYisL8>QBWQ<=}lbYx~rtZ}^Sxn~`wvns_da zVefnfBFC$;=fcv=2Xt}J=?GwTOgxVLNa-&5J33+@opaTjZ@qh3HsjtK3WoH^DIknr zPKH>lBLG2J0*S(eBn=BvsE$!i)Wdr!Imej{X8ex(a4$2w_*`C60WIJOhn@gjPQu~w z&0N;G62tp@nM@kIaX=M#hvOcX$z>kKMNq76--{TLb88eYt#L2Sp3 zGJcR_&jp++7qi*rF$V0=H*EkX-@s5Pc)_Yws}8&M*56FocLD+G9@HeCJS<5>wxy4r zM52Yo6JX3@4-CsrNR;2!X8a?DnefMtHmz4*XMz*Qo02n6GabLW-S~?t5DVj$lz81p z^o!w8^agjgh);j0Zz&k8Cz&>`JVOO8usg7{W;14Rw8Z?dU!A#Ra9`6OZ-}>7>6dRs zo{)}M$M$X8wpkU0t6RF-SAR^5H%uzG$?p%;Ha2c+sH(0?e)ERwXFUAyqsisVmxn^( z$dv8dTR$BN`R`7rlW=19tEp7Bj{pakd!e*6^3#nFs3vwfw_n~oj@COQSoe5{en96IEcRq$sS<>h6GWHP=M;u^`=+NVsOX1p*pt?uS6o2&;+L`{`gu_JD+;U(^` zOL{r9t^fZpRv1Fjg~G*+KCkzm^zRFY{e82U%t7gNChP^yAnbSH{DI-ahkvtt`+Bo= zTk}x-rVo2OQcdwpdc7auwYmku`WF%)!kkV-rCJyXl{_0&Ihk0jy$JVP(01;q=~Lrp?qfG?*mPZWb#+~H^Y+mT zzPRu+`%WOXH^*d9iF_cb34m&LEQMj{4M;$VwV71z6alwboOayAbI&)a7hb{@8#RfC zA2-2+rkIkmPB)!*|JC@VYDp@A&^e#~4A0ncH*|NW7TYm!VQ0%$lUwz!k>el_i>|x7 ze2^B|9F3TRwl}z= z$xI`4*6Xjoc7I7}2}BnENonb@yK8X7uUxhIIu|ty?qn(}O3!@l)z`3HoYGJ*2(xw} z3g>xX-WAn`}C=Nzp1&^-PYKGtrvxn ziLco4s~c2N^SVA1OFEqf?iIM-k2Vbr+nmRrcpTL?s`ZO$6|@Q zI&l`AdFJWk|1APxS+oWgX{?K0tEoYCtr9AglRHLmr02Y9wRW9}o^gu(9DnLTQ$BB@ zaj^&Df4$dKT)x<({`H!1wr@9H)DkcL@oXwSIk{>@4NKHaW5xN|wRXVxY@*$G;b!Vm zTySqCP8&7orI#W7UM5Zw0^x;b`%us-y87=daQUukpolc{tfm$rDZNH@|}ba58|f-imUQ+q&K)pZ}*R{p_br+l|C8%kVa- zy<`dk^c?9eEB#AX!&)4$#M!*Xs%x@agf070Bt;s19`kWH(ot!zc>*LIffUIvW3Md~ zW@VrG9IHK%+`iN_vH^Ht83$P;X@%|WNmE@_@tIUQww>ujHgDPZbC?qV_Y=rU zzmYB}yJ^eTq`|niT#q(r+Nwf>SfE*WynHP6ASB3F0X0F#wCPhjXqke9Teoc$;!s6k zbROYNDv!pYF(ErVudo*1P`x5L=LsSAW%^hFw+_+AL_U8)#ILq$sw-b_l?xxkN79O> zJlG`qV2iL$O{g7I7t^;-t!Zj% z(%im})clk@YN4=y9NI|@V=R+1g<#(6+Zvi0Y>U(bCq$2b;Z>JN$Oj=l8KhJ18PM$} zJH8!%(2ux`wH%>qm#9UOX=tXl)`t>(4($^a=(Rv#Q1e1I<15EOf zCrn`M7!&=}38wQe_n82ymMeE~qJ5?>oBj(k$hU;*1i|Nu=z&j#&GjWps+I zqvn`dM;v^H-xoIPH>^Xi-;A`tc}2rQVi0y3&HW>l$^c#><}ebe8Ief1t-QSCuC3dI zE0P)c!iP^3KNsI=OKp5Z2+S0_Lusy_LGPyx(KwVv#=+r-&0I2S)bN(O|9r15(WJen zPPBX$y?6Ae@7byX&U^iL+7)1>Z+pt?%V1*udk|EF+W4|IFTUhS)E zl9r4^rSPa3#OFc!rgr(9rH@9|>q}+P;EMl@KnhL!ay}ZBGY8F3*I<=tH!~19ZgLwo zo5(4j!emUF#J&G*{DTLY+&1fSUYDrS$1ll<^?U;ull979tB3%%q`W9BI=#B)Ty|NoL zi9>k_0DMM?2ZNOEfR+N%xc3ACzQ2IvXK>&6z5eSmnb_)VHinwe3Eafo>?+xQv|xIv z=r4=a5sikPtEw!$I390nPbL#W#O_2ZuG!s4r+@cpu34liw5_jdZ!@VZK~$<*B!~$H zLo!ys*xcOm+;d3rf;7o@p08l(!miSeKQyGvXbBpp$E>TUs9c#&r`$|BTbH)#ahbo{YHHoKcH@H} zLer<1j$i*4LXc{euU^0bzJu&#B__9Vvq?SkoQaCr4&|3$SkR*30W?0$DE(D1&Qs0{HR}7Rn^=eZ%}i* z$pp~ZZ7H_W)n1-LG(LF;8Kl{UG#pS@hZJpDDhZnbuGmSh1k1}~jCD)SldDf85@Rr` zr(s{bx@H{?IHx}YY(ClNO9EN{cQ84wv}3Z?*N&bGaJUiY2eAwZZ;X9=o4KI5$W+1b zw-ae^Z#NSTocQ~(<3`{5@+&WUR;^s~#qzSUJ~=mcK}B`dFO;{IuWK9l_x}h9@VP=D zrN*hKHcokk5~E685*|^?BFKN*f^8mXvWhSnv>lsfU5FS&j%qZ?4+TdDOWr_8ylY1Sc#QWlT7vgGB-IUzf ztaPA&#I^$0dG8_)AwdX3v$m#|R}~3^K^fp(+$0fCgh!1UA0IP%tVL+-oDqpc>z_+d zTguBzY2UV5Xd0&E^S0!PCO=F4^?H=|;PGoNnj@-8cI(!SikVm=S!2~SC1fnDx~YTQ zO6IRIGyY|>nPDxh+y8*O_E>qBDu`zt`LV<9@sqH*W!tvjD>+d!T)A@X-`B3+HiqCi z4vu%YmH>X0azU4C-Fg&zL1ROe>=%InRaI5eq1MByut=pKse4iU^Z5zO2HW1Uec&qDi}|SZXl87(M#uE09uW#UgcwFsX4jZnD7f_}II4?K)!vpJ0E)*7L zL1EqwzJYWgxaxNsLZR9Gn8fjRbF-^!>zJq8&q5&ojTAwe?~y~$4NrEBF^~_G3D{j- zSmq+6;2NK;%GFh+(5Y=eoIM_odoyINs(wjHsf|xq2~wy5CyjRCsnH7+$W{lDOb3ag z%-D`Z1L}gm>;8zw&{@G<3iRMfBolmc@8$X%`cOn>ylG|e&Q?=bKm7ZB`fUDEd;9h( z5b?}OlP1lmtsU^#Mj<>`>m{qLjlmnrbbVDN4pt2`hb>jBzS#CNBh_1WsS8p z{Qi*XQ;*$2AX;SxtS)nj^br7QCX*V+g@H`IFO&8=nJlq4r;$Oe=l*gA`UZURHC8Pi zL|t+-+2+8=;cjr+WD`TRQp^KZNs|TG>vQot(!+kH0}t_r9H}FzXP!4Dr=4s%zVm}z zMu;I!3pa*dYjer?R1zj8;c6M>WODss0{y63!q^B~W^w7c>)mwrE#&p{{$St@q!8`X zZyt_3n*S(UJ2rwXxD!5^_uM2ucmvA|nQXCsnP4sGS?+6xb z5uP%O?Kt=VlFFzh(YjTL8;2{uff07hO~{rgo{SEVu9%L}m&AL}|>9TeM_jY8VY4{NFiG^|~>n*JaVS=s9e(0(sJ+Pk3Vj%yY4zOa^%R^KmPITp%*W_ z^bz6`qZpGflS!Rt+oo~BP3?BQ3Mnz%M~^Q&s-h1IQ#k{+Y~7L~9q4QH z4PxfU29fkjk)a-A8walEZ)MZVzTVc}CYAV@bsM*Q*{>{HOJXcYa$EEH>FupoIjPKO zR>ZEewae76))1sdi^~IudCOG^s2bg5>XA%)#~rvRo`_s>(b~kLPmal~`T!U20eR^m z7*SktP!`<{;wF|D6$0BESyZih)A_((Em1{2clPSot+)97ed<2V%6u*x@E+;LJEA}v zRE2{v4gsiGtO2O+Nm&+nsT!M}^96#}dBUN`#0~O;&p(~e6X=#-=uDe?D?-*5$@@|A z_ltjNEo4WRUu4|5jGyrmAqR0o3?b(xxG)n8#wQg31es!Cum1QmHkc9;**-|56b)*=^ISH&WDKydonR08tDIc@Ai z*Mq@uwzahhoqsF)Lq^_~%_P6n+R{9N0A%Ha2_xrwOxe#~dih^li4DjDB`Kw0!He<{ zBM}HCDV%52t$fx6z3g+{-gWh5C8iQ{NrQMBr0?s#n-y*2kZbyuOCKJRh;zhUiH7Btm&AjW?( zKzZ<8+i#cP0^)@#zW*da_~XRLmxY=GQE0T z9qt^HAyLpxB&`bN!Jcr%HRZ~MD09R#Jh=1Rjt-L}NaJH;QegBbzxEcox=9PWi9Yc zmu!KV4&ok)bshG^yLnUNwma}78T5(E16w}`lT z>y|*gGv3GNmrKMSE-i_yO=pr7>2#L#Ij^XIVY_ZcWwnbKayM<>D9gPwnM%2(rDaO2 zq+S<9yN>}6RL-d=FVEH0*O;~IHhCi967J)PYTJ$n3&`^=f|EjAI^ zy8~K$%KsNYO@xe!T*gJpl47QK9TU(jrK+JEv2ZCE3AenceJM>()nQC^XMED+Iup5u zr(X1>nzox@b)~qEeIWl-zDpV2A_^h84C@fKf`8Z$jgZJVxkl?Zu&JtYOy@oKK?rfY zSc8opgco9`-+ISnSFSQHJGEfH1o`0fjL_S+)&$0kCW-MtzJL@2kF*QWUq4`ow{IWg z*+_!pl!+#E*bG}I!_K$d+Vv(2afwL17-3S+G|{fY*ikOn8QE^iYr>hTu|r)i->u{+ zNG#3H%Q()*+jiUfjbfD+DZARRA3Q`Q-MjN2^LI$Tf~$v3Z7r^7Vjgh%r^H7U*b2aO z+o4;?-uOYWdisK&uLH%`flx$$ftx36+E|=)0O{b7N6afR`m!J_L@6+AWFTrT)Y8f| zwN<8t)DS!#fNb>IV^JJs#E9W0lS;MXtrEAlMbc%{=1qKNT!CN)E{RWFyZum51xv42 zS-d+HxuPYlBtZxogh)=m@3x@L{;9l)R zj~R37Nyal`7`7)kBHTiVp`NkeW{c(^SS!6?YleqjGGo4$ARdzpr&F#))^6OY?KpSZoZs=@?7RR7>X`IWS_nIt>< zGQ=;k@2|87=pQh^Bp!T(DET(a=|kiu`IxK^Lhym`k{apPUZ=e(5Pwo$8UXyvd&?~m zWtP7Wv2~j8kw+UpfvnE^{%QjC!yuvqNOl~K^ciA_(&|1K@0kx)n#^)iwUJgH3@)#t z@#wb9!IX>~dRNWaC!GsRvm3o~5T_gp#R8~Jl$WyiR`ROg)f0T0rW_F>jE*&HO`Q2| z8b8L2QDy*YS~K^zK-eX`V%RnZaP2C#ny#hJpKt>3)kGm6wd-;}MSo))3~wO?_%o0P zBqNH2%McN5fgG6IW*jzh3$@qKPl`5!C<)?yx!sIaGGBz%24QJTQS6@XsHsJlQ7Xi> z4la-{|GdvM;XT3%Hwo$mdG+OS!MFp``d+>Gzr{K3iw1 zCS@VOTeqUIiG)_-9zGn``w!M`Gl6sG8nf_1lOo%@k!X7@ZLy*hwE?||acfcj7mL5? zu6seeL8k1?lTFa9H-Gp>>ZN~-367p*LNjL=FRBylk0>~LEqZZ=y#q>J7ZGT% z-s&nKL8`#vGwpYWz0UsO!eK<(FMzs&4v@ahh99$1T4xaiZsT z2O;8CL}LjMAY`c*K759W9DcOPlFOB0vsUV*f1xrl4mL6chC?VLMwsBVDOLipX&GW| zx5<#1b*oLukw-qz=g1l7B-d{>u~i#Qk~R#|LJ5w=BJ+3%&EiJ#ktZaQIGOuq zF#0eY7N@kw&2qG@6!XZyv7ci-*3;HRoB| zgUB|pUL$v@ohJ-Lr&$Ie9l@M#Thm}_cwY}uB))f(8Cz0rzA=1|`60wpjs&eim~>|( zyJ_+xyX0vcHPNJ`hY`Ij`Sj^TySJU_88q$040l?x9q|>CP$`i^3f--A=n1XeW_GZn?i?7H~xXBWwQ=9nN@3ykG&bXms$3n zNwb)$=j)HkF91{2Ug_sPV~u(dk&g^eTXdg=mhGc77Ec8po__sMv%)4c<6zo9%%tCV zv)O&)?E|^LHTfngJ#~FiR4nWq6yJP1{1s1<`6#P25r!yv&-b1mhB9KaNj83=B8KUw z{DU|1b;%4~_OauzBci4Pm_^WOv&^*ygis30R***Amu+x3|7jc8k}pKKJ!>MU5?Smc ze_Pya96QpS5lEYtSFAM?$TE$Fi57Qum=CsWHaA`WO|#&&6(;lF4;Vw7B2YzmsVznt z54;K(ct9BIS%JXv6QLurbRAS6PeX%oN#A*A_%L(NyUWbesf2kK2I57c;9)Rn7@mTe zBS7S{Am%wBcRj?OW)4Dp<ZcAyosex#HxNvct*g$YBihC6advZ(M+}xQpB_bv&N}`ce zWU3ff1-X9rl}F8&iPfw@>JC7SA5;xqF@N0Ij0GD6I7l*j8IRt3vq8e(h_abe);`_Y z22sySn<}bUNqgV~UGdNF`UBJ9EHhB0X0-kI=jc)Xsy_GJ>CLQMWt^7nNQ0qBiv|$A z8F>mIL>PJ%3&04V`p8HZF-a1LaB@f)-;hB_4RYENsI8qK5^Bo!8*eb-A>&NxoX;z< z(Ue^9c~q-g*YqG~NSVF)BR zer!qO4d4EkuYTZWUroiEo^arKxc!T?BmaDH)b?Eq+Q22Yt64lN-qcg-&7SH)Un%Z8 zo3|Qo|2j)(LqS$goYWT(9_!p9))u(2RWqokb~mzULKaww?G{%ByO_Kt!%M#ZAxNUf z4xeH!%spuy0#=FbsDOHhCu%a;cenM5l9tCqd3xRUNexODq;${ry|VoL0m`D?_+2df602y zoe;{)^ijogF&^m8THe%_HD720hCH*8iDBJXmG|6 z$6+I||98_S`SZ8NHJ*z@IeX zag*{Hxh#Z>I?;LOA5C!VSQDJiPExpW>g|64p@dC|4;?hss$ed9ZD5nm)=+ zo_MH3ED1>rH?OWR$w_NzZZfmKbcIAMY%eFhsjL>nB;D`7}c`Jd;U;9yL~{)qadhDafdF(W*ErR+jvgT zsub(%v#!K|4K5UO2avaDZ%NNu} zoE>nTu>Yi8eSpB6XKp=Tyz9z7WvT>M!^^RoqiRiN9Z6$sARp^dZt)c zxpO=N0qSTVZR@29b-5PthHvW(Bz{ut#79&+vwE$`ez3}fkCF%3eE0!H_OCy%Y8&N(h_PFwF+zy6NtTia^ZZ3NkQ8bCr;hfwQuE8n|UE__yt zkRR*fqO!Z2J(gh`~$tU>+FtYM!q=8{ESt;ilx(X)ljhVgi%PG<%m zBU5rLo%b(3%%b9Vch}*~ev^5#ImvrB+XI^A@reN|9}+9b-uNVX<2JA{U@?+qqY-t1 zdU6%-3bbj+4{`fxD+Q4p;^?1%C_Z5y1v4ok4BZ!=sVV+{3>bWp%6h5+-2S1SYX<2= zw3Wd6B4SjhB6V=$!;2xVFq#0*Sci+&$#suLqc~3WXmpJ~ZvyMUnBTSVbIfR2lE( z7cVxk+kS5eCQWXY@~Q%3AQYrSa0a%;XpH5F6D?@M#~x+M=Fhh2?QIxa>9^iCrRSb) z95%MJUVEL19DR(5-SG$O^^)Du{=FZV(z&xujzi5l@A{JoAAO{$`rbF~{4y&)F#aKf zaRQ)sq96K)4mXO4D0M!wambnHkn+nHnbd1Af0|FR9W_t z=A{McDu{6>fKJ{C0*Y{Ux~av>%rxH2m#lMK_iwr7H>R?*)!oj4dU_ZFAvN(>Du1b~ z&E0$ebGxqQAZmhQuMwQRkvxmLOm*J2!0~P!A?#jaUyM!PvUNlpc#;MsTVr3#rL%LT zXWLg2+m1~Ga zB+0l)qFT#NvjyUiewJ-+y$9m1f_j(=u2#SAUZl!a*@^07J1-IBBnV9G#6$>=Jz4s!Ki+=Nv?S`nk7^ueR2NKLLT<(D4 z-D6^nc~pVKeGfuJLo9I!NIBFs7Syf%{JC=j+Vcv5bQK{ys#^PFPn%A3@&FsSb}m8- z@{)y)EQll|A?UQJ=DBdI@wBY9x0pYMnU-(&MF?#RaPp`f71uM>33~r4-qbiV`sxNK zn`g8E2sKJXS1gLt5J5gYo66jYtP0!oc-nEu4A9vC)@5x^FR`WvjF`Igr$wekh#-oCC zUOZ6Ji^bh@0NQD6Fo}2Hv$2f8_^~Ga_A)$S4X8W+ZR($U8Uufe>HOG}-_pq*Jkkv;5qvd)KM-KHX zOaL)3VZ3Sk*7x)7<^p(>5+Wei4y!*t;7e{c+tvcq!mPp(xBR=(}+KZ1*^$E~3Wa z9{!eHodSaMILsk8Clv56_5_p#u``iq$Su57r;NYX@%{>)u7@F*$-r02>8>@n_#YGw zLd92xzdl+L`Bq1J5|5j6wbgr}a>Y1A1c?V8#_gKl8mn{>MbpJum1d$%Pidrwq<}tW zUPp>>+F{xGvrYR=KeZ_&zN#849TE>bWP^(i*}1Nwp%9?FRh@VJ&E$y0hp=sAcX)6| z%INl$4B|ei&ZM$fq}Bw*MbW2yg9q6SSuqS|Yj+5_KuYC!2{{9$(X%6cYaTVr-@`Xa zPC{2<6^JXu)-;-QL%Ru*>!i7S2t@?)!(uz)=mi2HQ-K)FkRc}0r_X}K8*l&IfeH9A zIAek2ExE9Jms6cd1?w2|tIxw6{U-j%GpBUC{`$?b2W5XNsvImWy)6`qe%({oZ)2*F z7*t+D=DTye(rkA&&sw&(jmf>Y;!sIU-IIo5w^wJfVJDaVe5fpP zha->49!dw-m3zYxg62|O!~cc7H5i!Hf+yW{hCS5Y`(;>@$W&be6kE?aMnM`^JCBM)qa%Xi-mI8_S(^b zZyqW6ArXflEyktH!mc7h)k`h_*+A^+_=P_d_>rW6m=G3h$m z=V@~)wtxG3f7NM|9^AAJ$mP#DR|LyS=lCL#jShB|=Gn6-JxHmfgj`>WmQ)-XEHC-9 z+yhF)!#fygZCm|-nSG-1Tz0mJEL}isg@X!-C+%y|4nS+G?Q4Z@&ygu;ym+!acpD82 z&#dR29j6{*YuW8>S4v%WSW|yV^ryl8b+>AcDyd}l>p2*SD{Thv;qG2ae%;QihUb2d z$Mt70*9;701>Q%?=(~;MKlzBRv7?CV1su;$LzR_>daKG`bub>qofYBM)>wIqqGz3E zx5mods{o$TM=U1-5mcm_>6jn+<1Ht^&8YMbNsIh*JOsCJI@#lazAQIwHl46(BFd|B zb$YyzM}>Erw!(XG(t#NBE#zTsHKpf&&g2>!P5eH#@}lZ#r=Jj!w37Qn!v-Jd?^FFq zK8rWsOxIrxQHLjNX*H>Kwy+{OKaxT}8jd9bxS39C@d2mAv(M|){M(Caos7~dDHpIa@M@Ob#+&h52KSN(MUFgYu&2qd^8JqDq&uULE5BHjhz9)IVsBeGs zVEtfoXho%Y7yGvpgJ0=8|7qC;Lqrt*e$_8x2p^PQwtPu0mAsN5p_h$vM+qR|@8q@aXw*JeK;3zYav0Q}u-lP4e-l*pFU8V&b9pCk*U_z%r${h)5(|vh7<;>B(oI&efUr8-HwriY`*Z z6Ywny`25#GDEHX3R_qU}p!S3c(Pk4*n~tqy4_o5kxsRyG52lPuny*P4NYf2-&t0aTm=yDa}M4R?eKj0+GaeC=cY=i7Un;SL(rt~n3;ku3M%#Sy(H``&LW6*RYVgKE-D?4K1MLphM zQu0GzC@@^!1b=1ayOInFV1)?M$rGhy#q8>}RmnXFz!F32^@I)XzuQ#a@HLJZ zeFlU2P*d^MD@~AqW8~ORn3D58Yo&%HP3;}OwCO4GjD?Rp9K*lK5}Vo-a;0A6plXcy z$joCPpqY5O?vN^Nd{tG?`XiA~qw61tzI~4cBC5u2fhk%*dc=?(Nl6brf0RX=kBD2~ zxzr9CsZ{*dg6p^&-~J1`9I=ZRM^Z+Kiq+R%y}oJbSKc!1@qdJ>s~^RU{Fag(a}cf&r`JM$k%DrP z9y(|YX7iC1sW>XucY{vskO$^&Z#N~C{Y+;^>jeb*tW!;d675_2l#X9b60BtRZwgz&O<#fQ;wLgyxa*LIQlkZpoy>DK(;~VH#jG*w*g`BF!k133;mIjrMvTH z?Wx};hEKWy38De>7-=&%kihm^RCsnE4-{P8vndysR~ z2$O!{C0w{qLL}H8sBVpCpJ$T~L&L_~@3M2!ZxK5oSuTqUG;-1jrt}M+GpVPad8T~) z0iSn=4CUt!{)Gy|;N*qb9ZT&AIZ2OIjV9HRU6EVP|n zx0SlUjW#WF;#up7BLJu359uAasEANXf-7iyGl%_=c?i~o0~5U@09xjk*o`!DNia5^ zv8rv`*vlV?M9?3#$HxQ@IZI?e6~`s}BAL8g9y)2mevjwR9BCgHp=UNXDn_%#AMiYq z4F-<$3Rbc9xtT=kRXn;cINdcANsRTp>X0Zm0@tH~)ob+mD^yIm9OeDmTge{>C?=xl zrF3SSnTc8ec%(yT@h$|JkiV5f;6{u#(Frq5TTAEnI&b^kAwg|+BBCyRIsNjhM6rjN z&OiQ%=s3xCYuB2@U+%Z@iqg5~8z07Wij6xonTyRjcrkhgtV=2kzX=hArnto44E-37LD`zMQ1uW4uf@!}#(j!CyFpMFh=U zODv?YwnxUaZ^1pR&mT>}1T|k_1SGR}A(zaK2#&(Rxn$;Or!zT1*$NIPu6yD3(R0r| zQ!0b=%+n@N5*nJx<^1BxvQ6A<=G`nODT=G^RP23tBT`W7rFf6dH1^23A@y>nLS0pN z-FFaK{YAWvlO32MYd8-tfd>gx{F1MGDZ0a@J2ms4e$m(@r6L>zm(=*^NUCRZ>&Mfv z#26oE1FP?V$GJZ97B(&3tH|ha#>4)jOndBx*s9gX;p1kF0~OwoZyvv|@(abd{@sE* z2=&%ACjhS#PkP-m7H@HDp1hG{_uO_KJ+&e!c`l_M)42N zt5(~WUS%q-SYqOT`>ToL4O8}RaNL1*!&A!*-!LV|pKMcDqGG(a1*aR^-qM)GK*J&{;K0fFP*rpFIf$WWyz5VjpZZ zX$;+uNP1|^!gPN_Pi(W0FW{|>8pCirY!ELQKmL@;JASqyfvdXx_FI9~aQ1BFi9wt+ zSYFzslzKZKwcewsNt-JJUM91h`E&SPGQ!HTp^^U1#z>3=1 z7c=wq8Eo}fQLT_Lmx@O^nj8LRJrambZL7_#USkh@D>?5RQ}yj{;<5TKGIC!gz_-Cf zjv+VeqB(4udW93+A2a>|L-_of4K&)+lBZUfils!@gJJ8%(%ujuM9EPkCZ2qv-7BI< zzI0FR7m?>fv5}5g=01PMOZqP}Wm4MoZ*z{=QRv_yf&|p zQwm`@tf3xf`kwUbvy6+Gxy$R|N8)&r)6`gKsK8N z>v5;@@%%t2bcYq&3?)BY#*4baY-|jMz4mBtcyPcsj{YRpAoWU5ek28>!u)`VfZ2?| z$wWYqtHk}3;!dFNvFNywpELODQ!l@4H*&@AzRzTtV{q(f2rAd^C{|>}Aht%%se#w# z)smjW6&#pjnnMp1=jD_TTWwD>gHscdN8YfgM=hE6Yp<_jR*WhJY}GoBmz zCY;|S)uvcfeP=Ro*J`^PyfgoNkGsWsd13&r`TJ|~-*?=#pTu-!ppyYDxZv_F$s{wM zmqf^cAJHk3uCD&}4UTsPT1@xSx37u*mZa9V-s;R;xafzeP`-s)N#}CT}2M#emS+mmk<1MoFdoMaQC)phP zx((e+sz?Q(bS;Jk%S&QY+iN?2d#BB{(mr+V6wI#1?~RU`dgcX?6n*bsubJ5I?nLDp z2%s9-&wi-!j~qcN{2g$dGScS{v^iQuAgD`Ai~!b(1HSVc4>d%aFA4Um{qeqXluQd8 zSJ|*?=ff`&Po<8>jrj<|1wL47e5u4`ao0T;McdC&prMw>pL)2wG&C_#$`LfH-$C+{ z&O^jkZeZZjhJpSrI|Yezp%$ex$2ep$dr(ojq$J!nR0;hR(WQ4hGGiqM)`B!_^+v&R|!m`w) z0@<^DoGr_K(L7huQ`T$3M47OX4I?KWZ?kXPzWoC`VeR=)x_TOyZ2Yf(x2Yu!r+pSx z3ZN-=f|^x&-sd>}r;lm>(ak2k@+}iN@&r>d`}3ylJ3q9NhTJMk6nJ&=0)n`~ zz@`3?!+$I;#wdC%;9<`O+Id_9106 zA^(I#dD+6yp#DEoa7^}t*T-V89a;NM0*zbl!Mis>Y6(t<{^|CPQ}Afr=gJllx^e;y zKQN>?foQIaaKu2!c(77Bs1s@0*|Sd#ADIlP#-@wiW_Dsm@O(*%l$PD^V5{joGM!l_ zBHI;uf~HW${wk&Y%%7b~kvMj`*zJp?oHUCd2W;4Q43wByw&Kf{B57XRDH0Dp{F&JA ze%Gy5&yQqq*YyXU^+;?!*|Wr;oJcEEclsQ6G0Mp;%VeJ zA<2WQMBMjMMEU$=!W>UdY)zbnqM(xs4Y5+hH#iiGy&`he#(s6X-JkBSVn-qIk}N6m zC13cQsrdSr4X$Bq52EDK$#D-yIp8`=vY}3O7cfI(rgAdLi#9>BY|eRP1OLn}GQhBP zz$ohi6_CB;7v(xShGt)UX(~GdU+a=)`H}7Vmo~imaarv){0NH>jy)V0+0Zo-BJ4On zNqOC?I54?h`@K``@tZ;e2i@r{jkYL=W;!{X8sEPcMi2awEVL1k>?%q2MjW;!n5NXL43Tp&S$_8W%v>f@?!^|h?aF>7 zAycot6^CclhLwmpC&&+vp3QG%{@!X8d#k(;_ceX{0m~h6&*6@-1aSvPbj^EG<1LB@n`=~ z5-vYc&v`k8+<(eZxK7FyOx&1C#_mtP{`Sd!Qf!E{kH*PxJ?F}PRVoG3wMeqt@iRYd z19ocr8vn#`#>YV!sg}+MU6&JMs32}g_3N~|(-vI!);`&7W5tVF@uDOVV#8_qw=cye zvkn_>Jhf=}7`@qL?|rSvP~osmjOhy6rZG`zZ^e?fcIN(^2Z?ZsFFRl6g#6!2gUPRZ zZ}s>6o-!Ncyy&j7`*mc^9*eaAb*Qo{k66B_5p+sBGDepru%|S5sZ)t3{sD0e%u5!D zZhZ4PqDMeZV|Vxu7-?#L|Eo>pa8<@`xveC_Mk|}~%UN35OCnE~+SLJYl0@ZKTxQ+H z%F)t61cIf4XtwXdfLOl61EjB8`Y8O}-F(D-WSiy+zX7|uXFil`j9-MFJ75HE5?(AK zCtE)mXp23c+1_%PReO@jPlf`%j|Z#5kLjR^?Bh@K>sckZyGg^zX6B?zOB;*lSS7NZ z*Q88*3#lJJ52KRxik_^I2rKpGGSmL-3%_-0st@(En=->WTcPbO%gU>(e`UFFX&FC; z4swYzcP;FmX0L^p5-Vp(FSyd9x-tj61TU>aC6_%MU&;jQxdMO4nfe*>WlwdSV^Nu)kxdY5VR?oMYP0szP$*3P}REFT)=@%&J@x zrK#l~82^uN8RzX+Gs6i7ACc72;R{N7lsnk|OdD|y5sQrXIMTzaVI(T%nu%v<4AQ-& z+U+O@n2--a=+PeAJr}Baad>$+fWW$bG?$O4c(5VT8#bEYmh}(_V`bctocmdr(K>mX zWQ503sXtW>>R0U@ij)Nj!tF%)*}!GOktSb7GBh_!^1ELQIaGoJ`k8Po9;{UQ zXPGq{=3A)&OjDWc5??g*jKePu7M&Q~M(`hf)F^beR`;JE_nY+__uQCjJdfxuFybY5?G`Z0D_q4KkIieh%<8q>kaCH`2`H$6iJwufr! zA8mj3rKj5l4mdK(&q0g8Ft)AhPl2MeQt2Ur~l?r4H(ingejFyMcfZ@ zSelF{2l8=cuj^-JCrbkt{-}!un~wIGI>Gp>F^Cy9o2AxgR;{}wzGC%-ery7=bE{HW z_r}t}W3E8R2my@m)#vySGtof!Pi;QmAWpo#(RzG@NUpmOgZuN$^Vf|3L73eN-sP#J zJGYK$9mqbn&!pF^JIBT!w8@BuK4Vt!d*6my^QDDA9A^R%LWW>If};8IyAL=! z@s_z}aMtTTNhg^|P4szu|M0lp4WbmAizU^ufLh&$V$~eY3}&1ZfV|$fiQ&A1Bm6+^ z6(JSnlx*L={k_r3%~JJ0jKZ+NyDW!!kUOVAalxNxZryX+;skyl?o>QUnzf3ZJqrea zBcD9Z+6&qDSLJsE4j;;SyLe!9&>S0^k`F!3UXHCMR~qGX!ao13su_5Vk3+-)Lk}?h z#!WbN(=*R)h(GpZnHORZfds~lv8q&w^1CqRv#Zyd*qwLsYgL~yQGPchw|>3NK-HcV zTkIrI^V(WVt=n%mfX+XdAGZkGS6U;=+A*K52R^(H0xqEmlWA--KF%JsKm7%ghvOV6 z<@0|I^@(#b*q6R){ZikymR|;a?y3xtZU@IqMA|D10C(;8eW9|g%E>n3a{aH28SfPUYwnN^(%hoAMh?6P`vcv zziS^$i6Mwhd>BQrvp?Fz#xphP50Wp{Tfy7W=``Va(gal) zj8v$~>#pH|296~qv$T{5xr@OmJ4D<+2V%=}qEvMc1~!6zV{KLV6RbTd1ntxkSd0eS z`t+;(SYvDJYhH-TMHknv-dfdc{8)RmkblHbQ@;3O>+TJ6Y^nN6KKD;6!PJyhwXk)< z2aZqMAI#}hgn+21Sm`w!p{fPx&GyPeTj!op3Xo5wg8WPrZtenc=J^HG)>Y(=fh_Vx z?Pmy*25N9VhyyYOb&`c*EvjYQv3a6PcEGr80BET{)R4fon$l^vLX?`>C#y~24eb(zsS#Dey zS3CQ3#M!N?YR6@7U^sS_zFnX8{X?H_kB-tBhDQy@0~sBjczgMUd46OGg40nwkIE9x zLzOv(_{0&f-5H!R4|K3aq_QmEtYEFVxz61k%`JDL_S<+*X2beRL*vKY$l-=i(T7*; zF|81%=Hd63_2TB$pyK8OolGMykjAMaAu+z!l2GO$z%@iFq!3%dKpEWnNdg_HAW5SR z;DwE4B_RyyG@BSmrHHo~^T0Z_#96haualH_BEL)VfPVc{KgGuC*&T#RXcSNsG}Lc~ z$UfmPx)l&?4a8atglI!x<`<@%?E1a$Utsj;u6`djh^iIxk@&Pru@b;_UxJQq&aWK5 zF5AC} zXUWS*)Fa&C^ozyaI$zY&B*~Egc_IuEOUoj=*Y$i5tSI{|zxQ!dI+-~Q@6gA9%Q$ZH zI~BGY3nxBp2Ig;(TK-sI+}OKG7vE^!AQ;Iy6iezgPPrugCopVyL2nlzWRbbo^Dbi3 zg$H^4yX9dj<`Kztw+~x$q^T+h-oFyK@N9nWT;OwKYnlyGxWV-Rec2$W z)tJ;TM{Hu`8}fj1%1vtA&H)S9F+7_=Z*#CIeBp2zuFjFT*lU=K!(nrqJgMX=)+4K| zW-%3y3zf0iD#OX`zA58PpC=wPiASC^4ac8u&o{HNrLIZuti$gS)fnsUioMz$L4k9C z&cHQ$05I7loTWDjF2 z*8F$gO94QP4(9Z$WUq?9AjbC40?(!WZth|yz|TMg{r5<3ly`ys3={1sfW?6oiG#-N zuPBw3JsBP{_@wx{O>sAeb^a?zKTL+HEwrgaAQW|#kJ==& zQ@kC?_F%uNYjdICwHTlaU@VRD((DTd(ZV}A5?mR6#c}r>m;DalyB3^hRis+=RIv)T z@(=X*tNI{{%Q?6IlMgkx)p+4 za!?19IBE;^^ei*zUfq6H27`8H@&MlpFy7^<58(o1@DSAW^7|$^oDKzS&W~XixbFst zs*grt)ybWP1MZ2r6_|+tW|JYicd*m*|JfICD$hR{tKX9axV;pqE~q6x&$2m5>QpYn zNhdDi-RG<3eaFxW>!IZ#9scUbzyaU2hg&16_Ev<9-dy&LNZ-EW(y7FcGfge?Er39< zt|GujLp3=z-gxBkttn&AcK8=L)V4G~j$}P;T@Ua6gQBE5ZPrWTq9oG)vqgaCr8heb zF(>=w|JfP+UpsL01faZ^mypSF)FQGw&M>%CNVz@I5v`iRUnIfJ*b<|0!+lqtoBZ{C zo4ehl9}FZ(3%}4G3C-dtn0wV{o^y(8=)XJ3Q^Kx%hyvp^Z)oxiN*+_tRQCSrM$-E> zu>DW{{~7|a1^1yKu;EhZy;fqddywB-ah8L#onFUz8+GeNSonkF_HVH2k+|8v%h8Y! z)&ql>;*`K=$v%CS3zOM>_6dGLX(|Vcje|n?iAa8H+u>)IIIO{k`jXEJ+q;z7he_^# jWIqIUfdG1o>z1s3{jf#VH(a2*Cw%e`Pcm)Kve|Z literal 0 HcmV?d00001 From 8c2201ec39d40632bcea80706324cfb8dfd53f99 Mon Sep 17 00:00:00 2001 From: Igor Egorov Date: Tue, 31 Jul 2018 12:23:38 +0300 Subject: [PATCH 93/97] Setup docker for remote debugging (#1617) Add gdbserver to docker dependencies. Modify docker security options for remote debugging. Signed-off-by: Igor Egorov --- docker/dependencies/Dockerfile | 2 +- docker/develop/Dockerfile | 2 +- docker/docker-compose.yml | 2 ++ 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/docker/dependencies/Dockerfile b/docker/dependencies/Dockerfile index fb5a5a1c2a..0cd8972b58 100644 --- a/docker/dependencies/Dockerfile +++ b/docker/dependencies/Dockerfile @@ -30,7 +30,7 @@ RUN set -e; \ # SWIG dependencies libpcre3-dev autoconf bison \ # other - wget curl file gdb ccache \ + wget curl file gdb gdbserver ccache \ gcovr cppcheck doxygen graphviz graphviz-dev unzip zip; \ apt-get -y clean diff --git a/docker/develop/Dockerfile b/docker/develop/Dockerfile index 5c156fe706..7a2dd169b2 100644 --- a/docker/develop/Dockerfile +++ b/docker/develop/Dockerfile @@ -30,7 +30,7 @@ RUN set -e; \ # SWIG dependencies libpcre3-dev autoconf bison \ # other - wget curl file gdb ccache \ + wget curl file gdb gdbserver ccache \ gcovr cppcheck doxygen rsync graphviz graphviz-dev unzip zip; \ apt-get -y clean diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 95803e4084..504636b84b 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -24,6 +24,8 @@ services: working_dir: /opt/iroha cap_add: - SYS_PTRACE + security_opt: + - seccomp:unconfined postgres: image: postgres:9.5 From 2c65ff2bec6d5a48a78a9926f7a5511bac71842e Mon Sep 17 00:00:00 2001 From: Fedor Muratov Date: Tue, 31 Jul 2018 14:50:54 +0300 Subject: [PATCH 94/97] Fix/batch any range (#1620) * Fix linkage errors: add stateless validation target if required * Rework transactionBatch::calculateReducedBatchHash with template instead of any_range Add toString method * Remove unnecessary header Signed-off-by: Fedor Muratov --- irohad/main/CMakeLists.txt | 1 + shared_model/builders/protobuf/CMakeLists.txt | 1 + .../iroha_internal/transaction_batch.cpp | 31 ++++++++++--------- .../iroha_internal/transaction_batch.hpp | 17 ++++++++-- .../iroha_internal/transaction_sequence.cpp | 1 + .../irohad/consensus/yac/CMakeLists.txt | 1 + .../module/irohad/synchronizer/CMakeLists.txt | 3 +- .../shared_model/backend_proto/CMakeLists.txt | 1 + 8 files changed, 36 insertions(+), 20 deletions(-) diff --git a/irohad/main/CMakeLists.txt b/irohad/main/CMakeLists.txt index 9590c017a9..76f7f73b67 100644 --- a/irohad/main/CMakeLists.txt +++ b/irohad/main/CMakeLists.txt @@ -28,6 +28,7 @@ add_library(raw_block_loader impl/raw_block_loader.cpp) target_link_libraries(raw_block_loader shared_model_interfaces shared_model_proto_backend + shared_model_stateless_validation logger ) diff --git a/shared_model/builders/protobuf/CMakeLists.txt b/shared_model/builders/protobuf/CMakeLists.txt index 747a7b4929..f0817979d8 100644 --- a/shared_model/builders/protobuf/CMakeLists.txt +++ b/shared_model/builders/protobuf/CMakeLists.txt @@ -22,4 +22,5 @@ target_link_libraries( shared_model_proto_builders shared_model_interfaces shared_model_proto_backend + shared_model_stateless_validation ) diff --git a/shared_model/interfaces/iroha_internal/transaction_batch.cpp b/shared_model/interfaces/iroha_internal/transaction_batch.cpp index 654539731f..8c0dec47ea 100644 --- a/shared_model/interfaces/iroha_internal/transaction_batch.cpp +++ b/shared_model/interfaces/iroha_internal/transaction_batch.cpp @@ -4,6 +4,7 @@ */ #include "interfaces/iroha_internal/transaction_batch.hpp" +#include "utils/string_builder.hpp" #include "validators/field_validator.hpp" #include "validators/transaction_validator.hpp" #include "validators/transactions_collection/batch_order_validator.hpp" @@ -30,13 +31,13 @@ namespace shared_model { } return std::none_of(++txs.begin(), - txs.end(), - [front_batch_meta = batch_meta.value()]( - const std::shared_ptr tx) { - return tx->batchMeta() - ? **tx->batchMeta() != *front_batch_meta - : false; - }); + txs.end(), + [front_batch_meta = batch_meta.value()]( + const std::shared_ptr tx) { + return tx->batchMeta() + ? **tx->batchMeta() != *front_batch_meta + : false; + }); }; template @@ -123,14 +124,14 @@ namespace shared_model { }); } - types::HashType TransactionBatch::calculateReducedBatchHash( - const boost::any_range - &reduced_hashes) { - std::stringstream concatenated_hash; - for (const auto &hash : reduced_hashes) { - concatenated_hash << hash.hex(); - } - return types::HashType::fromHexString(concatenated_hash.str()); + std::string TransactionBatch::toString() const { + return detail::PrettyStringBuilder() + .init("Batch") + .append("reducedHash", reducedHash().toString()) + .append("hasAllSignatures", hasAllSignatures() ? "true" : "false") + .append("transactions") + .appendAll(transactions(), [](auto &tx) { return tx->toString(); }) + .finalize(); } } // namespace interface diff --git a/shared_model/interfaces/iroha_internal/transaction_batch.hpp b/shared_model/interfaces/iroha_internal/transaction_batch.hpp index 9f113db703..30da44b513 100644 --- a/shared_model/interfaces/iroha_internal/transaction_batch.hpp +++ b/shared_model/interfaces/iroha_internal/transaction_batch.hpp @@ -15,7 +15,6 @@ namespace shared_model { class TransactionBatch { public: - /** * Create transaction batch out of collection of transactions * @tparam TransactionValidator validates every single transaction @@ -68,15 +67,27 @@ namespace shared_model { */ bool hasAllSignatures() const; + /** + * @return string representation of the object + */ + std::string toString() const; + /** * Get the concatenation of reduced hashes as a single hash * That kind of hash does not respect batch type + * @tparam Collection type of const ref iterator * @param reduced_hashes * @return concatenated reduced hashes */ + template static types::HashType calculateReducedBatchHash( - const boost::any_range - &reduced_hashes); + const Collection &reduced_hashes) { + std::stringstream concatenated_hash; + for (const auto &hash : reduced_hashes) { + concatenated_hash << hash.hex(); + } + return types::HashType::fromHexString(concatenated_hash.str()); + } private: explicit TransactionBatch( diff --git a/shared_model/interfaces/iroha_internal/transaction_sequence.cpp b/shared_model/interfaces/iroha_internal/transaction_sequence.cpp index ce775752f8..025413a28e 100644 --- a/shared_model/interfaces/iroha_internal/transaction_sequence.cpp +++ b/shared_model/interfaces/iroha_internal/transaction_sequence.cpp @@ -10,6 +10,7 @@ #include "validators/transaction_validator.hpp" #include "validators/transactions_collection/batch_order_validator.hpp" + namespace shared_model { namespace interface { diff --git a/test/module/irohad/consensus/yac/CMakeLists.txt b/test/module/irohad/consensus/yac/CMakeLists.txt index 3cf40ea7c0..811f962b4e 100644 --- a/test/module/irohad/consensus/yac/CMakeLists.txt +++ b/test/module/irohad/consensus/yac/CMakeLists.txt @@ -84,6 +84,7 @@ addtest(yac_hash_provider_test yac_hash_provider_test.cpp) target_link_libraries(yac_hash_provider_test yac shared_model_cryptography + shared_model_stateless_validation ) addtest(yac_common_test yac_common_test.cpp) diff --git a/test/module/irohad/synchronizer/CMakeLists.txt b/test/module/irohad/synchronizer/CMakeLists.txt index 2c2d7b48da..f91ea0ff3f 100644 --- a/test/module/irohad/synchronizer/CMakeLists.txt +++ b/test/module/irohad/synchronizer/CMakeLists.txt @@ -3,6 +3,5 @@ target_link_libraries(synchronizer_test synchronizer shared_model_cryptography shared_model_proto_backend + shared_model_stateless_validation ) - - diff --git a/test/module/shared_model/backend_proto/CMakeLists.txt b/test/module/shared_model/backend_proto/CMakeLists.txt index c06c3dac7a..52467ec253 100644 --- a/test/module/shared_model/backend_proto/CMakeLists.txt +++ b/test/module/shared_model/backend_proto/CMakeLists.txt @@ -27,6 +27,7 @@ addtest(shared_proto_tx_response_test ) target_link_libraries(shared_proto_tx_response_test shared_model_proto_backend + shared_model_stateless_validation ) addtest(shared_proto_transaction_test From d474d45e598d4b6637e8a54aabfe6376f5614d0f Mon Sep 17 00:00:00 2001 From: Igor Egorov Date: Wed, 1 Aug 2018 18:53:00 +0300 Subject: [PATCH 95/97] Fix non-determinism in client test (#1619) * Fix non-determinism in client test Non-determinism was introduced by status bus thread. On slow machines getTxStatus may not return expected status immediately. Additional read attempt may be required. Signed-off-by: Igor Egorov --- test/module/iroha-cli/client_test.cpp | 46 +++++++++++++++---- .../irohad/torii/torii_service_test.cpp | 13 +++++- 2 files changed, 48 insertions(+), 11 deletions(-) diff --git a/test/module/iroha-cli/client_test.cpp b/test/module/iroha-cli/client_test.cpp index 0e08a1f9b8..2a40e74e1f 100644 --- a/test/module/iroha-cli/client_test.cpp +++ b/test/module/iroha-cli/client_test.cpp @@ -46,6 +46,17 @@ using namespace std::chrono_literals; constexpr std::chrono::milliseconds initial_timeout = 1s; constexpr std::chrono::milliseconds nonfinal_timeout = 2 * 10s; +/** +Here we imitate the behavior of StatusStram client but on a bit lower level. So +the do-while cycle imitates client resubscription to the stream. Stream +"expiration" is a valid designed case (see pr #1615 for the details). + +The number of attempts (3) is a magic constant here. The idea behind this number +is the following: only one resubscription is usually enough to pass the test; if +three resubscribes were not enough, then most likely there is another bug. + */ +constexpr uint32_t status_read_attempts = 3; + class ClientServerTest : public testing::Test { public: virtual void SetUp() { @@ -183,12 +194,21 @@ TEST_F(ClientServerTest, SendTxWhenStatelessInvalid) { ASSERT_EQ(iroha_cli::CliClient(ip, port).sendTx(shm_tx).answer, iroha_cli::CliClient::OK); - auto tx_hash = shm_tx.hash(); - auto res = iroha_cli::CliClient(ip, port).getTxStatus( - shared_model::crypto::toBinaryString(tx_hash)); - ASSERT_EQ(res.answer.tx_status(), + auto getAnswer = [&]() { + return iroha_cli::CliClient(ip, port) + .getTxStatus(shared_model::crypto::toBinaryString(shm_tx.hash())) + .answer; + }; + decltype(getAnswer()) answer; + auto read_attempt_counter(status_read_attempts); + do { + answer = getAnswer(); + } while (answer.tx_status() + != iroha::protocol::TxStatus::STATELESS_VALIDATION_FAILED + and --read_attempt_counter); + ASSERT_EQ(answer.tx_status(), iroha::protocol::TxStatus::STATELESS_VALIDATION_FAILED); - ASSERT_NE(res.answer.error_message().size(), 0); + ASSERT_NE(answer.error_message().size(), 0); } /** @@ -239,10 +259,18 @@ TEST_F(ClientServerTest, SendTxWhenStatefulInvalid) { "index '2' did not pass verification with " "error 'CommandError'"; - // check it really failed with specific message - auto answer = - client.getTxStatus(shared_model::crypto::toBinaryString(tx.hash())) - .answer; + auto getAnswer = [&]() { + return client.getTxStatus(shared_model::crypto::toBinaryString(tx.hash())) + .answer; + }; + decltype(getAnswer()) answer; + auto read_attempt_counter(status_read_attempts); + do { + // check it really failed with specific message + answer = getAnswer(); + } while (answer.tx_status() + != iroha::protocol::TxStatus::STATEFUL_VALIDATION_FAILED + and --read_attempt_counter); ASSERT_EQ(answer.tx_status(), iroha::protocol::TxStatus::STATEFUL_VALIDATION_FAILED); ASSERT_EQ(answer.error_message(), stringified_error); diff --git a/test/module/irohad/torii/torii_service_test.cpp b/test/module/irohad/torii/torii_service_test.cpp index 7af07249d6..0ad77bb5f4 100644 --- a/test/module/irohad/torii/torii_service_test.cpp +++ b/test/module/irohad/torii/torii_service_test.cpp @@ -34,7 +34,16 @@ using namespace iroha::torii; using namespace std::chrono_literals; constexpr std::chrono::milliseconds initial_timeout = 1s; constexpr std::chrono::milliseconds nonfinal_timeout = 2 * 10s; -constexpr unsigned resubscribe_attempts = 3; + +/** +The do-while cycle imitates client resubscription to the stream. Stream +"expiration" is a valid designed case (see pr #1615 for the details). + +The number of attempts (3) is a magic constant here. The idea behind this number +is the following: only one resubscription is usually enough to pass the test; if +three resubscribes were not enough, then most likely there is another bug. + */ +constexpr uint32_t resubscribe_attempts = 3; using iroha::Commit; @@ -409,7 +418,7 @@ TEST_F(ToriiServiceTest, StreamingFullPipelineTest) { // (Committed in this case) will be received. We start request before // transaction sending so we need in a separate thread for it. std::thread t([&] { - unsigned resub_counter(resubscribe_attempts); + auto resub_counter(resubscribe_attempts); iroha::protocol::TxStatusRequest tx_request; tx_request.set_tx_hash(txhash); do { From f2ba2870a18738af505dc0c4ffbc292cdb4a151e Mon Sep 17 00:00:00 2001 From: Igor Egorov Date: Thu, 2 Aug 2018 14:17:34 +0300 Subject: [PATCH 96/97] Add blockstore cleanup to regression test (#1624) This will prevent cyclic test case fail when the first run was failed for some reason. Signed-off-by: Igor Egorov --- test/regression/regression_test.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/test/regression/regression_test.cpp b/test/regression/regression_test.cpp index 770d283480..02b4d06460 100644 --- a/test/regression/regression_test.cpp +++ b/test/regression/regression_test.cpp @@ -18,6 +18,7 @@ #include #include "builders/protobuf/queries.hpp" #include "builders/protobuf/transaction.hpp" +#include "common/files.hpp" #include "cryptography/crypto_provider/crypto_defaults.hpp" #include "framework/integration_framework/integration_test_framework.hpp" #include "framework/specified_visitor.hpp" @@ -120,6 +121,13 @@ TEST(RegressionTest, StateRecovery) { (boost::filesystem::temp_directory_path() / "iroha-state-recovery-test") .string(); const std::string dbname = "dbstatereq"; + + // Cleanup blockstore directory, because it may contain blocks from previous + // test launch if ITF was failed for some reason. If there are some blocks, + // then checkProposal will fail with "missed proposal" error, because of + // incorrect calculation of chain height. + iroha::remove_dir_contents(path); + { integration_framework::IntegrationTestFramework( 1, dbname, [](auto &) {}, false, path) From 67c450e3002351cc3ddeb51142c92cb067021057 Mon Sep 17 00:00:00 2001 From: Fedor Muratov Date: Thu, 2 Aug 2018 15:17:22 +0300 Subject: [PATCH 97/97] Fix/query signatures (#1625) * Fix query validators; Add descriptions for types Add missed signed block query validator Rework transaction and query builder with default signed validator Signed-off-by: Fedor Muratov * Fix typo in validator using name Signed-off-by: Igor Egorov * Make validators naming consistent Signed-off-by: Igor Egorov * Use unsined validators where it is required Signed-off-by: Igor Egorov * add test for query signature Signed-off-by: Fedor Muratov * Add review fixes: * clean up code * remove normal case * add additional check for stateless error Signed-off-by: Fedor Muratov --- irohad/torii/impl/command_service.cpp | 2 +- irohad/torii/impl/query_service.cpp | 4 +- shared_model/bindings/client_api.cpp | 2 +- shared_model/builders/protobuf/block.hpp | 8 +- .../builder_templates/block_template.hpp | 2 +- .../blocks_query_template.hpp | 2 +- .../builder_templates/query_template.hpp | 2 +- shared_model/validators/default_validator.hpp | 98 +++++++++++++++---- .../protobuf/transport_builder_test.cpp | 8 +- .../validators/query_validator_test.cpp | 2 +- test/regression/CMakeLists.txt | 7 ++ test/regression/query_test.cpp | 69 +++++++++++++ 12 files changed, 170 insertions(+), 36 deletions(-) create mode 100644 test/regression/query_test.cpp diff --git a/irohad/torii/impl/command_service.cpp b/irohad/torii/impl/command_service.cpp index 42e109d6ce..9dd6dbf806 100644 --- a/irohad/torii/impl/command_service.cpp +++ b/irohad/torii/impl/command_service.cpp @@ -78,7 +78,7 @@ namespace torii { void CommandService::Torii(const iroha::protocol::Transaction &request) { shared_model::proto::TransportBuilder< shared_model::proto::Transaction, - shared_model::validation::DefaultSignableTransactionValidator>() + shared_model::validation::DefaultSignedTransactionValidator>() .build(request) .match( [this]( diff --git a/irohad/torii/impl/query_service.cpp b/irohad/torii/impl/query_service.cpp index 6c7cfa4741..d44b7ca055 100644 --- a/irohad/torii/impl/query_service.cpp +++ b/irohad/torii/impl/query_service.cpp @@ -42,7 +42,7 @@ namespace torii { shared_model::proto::TransportBuilder< shared_model::proto::Query, - shared_model::validation::DefaultQueryValidator>() + shared_model::validation::DefaultSignableQueryValidator>() .build(request) .match( [this, &hash, &response]( @@ -80,7 +80,7 @@ namespace torii { log_->debug("Fetching commits"); shared_model::proto::TransportBuilder< shared_model::proto::BlocksQuery, - shared_model::validation::DefaultBlocksQueryValidator>() + shared_model::validation::DefaultSignableBlocksQueryValidator>() .build(*request) .match( [this, context, request, writer]( diff --git a/shared_model/bindings/client_api.cpp b/shared_model/bindings/client_api.cpp index e989bd95cf..fbaf0cdf49 100644 --- a/shared_model/bindings/client_api.cpp +++ b/shared_model/bindings/client_api.cpp @@ -31,7 +31,7 @@ namespace shared_model { void validateTransaction(const Blob &b) { auto blob = convert(b); auto s = get(blob) | [](auto tx) { - static validation::DefaultSignableTransactionValidator val; + static validation::DefaultSignedTransactionValidator val; return boost::make_optional( val.validate(proto::Transaction(tx)).reason()); }; diff --git a/shared_model/builders/protobuf/block.hpp b/shared_model/builders/protobuf/block.hpp index fb6b44a925..56cb8445b6 100644 --- a/shared_model/builders/protobuf/block.hpp +++ b/shared_model/builders/protobuf/block.hpp @@ -25,10 +25,10 @@ namespace shared_model { using BlockBuilder = TemplateBlockBuilder<>; - using UnsignedBlockBuilder = - TemplateBlockBuilder<0, - shared_model::validation::DefaultBlockValidator, - shared_model::proto::Block>; + using UnsignedBlockBuilder = TemplateBlockBuilder< + 0, + shared_model::validation::DefaultUnsignedBlockValidator, + shared_model::proto::Block>; } // namespace proto } // namespace shared_model diff --git a/shared_model/builders/protobuf/builder_templates/block_template.hpp b/shared_model/builders/protobuf/builder_templates/block_template.hpp index e8126847c7..91b0777123 100644 --- a/shared_model/builders/protobuf/builder_templates/block_template.hpp +++ b/shared_model/builders/protobuf/builder_templates/block_template.hpp @@ -40,7 +40,7 @@ namespace shared_model { * @tparam BT -- build type of built object returned by build method */ template > class DEPRECATED TemplateBlockBuilder { private: diff --git a/shared_model/builders/protobuf/builder_templates/blocks_query_template.hpp b/shared_model/builders/protobuf/builder_templates/blocks_query_template.hpp index 5da3b909bb..18757ae5d0 100644 --- a/shared_model/builders/protobuf/builder_templates/blocks_query_template.hpp +++ b/shared_model/builders/protobuf/builder_templates/blocks_query_template.hpp @@ -26,7 +26,7 @@ namespace shared_model { * @tparam BT -- build type of built object returned by build method */ template > class DEPRECATED TemplateBlocksQueryBuilder { private: diff --git a/shared_model/builders/protobuf/builder_templates/query_template.hpp b/shared_model/builders/protobuf/builder_templates/query_template.hpp index 7831d5800f..703739e027 100644 --- a/shared_model/builders/protobuf/builder_templates/query_template.hpp +++ b/shared_model/builders/protobuf/builder_templates/query_template.hpp @@ -27,7 +27,7 @@ namespace shared_model { * @tparam BT -- build type of built object returned by build method */ template > class DEPRECATED TemplateQueryBuilder { private: diff --git a/shared_model/validators/default_validator.hpp b/shared_model/validators/default_validator.hpp index fb5b444d30..2ee1428ede 100644 --- a/shared_model/validators/default_validator.hpp +++ b/shared_model/validators/default_validator.hpp @@ -33,45 +33,103 @@ namespace shared_model { namespace validation { + + // -----------------------| Transaction validation |------------------------ + + /** + * Transaction validator which checks stateless validation WITHOUT + * signatures + */ using DefaultTransactionValidator = TransactionValidator>; - using DefaultQueryValidator = + /** + * Transaction validator which checks stateless validation + */ + using DefaultSignedTransactionValidator = + SignableModelValidator; + + // --------------------------| Query validation |--------------------------- + + /** + * Query validator which checks stateless validation WITHOUT signatures + */ + using DefaultUnsignedQueryValidator = QueryValidator>; - using DefaultBlocksQueryValidator = BlocksQueryValidator; + /** + * Block query validator checks stateless validation WITHOUT signatures + */ + using DefaultUnsignedBlocksQueryValidator = + BlocksQueryValidator; - using DefaultProposalValidator = ProposalValidator< - FieldValidator, - DefaultTransactionValidator, - UnsignedTransactionsCollectionValidator>; + /** + * Query validator which checks stateless validation including signatures + */ + using DefaultSignableQueryValidator = + SignableModelValidator; + + /** + * Block query validator which checks stateless validation including + * signatures + */ + using DefaultSignableBlocksQueryValidator = + SignableModelValidator; + + // --------------------------| Block validation |--------------------------- - using DefaultBlockValidator = BlockValidator< + /** + * Block validator which checks blocks WITHOUT signatures + */ + using DefaultUnsignedBlockValidator = BlockValidator< FieldValidator, DefaultTransactionValidator, SignedTransactionsCollectionValidator>; + /** + * Block validator which checks blocks including signatures + */ + using DefaultSignableBlockValidator = + SignableModelValidator; + + /** + * @deprecated + * In https://soramitsu.atlassian.net/browse/IR-1418 should be removed + */ using DefaultEmptyBlockValidator = EmptyBlockValidator; + /** + * @deprecated + * In https://soramitsu.atlassian.net/browse/IR-1418 should be removed + */ using DefaultAnyBlockValidator = - AnyBlockValidator; + AnyBlockValidator; - using DefaultSignableTransactionValidator = - SignableModelValidator; + // ------------------------| Proposal validation |-------------------------- - using DefaultSignableQueryValidator = - SignableModelValidator; + /** + * Proposal validator which checks stateless validation of proposal + */ + using DefaultProposalValidator = ProposalValidator< + FieldValidator, + DefaultTransactionValidator, + UnsignedTransactionsCollectionValidator>; - using DefaultSignableBlockValidator = - SignableModelValidator; + // -----------------| Transaction collection validation |------------------- + /** + * Check sequence of transactions without signatures + */ using DefaultUnsignedTxCollectionValidator = UnsignedTransactionsCollectionValidator; diff --git a/test/module/shared_model/builders/protobuf/transport_builder_test.cpp b/test/module/shared_model/builders/protobuf/transport_builder_test.cpp index 4750d2cf01..f24e90bc56 100644 --- a/test/module/shared_model/builders/protobuf/transport_builder_test.cpp +++ b/test/module/shared_model/builders/protobuf/transport_builder_test.cpp @@ -218,7 +218,7 @@ class TransportBuilderTest : public ::testing::Test { */ TEST_F(TransportBuilderTest, TransactionCreationTest) { auto orig_model = createTransaction(); - testTransport( + testTransport( orig_model, [&orig_model](const Value &model) { ASSERT_EQ(model.value.getTransport().SerializeAsString(), @@ -237,7 +237,7 @@ TEST_F(TransportBuilderTest, TransactionCreationTest) { */ TEST_F(TransportBuilderTest, InvalidTransactionCreationTest) { auto orig_model = createInvalidTransaction(); - testTransport( + testTransport( orig_model, [](const Value) { FAIL(); }, [](const Error &) { SUCCEED(); }); @@ -283,7 +283,7 @@ TEST_F(TransportBuilderTest, InvalidQueryCreationTest) { */ TEST_F(TransportBuilderTest, BlockCreationTest) { auto orig_model = createBlock(); - testTransport( + testTransport( orig_model, [&orig_model](const Value &model) { ASSERT_EQ(model.value.getTransport().SerializeAsString(), @@ -299,7 +299,7 @@ TEST_F(TransportBuilderTest, BlockCreationTest) { */ TEST_F(TransportBuilderTest, InvalidBlockCreationTest) { auto orig_model = createInvalidBlock(); - testTransport( + testTransport( orig_model, [](const Value> &) { FAIL(); }, [](const Error &) { SUCCEED(); }); diff --git a/test/module/shared_model/validators/query_validator_test.cpp b/test/module/shared_model/validators/query_validator_test.cpp index af5f31fb0f..0f89fad225 100644 --- a/test/module/shared_model/validators/query_validator_test.cpp +++ b/test/module/shared_model/validators/query_validator_test.cpp @@ -21,7 +21,7 @@ class QueryValidatorTest : public ValidatorsTest { public: - shared_model::validation::DefaultQueryValidator query_validator; + shared_model::validation::DefaultUnsignedQueryValidator query_validator; }; using namespace shared_model; diff --git a/test/regression/CMakeLists.txt b/test/regression/CMakeLists.txt index c3e22283c7..9705065eb1 100644 --- a/test/regression/CMakeLists.txt +++ b/test/regression/CMakeLists.txt @@ -4,3 +4,10 @@ target_link_libraries(regression_test application integration_framework ) + +addtest(query_regression_test query_test.cpp) + +target_link_libraries(query_regression_test + application + integration_framework + ) diff --git a/test/regression/query_test.cpp b/test/regression/query_test.cpp new file mode 100644 index 0000000000..19cab11a57 --- /dev/null +++ b/test/regression/query_test.cpp @@ -0,0 +1,69 @@ +/** + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#include "builders/protobuf/queries.hpp" +#include "cryptography/crypto_provider/crypto_defaults.hpp" +#include "framework/integration_framework/integration_test_framework.hpp" +#include "framework/specified_visitor.hpp" +#include "module/shared_model/builders/protobuf/test_query_builder.hpp" + +template +auto makeQuery() { + return BaseType() + .createdTime(iroha::time::now()) + .creatorAccountId("admin@test") + .queryCounter(1) + .getAccount("admin@test") + .build(); +} + +template +auto createInvalidQuery(Query query, + const shared_model::crypto::Keypair &keypair) { + query.addSignature(shared_model::crypto::Signed(std::string(32, 'a')), + keypair.publicKey()); + return query; +} + +/** + * @given itf instance + * @when pass query with invalid signature + * @then assure that query with invalid signature is failed with stateless + * error + */ +TEST(QueryTest, FailedQueryTest) { + const auto key_pair = + shared_model::crypto::DefaultCryptoAlgorithmType::generateKeypair(); + + auto query_with_broken_signature = + createInvalidQuery(makeQuery(), key_pair); + auto stateless_invalid_query_response = [](auto &status) { + auto &resp = + boost::apply_visitor(framework::SpecifiedVisitor< + shared_model::interface::ErrorQueryResponse>(), + status.get()); + boost::apply_visitor( + framework::SpecifiedVisitor< + shared_model::interface::StatelessFailedErrorResponse>(), + resp.get()); + }; + + integration_framework::IntegrationTestFramework itf(1); + + itf.setInitialState(key_pair).sendQuery(query_with_broken_signature, + stateless_invalid_query_response); +} + +/** + * @given itf instance + * @when pass block query with invalid signature + * @then assure that query with invalid signature is failed with stateless + * error + */ +TEST(QueryTest, FailedBlockQueryTest) { + // TODO: 01/08/2018 @muratovv Implement test since IR-1569 will be completed +}