diff --git a/core/src/async/algorithm.h b/core/src/async/algorithm.h index 5e2cfff677..0c5a455410 100644 --- a/core/src/async/algorithm.h +++ b/core/src/async/algorithm.h @@ -34,41 +34,91 @@ #include "Future.hpp" #include +#include + namespace ledger { - namespace core { - namespace async { - - namespace internals { - - template - Future sequence_go(const std::shared_ptr& context, int index, const std::vector< Future >& futures, std::vector* buffer) { - if (index >= futures.size()) - return Future::successful(unit); - else { - Future fut = futures[index]; - return fut.template flatMap(context, [context, buffer, index, futures](const T &r) -> Future { - buffer->push_back(r); - return sequence_go(context, index + 1, futures, buffer); - }); - } +namespace core { +namespace async { + +namespace internals { + +template +Future sequence_go(const std::shared_ptr& context, int index, const std::vector>& futures, std::vector* buffer) +{ + if (index >= futures.size()) { + return Future::successful(unit); + } else { + Future fut = futures[index]; + return fut.template flatMap(context, [context, buffer, index, futures](const T &r) -> Future { + buffer->push_back(r); + return sequence_go(context, index + 1, futures, buffer); + }); } +} + +} // namespace internals +template +Future> sequence(const std::shared_ptr& context, const std::vector>& futures) +{ + auto buffer = new std::vector(); + return internals::sequence_go(context, 0, futures, buffer).template map>(context, [buffer] (const Unit&) -> std::vector { + auto res = *buffer; + delete buffer; + return res; + }); } - template - Future< std::vector > sequence(const std::shared_ptr& context, const std::vector< Future >& futures) { - auto buffer = new std::vector(); - return internals::sequence_go(context, 0, futures, buffer).template map< std::vector >(context, [buffer] (const Unit&) -> std::vector { - auto res = *buffer; - delete buffer; - return res; +namespace internals { + +template +struct sequence_t +{ + static auto go( + const std::shared_ptr& context, + std::tuple...>& tuple_futs, + std::tuple* tuple) -> Future + { + return std::get(tuple_futs) + .template flatMap(context, [context, tuple_futs, tuple](const auto& r) mutable { + std::get(*tuple) = r; + return sequence_t::go(context, tuple_futs, tuple); }); - } + } +}; - } +template +struct sequence_t +{ + static auto go( + const std::shared_ptr& context, + std::tuple...>& tuple_futs, + std::tuple* tuple) -> Future + { + return Future::successful(unit); } +}; + +} // namespace internals + +template +auto sequence( + const std::shared_ptr& context, + std::tuple...>& tuple_futs) -> Future> +{ + auto tuple = new std::tuple(); + return internals::sequence_t<0, sizeof...(T), T...>::go(context, tuple_futs, tuple) + .template map>(context, [tuple](const Unit&) { + const auto res = *tuple; + delete tuple; + return res; + }); } +} // namespace async +} // namespace core +} // namespace ledger + #define BEGIN_ASYNC_WHILE() #define ASYNC_FOREACH() diff --git a/core/src/wallet/tezos/TezosLikeAccount.cpp b/core/src/wallet/tezos/TezosLikeAccount.cpp index 24985bee1d..6018ef1070 100644 --- a/core/src/wallet/tezos/TezosLikeAccount.cpp +++ b/core/src/wallet/tezos/TezosLikeAccount.cpp @@ -129,7 +129,7 @@ namespace ledger { if (transaction.type == api::TezosOperationTag::OPERATION_TAG_ORIGINATION && transaction.status == 1) { updateOriginatedAccounts(sql, operation); } - out.push_back(operation); + out.push_back(operation); result = static_cast(transaction.type); } @@ -137,7 +137,7 @@ namespace ledger { operation.amount = transaction.value; operation.type = api::OperationType::RECEIVE; operation.refreshUid(); - out.push_back(operation); + out.push_back(operation); result = static_cast(transaction.type); } } @@ -164,13 +164,13 @@ namespace ledger { auto originatedAccountUid = TezosLikeAccountDatabaseHelper::createOriginatedAccountUid(getAccountUid(), origAccount.address); const auto found = std::find_if ( - _originatedAccounts.begin(), - _originatedAccounts.end(), - [&originatedAccountUid](const std::shared_ptr& element) { + _originatedAccounts.begin(), + _originatedAccounts.end(), + [&originatedAccountUid](const std::shared_ptr& element) { return std::dynamic_pointer_cast(element)->getAccountUid() == originatedAccountUid; }); - if (found == _originatedAccounts.end()) { + if (found == _originatedAccounts.end()) { _originatedAccounts.emplace_back( std::make_shared(originatedAccountUid, origAccount.address, @@ -205,10 +205,9 @@ namespace ledger { if (cachedBalance.hasValue()) { return FuturePtr::successful(std::make_shared(cachedBalance.getValue())); } - std::vector listAddresses{_keychain->getAddress()}; auto currency = getWallet()->getCurrency(); auto self = getSelf(); - return _explorer->getBalance(listAddresses).mapPtr(getMainExecutionContext(), [self, currency]( + return _explorer->getBalance(_keychain->getAddress()).mapPtr(getMainExecutionContext(), [self, currency]( const std::shared_ptr &balance) -> std::shared_ptr { Amount b(currency, 0, BigInt(balance->toString())); self->getWallet()->updateBalanceCache(self->getIndex(), b); diff --git a/core/src/wallet/tezos/TezosLikeAccount.h b/core/src/wallet/tezos/TezosLikeAccount.h index e8906b7469..91b3f4bb41 100644 --- a/core/src/wallet/tezos/TezosLikeAccount.h +++ b/core/src/wallet/tezos/TezosLikeAccount.h @@ -43,7 +43,7 @@ #include #include #include -#include +#include #include #include @@ -87,7 +87,7 @@ namespace ledger { void interpretTransaction(const TezosLikeBlockchainExplorerTransaction& transaction, std::vector& out); - + Try bulkInsert(const std::vector& operations); void updateOriginatedAccounts(soci::session &sql, const Operation &operation); diff --git a/core/src/wallet/tezos/TezosLikeAccount2.cpp b/core/src/wallet/tezos/TezosLikeAccount2.cpp index 101321b28a..52616e7eed 100644 --- a/core/src/wallet/tezos/TezosLikeAccount2.cpp +++ b/core/src/wallet/tezos/TezosLikeAccount2.cpp @@ -110,68 +110,7 @@ namespace ledger { auto startTime = DateUtils::now(); eventPublisher->postSticky(std::make_shared(api::EventCode::SYNCHRONIZATION_STARTED, api::DynamicObject::newInstance()), 0); - future.flatMap(getContext(), [self] (const Try &result) { - // Synchronize originated accounts ... - // Notes: We should rid of this part by implementing support for fetching - // txs for multiple addresses - // Hint: we could add originated accounts to keychain as - // managedAccounts and getAllObservableAddresses will return them as well - if (self->_originatedAccounts.empty()) { - return Future::successful(result.getValue()); - } - if (result.isFailure()) { - return Future::successful(BlockchainExplorerAccountSynchronizationResult{}); - } - using TxsBulk = TezosLikeBlockchainExplorer::TransactionsBulk; - - static std::function (std::shared_ptr, size_t, void*, BlockchainExplorerAccountSynchronizationResult)> getTxs = - [] (const std::shared_ptr &account, size_t id, void *session, BlockchainExplorerAccountSynchronizationResult result) { - std::vector addresses{account->_originatedAccounts[id]->getAddress()}; - - // Get offset to not start sync from beginning - auto offset = session ? Future>>::successful(std::vector>()) : - std::dynamic_pointer_cast( - account->_originatedAccounts[id]->queryOperations()->partial() - )->execute(); - - return offset.flatMap(account->getContext(), [=] (const std::vector> &ops) mutable { - // For the moment we start synchro from the beginning - auto getSession = session ? Future::successful(session) : - account->_explorer->startSession(); - return getSession.flatMap(account->getContext(), [=] (void *s) mutable { - return account->_explorer->getTransactions(addresses, std::to_string(ops.size()), s) - .flatMap(account->getContext(), [=] (const std::shared_ptr &bulk) mutable { - auto uid = TezosLikeAccountDatabaseHelper::createOriginatedAccountUid(account->getAccountUid(), addresses[0]); - { - std::vector operations; - for (auto &tx : bulk->transactions) { - tx.originatedAccountUid = uid; - tx.originatedAccountAddress = addresses[0]; - account->interpretTransaction(tx, operations); - } - auto f = account->bulkInsert(operations); - if (f.isSuccess()) { - result.newOperations += operations.size(); - } - } - - if (bulk->hasNext) { - return getTxs(account, id, s, result); - } - - if (id == account->_originatedAccounts.size() - 1) { - return Future::successful(result); - } - return getTxs(account, id + 1, nullptr, result); - }).recover(account->getContext(), [] (const Exception& ex) -> BlockchainExplorerAccountSynchronizationResult { - throw ex; - }); - }); - }); - }; - return getTxs(self, 0, nullptr, result.getValue()); - }).onComplete(getContext(), [eventPublisher, self, startTime](const auto &result) { - + future.onComplete(getContext(), [eventPublisher, self, startTime](const auto &result) { api::EventCode code; auto payload = std::make_shared(); auto duration = std::chrono::duration_cast( @@ -214,7 +153,7 @@ namespace ledger { std::cout << "broadcastTransaction: "<< counter->toString() << " / " << txHash << std::endl; //auto waitingTxs = self->getInternalPreferences()->getStringArray("waiting_counter_txs", {}); //waitingTxs.push_back(txHash); - //self->getInternalPreferences()->editor()->putStringArray("waiting_counter_txs", waitingTxs)->commit(); + //self->getInternalPreferences()->editor()->putStringArray("waiting_counter_txs", waitingTxs)->commit(); } void TezosLikeAccount::_broadcastRawTransaction(const std::vector &transaction, @@ -276,7 +215,7 @@ namespace ledger { // Check if balance is sufficient auto currency = self->getWallet()->getCurrency(); auto accountAddress = TezosLikeAddress::fromBase58(senderAddress, currency); - return explorer->getBalance(std::vector>{accountAddress}).flatMapPtr( + return explorer->getBalance(accountAddress).flatMapPtr( self->getMainExecutionContext(), [self, request, explorer, accountAddress, currency, senderAddress](const std::shared_ptr &balance) { // Check if all needed values are set @@ -329,8 +268,8 @@ namespace ledger { }; return setRevealStatus().flatMapPtr(self->getMainExecutionContext(), [=] (const Unit &result) { - - //initialize the value + + //initialize the value //note that the value will be recalculated for the wipe mode after calculating fees if (request.type != api::TezosOperationTag::OPERATION_TAG_DELEGATION) { tx->setValue(request.wipe ? std::make_shared(BigInt::ZERO) : request.value); @@ -419,15 +358,15 @@ namespace ledger { return gasPriceFut.flatMapPtr(self->getMainExecutionContext(), [self, filledTx] (const std::shared_ptr&gasPrice) -> FuturePtr { return self->estimateGasLimit(filledTx).flatMapPtr(self->getMainExecutionContext(), [filledTx, gasPrice] (const std::shared_ptr &gas) -> FuturePtr { // 0.000001 comes from the gasPrice->toInt64 being in picoTez - + filledTx->setRevealGasLimit(std::make_shared(gas->reveal)); const auto revealFees = std::make_shared(static_cast(1 + static_cast(gas->reveal.toInt64()) * static_cast(gasPrice->toInt64()) * 0.000001)); filledTx->setRevealFees(revealFees); filledTx->setTransactionGasLimit(std::make_shared(gas->transaction)); - const auto transactionFees = std::make_shared(static_cast(1 + static_cast(gas->transaction.toInt64()) * static_cast(gasPrice->toInt64()) * 0.000001)); + const auto transactionFees = std::make_shared(static_cast(1 + static_cast(gas->transaction.toInt64()) * static_cast(gasPrice->toInt64()) * 0.000001)); filledTx->setTransactionFees(transactionFees); - + return FuturePtr::successful(filledTx); }); }); @@ -495,14 +434,14 @@ namespace ledger { //else { // //keep only not validated counters: // auto waitingTxs = self->getInternalPreferences()->getStringArray("waiting_counter_txs", {}); - // std::cout << "get waiting_counter_txs ="; + // std::cout << "get waiting_counter_txs ="; // for (auto& s: waitingTxs) std::cout << s << "-"; // std::cout << std::endl; // auto waitingTxsSize = waitingCounter - explorerCounter->toInt64(); // if(waitingTxsSize < waitingTxs.size()) { // waitingTxs = std::vector(waitingTxs.end() - waitingTxsSize, waitingTxs.end()); // self->getInternalPreferences()->editor()->putStringArray("waiting_counter_txs", waitingTxs)->commit(); - // std::cout << "set waiting_counter_txs ="; + // std::cout << "set waiting_counter_txs ="; // for (auto& s: waitingTxs) std::cout << s << "-"; // std::cout << std::endl; // } diff --git a/core/src/wallet/tezos/TezosLikeWallet.h b/core/src/wallet/tezos/TezosLikeWallet.h index 464b2e1b49..0de6f22ea7 100644 --- a/core/src/wallet/tezos/TezosLikeWallet.h +++ b/core/src/wallet/tezos/TezosLikeWallet.h @@ -34,7 +34,7 @@ #include #include #include -#include +#include #include #include diff --git a/core/src/wallet/tezos/explorers/ExternalTezosLikeBlockchainExplorer.cpp b/core/src/wallet/tezos/explorers/ExternalTezosLikeBlockchainExplorer.cpp index 6864ddd99d..5293732718 100644 --- a/core/src/wallet/tezos/explorers/ExternalTezosLikeBlockchainExplorer.cpp +++ b/core/src/wallet/tezos/explorers/ExternalTezosLikeBlockchainExplorer.cpp @@ -29,6 +29,9 @@ */ #include "ExternalTezosLikeBlockchainExplorer.h" +#include +#include +#include #include #include #include @@ -36,40 +39,33 @@ namespace ledger { namespace core { + ExternalTezosLikeBlockchainExplorer::ExternalTezosLikeBlockchainExplorer( const std::shared_ptr &context, const std::shared_ptr &http, const api::TezosLikeNetworkParameters ¶meters, - const std::shared_ptr &configuration) : - DedicatedContext(context), - TezosLikeBlockchainExplorer(configuration, {api::Configuration::BLOCKCHAIN_EXPLORER_API_ENDPOINT}) { - _http = http; - _parameters = parameters; + const std::shared_ptr &configuration) + : TezosLikeBlockchainExplorer( + context, http, parameters, configuration, + {api::Configuration::BLOCKCHAIN_EXPLORER_API_ENDPOINT}) + { _bcd = configuration->getString(api::TezosConfiguration::BCD_API) .value_or(api::TezosConfigurationDefaults::BCD_API_ENDPOINT); } Future> - ExternalTezosLikeBlockchainExplorer::getBalance(const std::vector &addresses) { - auto size = addresses.size(); - if (size != 1) { - throw make_exception(api::ErrorCode::INVALID_ARGUMENT, - "Can only get balance of 1 address from Tezos Node, but got {} addresses", - addresses.size()); - } - std::string addressesStr = addresses[0]->toString(); - return getHelper(fmt::format("account/{}", addressesStr), - "total_balance", - std::unordered_map{}, - "0", - "", - true - ); + ExternalTezosLikeBlockchainExplorer::getBalance(const TezosLikeKeychain::Address &address) const { + return getHelper(fmt::format("account/{}", address->toString()), + "total_balance", + std::unordered_map{}, + "0", + "", + true); } Future> - ExternalTezosLikeBlockchainExplorer::getFees() { + ExternalTezosLikeBlockchainExplorer::getFees() const { // The best value is probably // divider_tx = (n_ops - n_ops_failed - n_ops_contract - // n_seed_nonce_revelation - n_double_baking_evidence - @@ -89,7 +85,7 @@ namespace ledger { //Is there a fees field ? if (!json.IsObject()) { throw make_exception(api::ErrorCode::HTTP_ERROR, - fmt::format("Failed to get fees from network, no (or malformed) response")); + fmt::format("Failed to get fees from network, no (or malformed) response")); } // Return 0 if the block had no transaction at all @@ -113,7 +109,7 @@ namespace ledger { } if (feesValueStr.empty()) { throw make_exception(api::ErrorCode::HTTP_ERROR, - "Failed to get fees from network, no (or malformed) response"); + "Failed to get fees from network, no (or malformed) response"); } const auto totalFees = api::BigInt::fromDecimalString(feesValueStr, 6, "."); @@ -129,7 +125,7 @@ namespace ledger { } Future> - ExternalTezosLikeBlockchainExplorer::getGasPrice() { + ExternalTezosLikeBlockchainExplorer::getGasPrice() const { const bool parseNumbersAsString = true; const auto gasPriceField = "gas_price"; @@ -140,7 +136,7 @@ namespace ledger { if (!json.IsObject() || !json.HasMember(gasPriceField) || !json[gasPriceField].IsString()) { throw make_exception(api::ErrorCode::HTTP_ERROR, - fmt::format("Failed to get gas_price from network, no (or malformed) field \"{}\" in response", gasPriceField)); + fmt::format("Failed to get gas_price from network, no (or malformed) field \"{}\" in response", gasPriceField)); } const std::string apiGasPrice = json[gasPriceField].GetString(); const std::string picoTezGasPrice = api::BigInt::fromDecimalString(apiGasPrice, 6, ".")->toString(10); @@ -154,89 +150,53 @@ namespace ledger { body << '"' << hex::toString(transaction) << '"'; auto bodyString = body.str(); return _http->POST("/injection/operation?chain=main", - std::vector(bodyString.begin(), bodyString.end()), - std::unordered_map{{"Content-Type", "application/json"}}, - getRPCNodeEndpoint()) - .json().template map(getExplorerContext(), - [](const HttpRequest::JsonResult &result) -> String { - auto &json = *std::get<1>(result); - - if (!json.IsString()) { - throw make_exception(api::ErrorCode::HTTP_ERROR, + std::vector(bodyString.begin(), bodyString.end()), + std::unordered_map{{"Content-Type", "application/json"}}, + getRPCNodeEndpoint()) + .json().template map(getContext(), + [](const HttpRequest::JsonResult &result) -> String { + auto &json = *std::get<1>(result); + + if (!json.IsString()) { + throw make_exception(api::ErrorCode::HTTP_ERROR, "Failed to parse broadcast transaction response, missing transaction hash"); - } - return json.GetString(); - }); - } - - Future ExternalTezosLikeBlockchainExplorer::startSession() { - std::string sessionToken = fmt::format("{}", std::rand()); - _sessions.insert(std::make_pair(sessionToken, 0)); - return Future::successful(new std::string(sessionToken)); - } - - Future ExternalTezosLikeBlockchainExplorer::killSession(void *session) { - if (session) { - _sessions.erase(*(reinterpret_cast(session))); - } - return Future::successful(unit); - } - - Future ExternalTezosLikeBlockchainExplorer::getRawTransaction(const String &transactionHash) { - // WARNING: not implemented - throw make_exception(api::ErrorCode::IMPLEMENTATION_IS_MISSING, - "Endpoint to get raw transactions is not implemented."); - } - - Future ExternalTezosLikeBlockchainExplorer::pushTransaction(const std::vector &transaction) { - return pushLedgerApiTransaction(transaction); + } + return json.GetString(); + }); } FuturePtr - ExternalTezosLikeBlockchainExplorer::getTransactions(const std::vector &addresses, - Option offset, - Option session) { - auto tryOffset = Try::from([=]() -> uint64_t { - return std::stoul(offset.getValueOr(""), nullptr, 10); - }); - - uint64_t localOffset = tryOffset.isSuccess() ? tryOffset.getValue() : 0; - uint64_t limit = 100; - if (session.hasValue()) { - auto s = _sessions[*((std::string *)session.getValue())]; - localOffset += limit * s; - _sessions[*reinterpret_cast(session.getValue())]++; - } - if (addresses.size() != 1) { - throw make_exception(api::ErrorCode::INVALID_ARGUMENT, - "Can only get transactions for 1 address from Tezos Node, but got {} addresses", - addresses.size()); - } - std::string params = fmt::format("?limit={}",limit); + ExternalTezosLikeBlockchainExplorer::getTransactions(const std::string& address, + const Either& token) const { + + const uint64_t localOffset = token.isRight() ? token.getRight() : 0; + const uint64_t limit = 100; + std::string params = fmt::format("?limit={}", limit); if (localOffset > 0) { - params += fmt::format("&offset={}",localOffset); + params += fmt::format("&offset={}", localOffset); } using EitherTransactionsBulk = Either>; - return _http->GET(fmt::format("account/{}/op{}", addresses[0], params)) + return _http->GET(fmt::format("account/{}/op{}", address, params)) .template json( LedgerApiParser()) - .template mapPtr(getExplorerContext(), - [limit](const EitherTransactionsBulk &result) { - if (result.isLeft()) { - // Because it fails when there are no ops - return std::make_shared(); - } else { - result.getRight()->hasNext = result.getRight()->transactions.size() == limit; - return result.getRight(); - } - }); + .template mapPtr(getContext(), + [limit](const EitherTransactionsBulk &result) { + if (result.isLeft()) { + // Because it fails when there are no ops + return std::make_shared(); + } else { + result.getRight()->hasNext = result.getRight()->transactions.size() == limit; + return result.getRight(); + } + }); } - FuturePtr ExternalTezosLikeBlockchainExplorer::getCurrentBlock() const { + FuturePtr + ExternalTezosLikeBlockchainExplorer::getCurrentBlock() const { return _http->GET("block/head") .template json(LedgerApiParser()) - .template mapPtr(getExplorerContext(), + .template mapPtr(getContext(), [](const Either> &result) { if (result.isLeft()) { throw result.getLeft(); @@ -246,25 +206,133 @@ namespace ledger { }); } - FuturePtr - ExternalTezosLikeBlockchainExplorer::getTransactionByHash(const String &transactionHash) const { - return getLedgerApiTransactionByHash(transactionHash); + std::string ExternalTezosLikeBlockchainExplorer::getExplorerVersion() const { + return ""; } - Future ExternalTezosLikeBlockchainExplorer::getTimestamp() const { - return getLedgerApiTimestamp(); + Future> + ExternalTezosLikeBlockchainExplorer::getEstimatedGasLimit(const std::string &address) const { + return FuturePtr::successful( + std::make_shared(api::TezosConfigurationDefaults::TEZOS_DEFAULT_GAS_LIMIT) + ); } - std::shared_ptr ExternalTezosLikeBlockchainExplorer::getExplorerContext() const { - return _executionContext; + Future> + ExternalTezosLikeBlockchainExplorer::getEstimatedGasLimit(const std::shared_ptr &tx) const { + return TezosLikeBlockchainExplorer::getEstimatedGasLimit(_http, getContext(), tx); } - api::TezosLikeNetworkParameters ExternalTezosLikeBlockchainExplorer::getNetworkParameters() const { - return _parameters; + Future> + ExternalTezosLikeBlockchainExplorer::getStorage(const std::string &address) const { + return FuturePtr::successful( + std::make_shared(api::TezosConfigurationDefaults::TEZOS_DEFAULT_STORAGE_LIMIT) + ); } - std::string ExternalTezosLikeBlockchainExplorer::getExplorerVersion() const { - return ""; + Future> + ExternalTezosLikeBlockchainExplorer::getCounter(const std::string &address) const { + return getHelper(fmt::format("/chains/main/blocks/head/context/contracts/{}/counter", address), + "", + std::unordered_map{}, + "0", + getRPCNodeEndpoint() + ); + } + + Future> + ExternalTezosLikeBlockchainExplorer::forgeKTOperation(const std::shared_ptr &tx) const { + return TezosLikeBlockchainExplorer::forgeKTOperation(tx, getExplorerContext(), _http, getRPCNodeEndpoint()); + } + + Future + ExternalTezosLikeBlockchainExplorer::getManagerKey(const std::string &address) const { + return TezosLikeBlockchainExplorer::getManagerKey(address, getExplorerContext(), _http, getRPCNodeEndpoint()); + } + + Future + ExternalTezosLikeBlockchainExplorer::isAllocated(const std::string &address) const { + return TezosLikeBlockchainExplorer::isAllocated(address, getExplorerContext(), _http, getRPCNodeEndpoint()); + } + + Future + ExternalTezosLikeBlockchainExplorer::getCurrentDelegate(const std::string &address) const { + return TezosLikeBlockchainExplorer::getCurrentDelegate(address, getExplorerContext(), _http, getRPCNodeEndpoint()); + } + + Future + ExternalTezosLikeBlockchainExplorer::isFunded(const std::string &address) const { + return _http->GET(fmt::format("account/{}", address)) + .json(false, true).map(getExplorerContext(), [=](const HttpRequest::JsonResult &result) { + auto& connection = *std::get<0>(result); + if (connection.getStatusCode() == 404) { + //an empty account + return false; + } + else if (connection.getStatusCode() < 200 || connection.getStatusCode() >= 300) { + throw Exception(api::ErrorCode::HTTP_ERROR, connection.getStatusText()); + } + else { + auto& json = *std::get<1>(result); + + // look for the is_funded field + const auto field = "is_funded"; + if (!json.IsObject() || !json.HasMember(field) || + !json[field].IsBool()) { + throw make_exception(api::ErrorCode::HTTP_ERROR, + "Failed to get is_funded from network, no (or malformed) field \"result\" in response"); + } + + return json[field].GetBool(); + } + }); + } + + Future> + ExternalTezosLikeBlockchainExplorer::getTokenBalance(const std::string& accountAddress, + const std::string& tokenAddress) const { + const auto parseNumbersAsString = true; + return _http->GET(fmt::format("/account/mainnet/{}", accountAddress), {}, _bcd) + .json(parseNumbersAsString) + .mapPtr(getContext(), + [=](const HttpRequest::JsonResult &result) { + const auto &json = *std::get<1>(result); + if (!json.HasMember("tokens") || !json["tokens"].IsArray()) { + throw make_exception(api::ErrorCode::HTTP_ERROR, + fmt::format("Failed to get tokens for {}, no (or malformed) field `tokens` in response", accountAddress)); + } + + const auto tokens = json["tokens"].GetArray(); + for (const auto& token : tokens) { + if (!token.HasMember("contract") || !token["contract"].IsString()) { + throw make_exception(api::ErrorCode::HTTP_ERROR, + "Failed to get contract from network, no (or malformed) field `contract` in response"); + } + if (token["contract"].GetString() == tokenAddress) { + if (!token.HasMember("balance") || !token["balance"].IsString()) { + throw make_exception(api::ErrorCode::HTTP_ERROR, + "Failed to get contract balance from network, no (or malformed) field `balance` in response"); + } + return std::make_shared(BigInt::fromString(token["balance"].GetString())); + } + } + return std::make_shared(BigInt::ZERO); + }); + } + + Future + ExternalTezosLikeBlockchainExplorer::isDelegate(const std::string &address) const { + return _http->GET(fmt::format("account/{}", address)) + .json(false).map(getExplorerContext(), [=](const HttpRequest::JsonResult &result) { + auto& json = *std::get<1>(result); + // look for the is_active_delegate field + const auto field = "is_active_delegate"; + if (!json.IsObject() || !json.HasMember(field) || + !json[field].IsBool()) { + throw make_exception(api::ErrorCode::HTTP_ERROR, + "Failed to get is_active_delegate from network, no (or malformed) field in response"); + } + return json[field].GetBool(); + }); } Future> @@ -273,7 +341,7 @@ namespace ledger { const std::unordered_map ¶ms, const std::string &fallbackValue, const std::string &forceUrl, - bool isDecimal) { + bool isDecimal) const { const bool parseNumbersAsString = true; const bool ignoreStatusCode = true; auto networkId = getNetworkParameters().Identifier; @@ -305,8 +373,8 @@ namespace ledger { !json.HasMember(field.c_str()) || !json[field.c_str()].IsString()) && !json.IsString()) { throw make_exception(api::ErrorCode::HTTP_ERROR, - fmt::format("Failed to get {} for {}", field, - networkId)); + fmt::format("Failed to get {} for {}", field, + networkId)); } std::string value = json.IsString() ? json.GetString() : json[field.c_str()].GetString(); if (value == "0" && !fallbackValue.empty()) { @@ -318,136 +386,5 @@ namespace ledger { }); } - Future> - ExternalTezosLikeBlockchainExplorer::getEstimatedGasLimit(const std::string &address) { - return FuturePtr::successful( - std::make_shared(api::TezosConfigurationDefaults::TEZOS_DEFAULT_GAS_LIMIT) - ); - } - - Future> - ExternalTezosLikeBlockchainExplorer::getEstimatedGasLimit(const std::shared_ptr &tx) { - return TezosLikeBlockchainExplorer::getEstimatedGasLimit(_http, getContext(), tx); - } - - Future> - ExternalTezosLikeBlockchainExplorer::getStorage(const std::string &address) { - return FuturePtr::successful( - std::make_shared(api::TezosConfigurationDefaults::TEZOS_DEFAULT_STORAGE_LIMIT) - ); - } - - Future> - ExternalTezosLikeBlockchainExplorer::getCounter(const std::string &address) { - return getHelper(fmt::format("/chains/main/blocks/head/context/contracts/{}/counter", address), - "", - std::unordered_map{}, - "0", - getRPCNodeEndpoint() - ); - } - - Future> ExternalTezosLikeBlockchainExplorer::forgeKTOperation(const std::shared_ptr &tx) { - return TezosLikeBlockchainExplorer::forgeKTOperation(tx, - getExplorerContext(), - _http, - getRPCNodeEndpoint()); - } - - Future ExternalTezosLikeBlockchainExplorer::getManagerKey(const std::string &address) { - return TezosLikeBlockchainExplorer::getManagerKey(address, - getExplorerContext(), - _http, - getRPCNodeEndpoint()); - } - - Future ExternalTezosLikeBlockchainExplorer::isAllocated(const std::string &address) { - return TezosLikeBlockchainExplorer::isAllocated(address, - getExplorerContext(), - _http, - getRPCNodeEndpoint()); - } - - Future ExternalTezosLikeBlockchainExplorer::getCurrentDelegate(const std::string &address) { - return TezosLikeBlockchainExplorer::getCurrentDelegate(address, - getExplorerContext(), - _http, - getRPCNodeEndpoint()); - } - - Future> - ExternalTezosLikeBlockchainExplorer::getTokenBalance(const std::string& accountAddress, - const std::string& tokenAddress) const { - const auto parseNumbersAsString = true; - return _http->GET(fmt::format("/account/mainnet/{}", accountAddress), {}, _bcd) - .json(parseNumbersAsString) - .mapPtr(getContext(), - [=](const HttpRequest::JsonResult &result) { - const auto &json = *std::get<1>(result); - if (!json.HasMember("tokens") || !json["tokens"].IsArray()) { - throw make_exception(api::ErrorCode::HTTP_ERROR, - fmt::format("Failed to get tokens for {}, no (or malformed) field `tokens` in response", accountAddress)); - } - - const auto tokens = json["tokens"].GetArray(); - for (const auto& token : tokens) { - if (!token.HasMember("contract") || !token["contract"].IsString()) { - throw make_exception(api::ErrorCode::HTTP_ERROR, - "Failed to get contract from network, no (or malformed) field `contract` in response"); - } - if (token["contract"].GetString() == tokenAddress) { - if (!token.HasMember("balance") || !token["balance"].IsString()) { - throw make_exception(api::ErrorCode::HTTP_ERROR, - "Failed to get contract balance from network, no (or malformed) field `balance` in response"); - } - return std::make_shared(BigInt::fromString(token["balance"].GetString())); - } - } - return std::make_shared(BigInt::ZERO); - }); - } - - Future ExternalTezosLikeBlockchainExplorer::isFunded(const std::string &address) { - return - _http->GET(fmt::format("account/{}", address)) - .json(false, true).map(getExplorerContext(), [=](const HttpRequest::JsonResult &result) { - auto& connection = *std::get<0>(result); - if (connection.getStatusCode() == 404) { - //an empty account - return false; - } - else if (connection.getStatusCode() < 200 || connection.getStatusCode() >= 300) { - throw Exception(api::ErrorCode::HTTP_ERROR, connection.getStatusText()); - } - else { - auto& json = *std::get<1>(result); - - // look for the is_funded field - const auto field = "is_funded"; - if (!json.IsObject() || !json.HasMember(field) || - !json[field].IsBool()) { - throw make_exception(api::ErrorCode::HTTP_ERROR, - "Failed to get is_funded from network, no (or malformed) field \"result\" in response"); - } - - return json[field].GetBool(); - } - }); - } - - Future ExternalTezosLikeBlockchainExplorer::isDelegate(const std::string &address) { - return _http->GET(fmt::format("account/{}", address)) - .json(false).map(getExplorerContext(), [=](const HttpRequest::JsonResult &result) { - auto& json = *std::get<1>(result); - // look for the is_active_delegate field - const auto field = "is_active_delegate"; - if (!json.IsObject() || !json.HasMember(field) || - !json[field].IsBool()) { - throw make_exception(api::ErrorCode::HTTP_ERROR, - "Failed to get is_active_delegate from network, no (or malformed) field in response"); - } - return json[field].GetBool(); - }); - } - } -} + } // namespace core +} // namespace ledger diff --git a/core/src/wallet/tezos/explorers/ExternalTezosLikeBlockchainExplorer.h b/core/src/wallet/tezos/explorers/ExternalTezosLikeBlockchainExplorer.h index e91cbc9eec..46923a408e 100644 --- a/core/src/wallet/tezos/explorers/ExternalTezosLikeBlockchainExplorer.h +++ b/core/src/wallet/tezos/explorers/ExternalTezosLikeBlockchainExplorer.h @@ -28,97 +28,77 @@ * */ -#pragma once -#include +#ifndef LEDGER_CORE_EXTERNALTEZOSLIKEBLOCKCHAINEXPLORER_H +#define LEDGER_CORE_EXTERNALTEZOSLIKEBLOCKCHAINEXPLORER_H + #include -#include -#include -#include -#include #include namespace ledger { namespace core { - using ExternalApiBlockchainExplorer = AbstractLedgerApiBlockchainExplorer< - TezosLikeBlockchainExplorerTransaction, - TezosLikeBlockchainExplorer::TransactionsBulk, - TezosLikeTransactionsParser, - TezosLikeTransactionsBulkParser, - TezosLikeBlockParser, - api::TezosLikeNetworkParameters>; - - class ExternalTezosLikeBlockchainExplorer : public TezosLikeBlockchainExplorer, - public ExternalApiBlockchainExplorer, - public DedicatedContext, - public std::enable_shared_from_this { + + class ExternalTezosLikeBlockchainExplorer : public TezosLikeBlockchainExplorer + { public: - ExternalTezosLikeBlockchainExplorer(const std::shared_ptr &context, - const std::shared_ptr &http, - const api::TezosLikeNetworkParameters ¶meters, - const std::shared_ptr &configuration); + ExternalTezosLikeBlockchainExplorer( + const std::shared_ptr &context, + const std::shared_ptr &http, + const api::TezosLikeNetworkParameters ¶meters, + const std::shared_ptr &configuration); Future> - getBalance(const std::vector &addresses) override; + getBalance(const TezosLikeKeychain::Address &address) const override; Future> - getFees() override; + getFees() const override; Future> - getGasPrice() override; - - Future pushLedgerApiTransaction(const std::vector &transaction) override; - - Future startSession() override; + getGasPrice() const override; - Future killSession(void *session) override; - - Future getRawTransaction(const String &transactionHash) override; - - Future pushTransaction(const std::vector &transaction) override; + Future pushLedgerApiTransaction(const std::vector &transaction) override ; FuturePtr - getTransactions(const std::vector &addresses, - Option offset = Option(), - Option session = Option()) override; - - FuturePtr getCurrentBlock() const override; - - FuturePtr - getTransactionByHash(const String &transactionHash) const override; + getTransactions(const std::string& address, + const Either& token = {}) const override; - Future getTimestamp() const override; - - std::shared_ptr getExplorerContext() const override; - - api::TezosLikeNetworkParameters getNetworkParameters() const override; + FuturePtr + getCurrentBlock() const override; std::string getExplorerVersion() const override; Future> - getEstimatedGasLimit(const std::string &address) override; + getEstimatedGasLimit(const std::string &address) const override; Future> - getEstimatedGasLimit(const std::shared_ptr &transaction) override; + getEstimatedGasLimit(const std::shared_ptr &tx) const override; Future> - getStorage(const std::string &address) override; + getStorage(const std::string &address) const override; - Future> getCounter(const std::string &address) override; + Future> + forgeKTOperation(const std::shared_ptr &tx) const override; - Future> forgeKTOperation(const std::shared_ptr &tx) override; + Future + getManagerKey(const std::string &address) const override; - Future getManagerKey(const std::string &address) override; + Future + isAllocated(const std::string &address) const override; - Future isAllocated(const std::string &address) override; + Future + getCurrentDelegate(const std::string &address) const override; - Future getCurrentDelegate(const std::string &address) override; + Future> + getCounter(const std::string &address) const override; - Future isFunded(const std::string &address) override; + Future + isFunded(const std::string &address) const override; - Future> - getTokenBalance(const std::string& accountAddress, const std::string& tokenAddress) const override; + Future + isDelegate(const std::string &address) const override; - Future isDelegate(const std::string &address) override; + Future> + getTokenBalance(const std::string& accountAddress, + const std::string& tokenAddress) const override; private: /* @@ -132,14 +112,16 @@ namespace ledger { Future> getHelper(const std::string &url, const std::string &field, - const std::unordered_map ¶ms = std::unordered_map(), + const std::unordered_map ¶ms = {}, const std::string &fallbackValue = "", const std::string &forceUrl = "", - bool isDecimal = false); + bool isDecimal = false) const; - api::TezosLikeNetworkParameters _parameters; - std::unordered_map _sessions; + private: std::string _bcd; }; - } -} + + } // namespace core +} // namespace ledger + +#endif //LEDGER_CORE_EXTERNALTEZOSLIKEBLOCKCHAINEXPLORER_H diff --git a/core/src/wallet/tezos/explorers/NodeTezosLikeBlockchainExplorer.cpp b/core/src/wallet/tezos/explorers/NodeTezosLikeBlockchainExplorer.cpp index 3a0e9a7183..9b1624a9fd 100644 --- a/core/src/wallet/tezos/explorers/NodeTezosLikeBlockchainExplorer.cpp +++ b/core/src/wallet/tezos/explorers/NodeTezosLikeBlockchainExplorer.cpp @@ -30,6 +30,9 @@ #include "NodeTezosLikeBlockchainExplorer.h" +#include +#include +#include #include #include #include @@ -38,38 +41,34 @@ namespace ledger { namespace core { + NodeTezosLikeBlockchainExplorer::NodeTezosLikeBlockchainExplorer( const std::shared_ptr &context, const std::shared_ptr &http, const api::TezosLikeNetworkParameters ¶meters, - const std::shared_ptr &configuration) : - DedicatedContext(context), - TezosLikeBlockchainExplorer(configuration, {api::Configuration::BLOCKCHAIN_EXPLORER_API_ENDPOINT}) { - _http = http; - _parameters = parameters; + const std::shared_ptr &configuration) + : TezosLikeBlockchainExplorer( + context, http, parameters, configuration, + {api::Configuration::BLOCKCHAIN_EXPLORER_API_ENDPOINT}) + { _explorerVersion = configuration->getString(api::Configuration::BLOCKCHAIN_EXPLORER_VERSION) .value_or(api::TezosConfigurationDefaults::TEZOS_DEFAULT_API_VERSION); } Future> - NodeTezosLikeBlockchainExplorer::getBalance(const std::vector &addresses) { - auto size = addresses.size(); - if (size != 1) { - throw make_exception(api::ErrorCode::INVALID_ARGUMENT, - "Can only get balance of 1 address from Tezos Node, but got {} addresses", addresses.size()); - } + NodeTezosLikeBlockchainExplorer::getBalance(const TezosLikeKeychain::Address &address) const { bool parseNumbersAsString = true; - std::string addressesStr = addresses[0]->toBase58(); + const auto addressStr = address->toString(); return _http->GET(fmt::format("blockchain/{}/{}/balance/{}", getExplorerVersion(), getNetworkParameters().Identifier, - addressesStr)) + addressStr)) .json(parseNumbersAsString) - .mapPtr(getContext(), [addressesStr](const HttpRequest::JsonResult &result) { + .mapPtr(getContext(), [addressStr](const HttpRequest::JsonResult &result) { auto &json = *std::get<1>(result); if (!json.IsArray() && json.Size() == 1 && json[0].IsString()) { - throw make_exception(api::ErrorCode::HTTP_ERROR, "Failed to get balance for {}", addressesStr); + throw make_exception(api::ErrorCode::HTTP_ERROR, "Failed to get balance for {}", addressStr); } auto info = json[0].GetString(); return std::make_shared(info); @@ -77,7 +76,7 @@ namespace ledger { } Future> - NodeTezosLikeBlockchainExplorer::getFees() { + NodeTezosLikeBlockchainExplorer::getFees() const { bool parseNumbersAsString = true; return _http->GET(fmt::format("blockchain/{}/{}/head", getExplorerVersion(), getNetworkParameters().Identifier)) .json(parseNumbersAsString).mapPtr(getContext(), [](const HttpRequest::JsonResult &result) { @@ -86,7 +85,7 @@ namespace ledger { if (!json.IsObject() || !json.HasMember("fees") || !json["fees"].IsString()) { throw make_exception(api::ErrorCode::HTTP_ERROR, - "Failed to get fees from network, no (or malformed) field \"result\" in response"); + "Failed to get fees from network, no (or malformed) field \"result\" in response"); } std::string fees = json["fees"].GetString(); // Sometimes network is sending 0 for fees @@ -97,7 +96,8 @@ namespace ledger { }); } - Future> NodeTezosLikeBlockchainExplorer::getGasPrice() { + Future> + NodeTezosLikeBlockchainExplorer::getGasPrice() const { throw make_exception( api::ErrorCode::RUNTIME_ERROR, "getGasPrice is unimplemented for NodeTezosLikeExplorer"); @@ -110,7 +110,7 @@ namespace ledger { body << "{" << "\"tx\":" << '"' << hex::toString(transaction) << '"' << "}"; auto bodyString = body.str(); return _http->POST(fmt::format("blockchain/{}/{}/broadcast_transaction", getExplorerVersion(), getNetworkParameters().Identifier), - std::vector(bodyString.begin(), bodyString.end())) + std::vector(bodyString.begin(), bodyString.end())) .json().template map(getExplorerContext(), [](const HttpRequest::JsonResult &result) -> String { auto &json = *std::get<1>(result); if (!json.IsString()) { @@ -120,36 +120,15 @@ namespace ledger { }); } - Future NodeTezosLikeBlockchainExplorer::startSession() { - return Future::successful(new std::string("", 0)); - } - - Future NodeTezosLikeBlockchainExplorer::killSession(void *session) { - return Future::successful(unit); - } - - Future NodeTezosLikeBlockchainExplorer::getRawTransaction(const String &transactionHash) { - // WARNING: not implemented - throw make_exception(api::ErrorCode::IMPLEMENTATION_IS_MISSING, "Endpoint to get raw transactions is not implemented."); - } - - Future NodeTezosLikeBlockchainExplorer::pushTransaction(const std::vector &transaction) { - return pushLedgerApiTransaction(transaction); - } - FuturePtr - NodeTezosLikeBlockchainExplorer::getTransactions(const std::vector &addresses, - Option fromBlockHash, - Option session) { - if (addresses.size() != 1) { - throw make_exception(api::ErrorCode::INVALID_ARGUMENT, - "Can only get transactions for 1 address from Tezos Node, but got {} addresses", addresses.size()); - } + NodeTezosLikeBlockchainExplorer::getTransactions(const std::string &address, + const Either& token) const { std::string params; - if (fromBlockHash.hasValue()) { - params = "&block_hash=" + fromBlockHash.getValue(); + if (token.isLeft()) { + params = "&block_hash=" + token.getLeft(); } - auto self = shared_from_this(); + // TODO FIXME + auto self = std::make_shared(*this); //shared_from_this(); using EitherTransactionsBulk = Either>; static std::vector txTypes {"Transaction", "Reveal", "Origination", "Delegation"}; // Note: we should get rid of this if we tweak explorer @@ -178,7 +157,7 @@ namespace ledger { }); }; auto transactionsBulk = std::make_shared(); - return getTransactionsOfType(addresses[0], params, transactionsBulk, 0); + return getTransactionsOfType(address, params, transactionsBulk, 0); } FuturePtr NodeTezosLikeBlockchainExplorer::getCurrentBlock() const { @@ -194,66 +173,21 @@ namespace ledger { }); } - FuturePtr - NodeTezosLikeBlockchainExplorer::getTransactionByHash(const String &transactionHash) const { - return getLedgerApiTransactionByHash(transactionHash); - } - - Future NodeTezosLikeBlockchainExplorer::getTimestamp() const { - return getLedgerApiTimestamp(); - } - - std::shared_ptr NodeTezosLikeBlockchainExplorer::getExplorerContext() const { - return _executionContext; - } - - api::TezosLikeNetworkParameters NodeTezosLikeBlockchainExplorer::getNetworkParameters() const { - return _parameters; - } - std::string NodeTezosLikeBlockchainExplorer::getExplorerVersion() const { return _explorerVersion; } - Future> NodeTezosLikeBlockchainExplorer::getHelper(const std::string &url, - const std::string &field, - const std::unordered_map ¶ms, - const std::string &fallbackValue) { - bool parseNumbersAsString = true; - auto networkId = getNetworkParameters().Identifier; - - std::string p, separator = "?"; - for (auto ¶m : params) { - p += fmt::format("{}{}={}", separator, param.first, param.second); - separator = "&"; - } - - return _http->GET(url + p, std::unordered_map()) - .json(parseNumbersAsString) - .mapPtr(getContext(), [field, networkId, fallbackValue] (const HttpRequest::JsonResult& result) { - auto& json = *std::get<1>(result); - if (!json.IsArray() || json.Size() == 0 || !json[0].IsString()) { - throw make_exception(api::ErrorCode::HTTP_ERROR, fmt::format("Failed to get {} for {}", field, networkId)); - } - std::string value = json[0].GetString(); - if (value == "0" && !fallbackValue.empty()) { - value = fallbackValue; - } - return std::make_shared(value); - }); - } - - Future> NodeTezosLikeBlockchainExplorer::getEstimatedGasLimit(const std::string &address) { + Future> NodeTezosLikeBlockchainExplorer::getEstimatedGasLimit(const std::string &address) const { // TODO: activate when backend fix issue with gas limit estimation /* return getHelper(fmt::format("blockchain/{}/{}/estimate_gas", - getExplorerVersion(), - getNetworkParameters().Identifier), - "estimated_gas_limit", - std::unordered_map{{"token", address}}, - api::TezosConfigurationDefaults::TEZOS_DEFAULT_GAS_LIMIT + getExplorerVersion(), + getNetworkParameters().Identifier), + "estimated_gas_limit", + std::unordered_map{{"token", address}}, + api::TezosConfigurationDefaults::TEZOS_DEFAULT_GAS_LIMIT ); - */ + */ return FuturePtr::successful( std::make_shared(api::TezosConfigurationDefaults::TEZOS_DEFAULT_GAS_LIMIT) ); @@ -261,93 +195,85 @@ namespace ledger { Future> NodeTezosLikeBlockchainExplorer::getEstimatedGasLimit( - const std::shared_ptr &tx) + const std::shared_ptr &tx) const { return TezosLikeBlockchainExplorer::getEstimatedGasLimit(_http, getContext(), tx); } Future> NodeTezosLikeBlockchainExplorer::getStorage( - const std::string &address) + const std::string &address) const { return getHelper(fmt::format("blockchain/{}/{}/estimate_storage", - getExplorerVersion(), - getNetworkParameters().Identifier), - "storage", - std::unordered_map{{"token", address}}, - api::TezosConfigurationDefaults::TEZOS_DEFAULT_STORAGE_LIMIT + getExplorerVersion(), + getNetworkParameters().Identifier), + "storage", + std::unordered_map{{"token", address}}, + api::TezosConfigurationDefaults::TEZOS_DEFAULT_STORAGE_LIMIT ); } - Future> NodeTezosLikeBlockchainExplorer::getCounter(const std::string &address) { + Future> NodeTezosLikeBlockchainExplorer::getCounter(const std::string &address) const { return getHelper(fmt::format("blockchain/{}/{}/counter", - getExplorerVersion(), - getNetworkParameters().Identifier), - "counter", - std::unordered_map{{"token", address}} + getExplorerVersion(), + getNetworkParameters().Identifier), + "counter", + std::unordered_map{{"token", address}} ); } - Future> NodeTezosLikeBlockchainExplorer::forgeKTOperation(const std::shared_ptr &tx) { - return TezosLikeBlockchainExplorer::forgeKTOperation(tx, - getExplorerContext(), - _http, - getRPCNodeEndpoint()); + Future> + NodeTezosLikeBlockchainExplorer::forgeKTOperation(const std::shared_ptr &tx) const { + return TezosLikeBlockchainExplorer::forgeKTOperation(tx, getExplorerContext(), _http, getRPCNodeEndpoint()); } - Future NodeTezosLikeBlockchainExplorer::getManagerKey(const std::string &address) { - return TezosLikeBlockchainExplorer::getManagerKey(address, - getExplorerContext(), - _http, - getRPCNodeEndpoint()); + Future + NodeTezosLikeBlockchainExplorer::getManagerKey(const std::string &address) const { + return TezosLikeBlockchainExplorer::getManagerKey(address, getExplorerContext(), _http, getRPCNodeEndpoint()); } - Future NodeTezosLikeBlockchainExplorer::isAllocated(const std::string &address) { - return TezosLikeBlockchainExplorer::isAllocated(address, - getExplorerContext(), - _http, - getRPCNodeEndpoint()); + Future + NodeTezosLikeBlockchainExplorer::isAllocated(const std::string &address) const { + return TezosLikeBlockchainExplorer::isAllocated(address, getExplorerContext(), _http, getRPCNodeEndpoint()); } - Future NodeTezosLikeBlockchainExplorer::getCurrentDelegate(const std::string &address) { - return TezosLikeBlockchainExplorer::getCurrentDelegate(address, - getExplorerContext(), - _http, - getRPCNodeEndpoint()); + Future + NodeTezosLikeBlockchainExplorer::getCurrentDelegate(const std::string &address) const { + return TezosLikeBlockchainExplorer::getCurrentDelegate(address, getExplorerContext(), _http, getRPCNodeEndpoint()); } - Future NodeTezosLikeBlockchainExplorer::isFunded(const std::string &address) { - return - _http->GET(fmt::format("blockchain/{}/{}/account/{}", address)) - .json(false, true).map(getExplorerContext(), [=](const HttpRequest::JsonResult &result) { - auto& connection = *std::get<0>(result); - if (connection.getStatusCode() == 404) { - //an empty account - return false; - } - else if (connection.getStatusCode() < 200 || connection.getStatusCode() >= 300) { - throw Exception(api::ErrorCode::HTTP_ERROR, connection.getStatusText()); - } - else { - auto& json = *std::get<1>(result); - - // look for the is_funded field - const auto field = "is_funded"; - if (!json.IsObject() || !json.HasMember(field) || - !json[field].IsBool()) { - throw make_exception(api::ErrorCode::HTTP_ERROR, - "Failed to get is_funded from network, no (or malformed) field \"result\" in response"); - } + Future + NodeTezosLikeBlockchainExplorer::isFunded(const std::string &address) const { + return _http->GET(fmt::format("blockchain/{}/{}/account/{}", address)) + .json(false, true).map(getExplorerContext(), [=](const HttpRequest::JsonResult &result) { + auto& connection = *std::get<0>(result); + if (connection.getStatusCode() == 404) { + //an empty account + return false; + } + else if (connection.getStatusCode() < 200 || connection.getStatusCode() >= 300) { + throw Exception(api::ErrorCode::HTTP_ERROR, connection.getStatusText()); + } + else { + auto& json = *std::get<1>(result); - return json[field].GetBool(); + // look for the is_funded field + const auto field = "is_funded"; + if (!json.IsObject() || !json.HasMember(field) || + !json[field].IsBool()) { + throw make_exception(api::ErrorCode::HTTP_ERROR, + "Failed to get is_funded from network, no (or malformed) field \"result\" in response"); } - }); + + return json[field].GetBool(); + } + }); } - Future NodeTezosLikeBlockchainExplorer::isDelegate(const std::string &address) { + Future NodeTezosLikeBlockchainExplorer::isDelegate(const std::string &address) const { return _http->GET(fmt::format("blockchain/{}/{}/account/{}", - getExplorerVersion(), - getNetworkParameters().Identifier, - address)) + getExplorerVersion(), + getNetworkParameters().Identifier, + address)) .json(false).map(getExplorerContext(), [=](const HttpRequest::JsonResult &result) { auto& json = *std::get<1>(result); // look for the is_active_delegate field @@ -363,11 +289,40 @@ namespace ledger { Future> NodeTezosLikeBlockchainExplorer::getTokenBalance(const std::string& accountAddress, - const std::string& tokenAddress) const { + const std::string& tokenAddress) const { return Future>::failure( Exception(api::ErrorCode::IMPLEMENTATION_IS_MISSING, "Endpoint to get token balance is not implemented." )); } - } -} + + Future> NodeTezosLikeBlockchainExplorer::getHelper(const std::string &url, + const std::string &field, + const std::unordered_map ¶ms, + const std::string &fallbackValue) const { + bool parseNumbersAsString = true; + auto networkId = getNetworkParameters().Identifier; + + std::string p, separator = "?"; + for (auto ¶m : params) { + p += fmt::format("{}{}={}", separator, param.first, param.second); + separator = "&"; + } + + return _http->GET(url + p, std::unordered_map()) + .json(parseNumbersAsString) + .mapPtr(getContext(), [field, networkId, fallbackValue] (const HttpRequest::JsonResult& result) { + auto& json = *std::get<1>(result); + if (!json.IsArray() || json.Size() == 0 || !json[0].IsString()) { + throw make_exception(api::ErrorCode::HTTP_ERROR, fmt::format("Failed to get {} for {}", field, networkId)); + } + std::string value = json[0].GetString(); + if (value == "0" && !fallbackValue.empty()) { + value = fallbackValue; + } + return std::make_shared(value); + }); + } + + } // namespace core +} // namespace ledger diff --git a/core/src/wallet/tezos/explorers/NodeTezosLikeBlockchainExplorer.h b/core/src/wallet/tezos/explorers/NodeTezosLikeBlockchainExplorer.h index fcd2211c83..8cad4416e0 100644 --- a/core/src/wallet/tezos/explorers/NodeTezosLikeBlockchainExplorer.h +++ b/core/src/wallet/tezos/explorers/NodeTezosLikeBlockchainExplorer.h @@ -28,11 +28,9 @@ * */ - #ifndef LEDGER_CORE_NODETEZOSLIKEBLOCKCHAINEXPLORER_H #define LEDGER_CORE_NODETEZOSLIKEBLOCKCHAINEXPLORER_H -#include #include #include #include @@ -42,80 +40,71 @@ namespace ledger { namespace core { - using LedgerApiBlockchainExplorer = AbstractLedgerApiBlockchainExplorer; - class NodeTezosLikeBlockchainExplorer : public TezosLikeBlockchainExplorer, - public LedgerApiBlockchainExplorer, - public DedicatedContext, - public std::enable_shared_from_this { + class NodeTezosLikeBlockchainExplorer : public TezosLikeBlockchainExplorer + { public: - NodeTezosLikeBlockchainExplorer(const std::shared_ptr &context, - const std::shared_ptr &http, - const api::TezosLikeNetworkParameters ¶meters, - const std::shared_ptr &configuration); + NodeTezosLikeBlockchainExplorer( + const std::shared_ptr &context, + const std::shared_ptr &http, + const api::TezosLikeNetworkParameters ¶meters, + const std::shared_ptr &configuration); Future> - getBalance(const std::vector &addresses) override; + getBalance(const TezosLikeKeychain::Address &address) const override; Future> - getFees() override; + getFees() const override; Future> - getGasPrice() override; - - Future pushLedgerApiTransaction(const std::vector &transaction) override; - - Future startSession() override; - - Future killSession(void *session) override; - - Future getRawTransaction(const String &transactionHash) override; - - Future pushTransaction(const std::vector &transaction) override; - - FuturePtr - getTransactions(const std::vector &addresses, - Option fromBlockHash = Option(), - Option session = Option()) override; - - FuturePtr getCurrentBlock() const override; + getGasPrice() const override; - FuturePtr - getTransactionByHash(const String &transactionHash) const override; + Future + pushLedgerApiTransaction(const std::vector &transaction) override; - Future getTimestamp() const override; + FuturePtr + getTransactions(const std::string& address, + const Either& token = {}) const override; - std::shared_ptr getExplorerContext() const override; - - api::TezosLikeNetworkParameters getNetworkParameters() const override; + FuturePtr + getCurrentBlock() const override; std::string getExplorerVersion() const override; Future> - getEstimatedGasLimit(const std::string &address) override; + getEstimatedGasLimit(const std::string &address) const override; - virtual Future> - getEstimatedGasLimit(const std::shared_ptr &transaction) override; + Future> + getEstimatedGasLimit(const std::shared_ptr &tx) const override; Future> - getStorage(const std::string &address) override; - - Future> getCounter(const std::string &address) override; + getStorage(const std::string &address) const override; - Future> forgeKTOperation(const std::shared_ptr &tx) override ; + Future> + forgeKTOperation(const std::shared_ptr &tx) const override; - Future getManagerKey(const std::string &address) override; + Future + getManagerKey(const std::string &address) const override; - Future isAllocated(const std::string &address) override; + Future + isAllocated(const std::string &address) const override; - Future getCurrentDelegate(const std::string &address) override; + Future + getCurrentDelegate(const std::string &address) const override; - Future isFunded(const std::string &address) override; Future> - getTokenBalance(const std::string& accountAddress, const std::string& tokenAddress) const override; + getCounter(const std::string &address) const override; + + Future + isFunded(const std::string &address) const override; + + Future + isDelegate(const std::string &address) const override; - Future isDelegate(const std::string &address) override; + Future> + getTokenBalance(const std::string& accountAddress, + const std::string& tokenAddress) const override; private: /* @@ -129,12 +118,14 @@ namespace ledger { Future> getHelper(const std::string &url, const std::string &field, - const std::unordered_map ¶ms = std::unordered_map(), - const std::string &fallbackValue = ""); + const std::unordered_map ¶ms = {}, + const std::string &fallbackValue = "") const; - api::TezosLikeNetworkParameters _parameters; + private: std::string _explorerVersion; }; - } -} + + } // namespace core +} // namespace ledger + #endif //LEDGER_CORE_NODETEZOSLIKEBLOCKCHAINEXPLORER_H diff --git a/core/src/wallet/tezos/explorers/TezosLikeBlockchainExplorer.cpp b/core/src/wallet/tezos/explorers/TezosLikeBlockchainExplorer.cpp index ebebe980f0..6c789d92a9 100644 --- a/core/src/wallet/tezos/explorers/TezosLikeBlockchainExplorer.cpp +++ b/core/src/wallet/tezos/explorers/TezosLikeBlockchainExplorer.cpp @@ -28,8 +28,11 @@ * */ - #include "TezosLikeBlockchainExplorer.h" +#include +#include +#include +#include #include #include #include @@ -43,32 +46,52 @@ namespace ledger { namespace core { TezosLikeBlockchainExplorer::TezosLikeBlockchainExplorer( + const std::shared_ptr &context, + const std::shared_ptr &http, + const api::TezosLikeNetworkParameters ¶meters, const std::shared_ptr &configuration, - const std::vector &matchableKeys) : ConfigurationMatchable(matchableKeys) { + const std::vector &matchableKeys) + : TezosLikeLedgerApiBlockchainExplorer() + , ConfigurationMatchable(matchableKeys) + , DedicatedContext(context) + , _parameters(parameters) + { + _http = http; setConfiguration(configuration); _rpcNode = configuration->getString(api::TezosConfiguration::TEZOS_NODE) - .value_or(api::TezosConfigurationDefaults::TEZOS_DEFAULT_NODE); + .value_or(api::TezosConfigurationDefaults::TEZOS_DEFAULT_NODE); + } + + Future + TezosLikeBlockchainExplorer::pushTransaction(const std::vector &transaction) { + return pushLedgerApiTransaction(transaction); } - Future> TezosLikeBlockchainExplorer::forgeKTOperation(const std::shared_ptr &tx, - const std::shared_ptr &context, - const std::shared_ptr &http, - const std::string &rpcNode) { + FuturePtr + TezosLikeBlockchainExplorer::getTransactionByHash(const String &transactionHash) const { + return getLedgerApiTransactionByHash(transactionHash); + } + + Future> + TezosLikeBlockchainExplorer::forgeKTOperation(const std::shared_ptr &tx, + const std::shared_ptr &context, + const std::shared_ptr &http, + const std::string &rpcNode) { std::string params; switch (tx->getType()) { case api::TezosOperationTag::OPERATION_TAG_TRANSACTION: params = fmt::format("\"parameters\":" - "{{\"entrypoint\":\"do\",\"value\":[" - "{{\"prim\":\"DROP\"}}," - "{{\"prim\":\"NIL\",\"args\":[{{\"prim\":\"operation\"}}]}}," - "{{\"prim\":\"PUSH\",\"args\":[{{\"prim\":\"key_hash\"}},{{\"string\":\"{}\"}}]}}," - "{{\"prim\":\"IMPLICIT_ACCOUNT\"}}," - "{{\"prim\":\"PUSH\",\"args\":[{{\"prim\":\"mutez\"}},{{\"int\":\"{}\"}}]}}," - "{{\"prim\":\"UNIT\"}}," - "{{\"prim\":\"TRANSFER_TOKENS\"}}," - "{{\"prim\":\"CONS\"}}]}}", - tx->getReceiver()->toBase58(), - tx->getValue()->toString()); + "{{\"entrypoint\":\"do\",\"value\":[" + "{{\"prim\":\"DROP\"}}," + "{{\"prim\":\"NIL\",\"args\":[{{\"prim\":\"operation\"}}]}}," + "{{\"prim\":\"PUSH\",\"args\":[{{\"prim\":\"key_hash\"}},{{\"string\":\"{}\"}}]}}," + "{{\"prim\":\"IMPLICIT_ACCOUNT\"}}," + "{{\"prim\":\"PUSH\",\"args\":[{{\"prim\":\"mutez\"}},{{\"int\":\"{}\"}}]}}," + "{{\"prim\":\"UNIT\"}}," + "{{\"prim\":\"TRANSFER_TOKENS\"}}," + "{{\"prim\":\"CONS\"}}]}}", + tx->getReceiver()->toBase58(), + tx->getValue()->toString()); break; case api::TezosOperationTag::OPERATION_TAG_DELEGATION: params = "\"parameters\":" @@ -118,15 +141,16 @@ namespace ledger { }); } - Future TezosLikeBlockchainExplorer::getManagerKey(const std::string &address, - const std::shared_ptr &context, - const std::shared_ptr &http, - const std::string &rpcNode) { + Future + TezosLikeBlockchainExplorer::getManagerKey(const std::string &address, + const std::shared_ptr &context, + const std::shared_ptr &http, + const std::string &rpcNode) { const bool parseNumbersAsString = true; std::unordered_map headers{{"Content-Type", "application/json"}}; return http->GET(fmt::format("/chains/main/blocks/head/context/contracts/{}/manager_key", address), - std::unordered_map{}, - rpcNode) + std::unordered_map{}, + rpcNode) .json(parseNumbersAsString) .map(context, [](const HttpRequest::JsonResult &result) { auto &json = *std::get<1>(result); @@ -143,33 +167,40 @@ namespace ledger { }); } - Future TezosLikeBlockchainExplorer::isAllocated(const std::string &address, - const std::shared_ptr &context, - const std::shared_ptr &http, - const std::string &rpcNode) { + Future + TezosLikeBlockchainExplorer::isAllocated(const std::string &address, + const std::shared_ptr &context, + const std::shared_ptr &http, + const std::string &rpcNode) { const bool parseNumbersAsString = true; std::unordered_map headers{{"Content-Type", "application/json"}}; return http->GET(fmt::format("/chains/main/blocks/head/context/contracts/{}", address), - std::unordered_map{}, - rpcNode) + std::unordered_map{}, + rpcNode) .json(parseNumbersAsString) .map(context, [](const HttpRequest::JsonResult &result) { - return true; - }).recover(context, [] (const Exception &exception) { + return true; + }).recover(context, [](const Exception &exception) { return false; }); } - Future TezosLikeBlockchainExplorer::getCurrentDelegate(const std::string &address, - const std::shared_ptr &context, - const std::shared_ptr &http, - const std::string &rpcNode) { + Future + TezosLikeBlockchainExplorer::isAllocated(const std::string &address) const { + return isAllocated(address, getExplorerContext(), _http, getRPCNodeEndpoint()); + } + + Future + TezosLikeBlockchainExplorer::getCurrentDelegate(const std::string &address, + const std::shared_ptr &context, + const std::shared_ptr &http, + const std::string &rpcNode) { const bool parseNumbersAsString = true; std::unordered_map headers{{"Content-Type", "application/json"}}; return http->GET(fmt::format("/chains/main/blocks/head/context/contracts/{}/delegate", address), - std::unordered_map{}, - rpcNode) + std::unordered_map{}, + rpcNode) .json(parseNumbersAsString) .map(context, [](const HttpRequest::JsonResult &result) { auto &json = *std::get<1>(result); @@ -177,19 +208,26 @@ namespace ledger { return ""; } return json.GetString(); - }).recover(context, [] (const Exception &exception) { + }).recover(context, [](const Exception &exception) { // FIXME: throw exception to signal errors (i.e: network error) return ""; }); } - Future TezosLikeBlockchainExplorer::getChainId(const std::shared_ptr &context, - const std::shared_ptr &http) { + Future + TezosLikeBlockchainExplorer::getCurrentDelegate(const std::string &address) const { + return getCurrentDelegate(address, getExplorerContext(), _http, getRPCNodeEndpoint()); + } + + Future + TezosLikeBlockchainExplorer::getChainId(const std::shared_ptr &context, + const std::shared_ptr &http) const + { const bool parseNumbersAsString = true; std::unordered_map headers{{"Content-Type", "application/json"}}; return http->GET(fmt::format("/chains/main/chain_id"), - std::unordered_map{}, - getRPCNodeEndpoint()) + std::unordered_map{}, + getRPCNodeEndpoint()) .json(parseNumbersAsString) .map(context, [](const HttpRequest::JsonResult &result) { auto &json = *std::get<1>(result); @@ -198,7 +236,7 @@ namespace ledger { return ""; } return json.GetString(); - }).recover(context, [] (const Exception &exception) { + }).recover(context, [](const Exception &exception) { return ""; }); } @@ -206,7 +244,7 @@ namespace ledger { Future> TezosLikeBlockchainExplorer::getEstimatedGasLimit( const std::shared_ptr &http, const std::shared_ptr &context, - const std::shared_ptr &tx) + const std::shared_ptr &tx) const { return getChainId(context, http).flatMapPtr(context, [=](const std::string& result)-> FuturePtr { return getEstimatedGasLimit(http, context, tx, result); @@ -216,8 +254,8 @@ namespace ledger { Future> TezosLikeBlockchainExplorer::getEstimatedGasLimit( const std::shared_ptr &http, const std::shared_ptr &context, - const std::shared_ptr &tx, - const std::string& strChainID) + const std::shared_ptr &tx, + const std::string& strChainID) const { const auto postPath = fmt::format("/chains/{}/blocks/head/helpers/scripts/run_operation", strChainID); @@ -264,7 +302,7 @@ namespace ledger { api::ErrorCode::HTTP_ERROR, std::make_shared(result), "failed to get operation_result in simulation"); - } + } auto &operationResult = content.GetObject()["metadata"] .GetObject()["operation_result"]; @@ -318,92 +356,5 @@ namespace ledger { }); } - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - } -} + } // namespace core +} // namespace ledger diff --git a/core/src/wallet/tezos/explorers/TezosLikeBlockchainExplorer.h b/core/src/wallet/tezos/explorers/TezosLikeBlockchainExplorer.h index 2096a17389..0573f567bd 100644 --- a/core/src/wallet/tezos/explorers/TezosLikeBlockchainExplorer.h +++ b/core/src/wallet/tezos/explorers/TezosLikeBlockchainExplorer.h @@ -32,21 +32,25 @@ #ifndef LEDGER_CORE_TEZOSLIKEBLOCKCHAINEXPLORER_H #define LEDGER_CORE_TEZOSLIKEBLOCKCHAINEXPLORER_H -#include - #include #include #include +#include #include -#include +#include #include #include #include #include + #include -#include +#include #include -#include + +#include +#include +#include +#include namespace ledger { namespace core { @@ -57,8 +61,8 @@ namespace ledger { bool isDelegatable = false) : address(a), spendable(isSpendable), - delegatable(isDelegatable) { - }; + delegatable(isDelegatable) + {} std::string address; bool spendable; @@ -88,26 +92,6 @@ namespace ledger { type = api::TezosOperationTag::OPERATION_TAG_NONE; status = 0; } - - TezosLikeBlockchainExplorerTransaction(const TezosLikeBlockchainExplorerTransaction &cpy) { - this->hash = cpy.hash; - this->receivedAt = cpy.receivedAt; - this->value = cpy.value; - this->fees = cpy.fees; - this->gas_limit = cpy.gas_limit; - this->storage_limit = cpy.storage_limit; - this->receiver = cpy.receiver; - this->sender = cpy.sender; - this->block = cpy.block; - this->confirmations = cpy.confirmations; - this->type = cpy.type; - this->publicKey = cpy.publicKey; - this->originatedAccount = cpy.originatedAccount; - this->status = cpy.status; - this->originatedAccountUid = cpy.originatedAccountUid; - this->originatedAccountAddress = cpy.originatedAccountAddress; - } - }; struct GasLimit { @@ -121,56 +105,94 @@ namespace ledger { reveal(r), transaction(t) {} }; + struct TezosLikeBlchainExplorerTransactionsBulk { + std::vector transactions; + bool hasNext{false}; + }; + + + class TezosLikeTransactionsParser; + class TezosLikeTransactionsBulkParser; + class TezosLikeBlockParser; + + using TezosLikeLedgerApiBlockchainExplorer = AbstractLedgerApiBlockchainExplorer< + TezosLikeBlockchainExplorerTransaction, + TezosLikeBlchainExplorerTransactionsBulk, + TezosLikeTransactionsParser, + TezosLikeTransactionsBulkParser, + TezosLikeBlockParser, + api::TezosLikeNetworkParameters>; + class TezosLikeTransactionApi; - class TezosLikeBlockchainExplorer : public ConfigurationMatchable, - public AbstractBlockchainExplorer { + class TezosLikeBlockchainExplorer : protected TezosLikeLedgerApiBlockchainExplorer, + public ConfigurationMatchable, + public DedicatedContext + { public: - typedef ledger::core::Block Block; + using Block = ledger::core::Block; using Transaction = TezosLikeBlockchainExplorerTransaction; + using TransactionsBulk = TezosLikeBlchainExplorerTransactionsBulk; - TezosLikeBlockchainExplorer(const std::shared_ptr &configuration, - const std::vector &matchableKeys); + TezosLikeBlockchainExplorer( + const std::shared_ptr &context, + const std::shared_ptr &http, + const api::TezosLikeNetworkParameters ¶meters, + const std::shared_ptr &configuration, + const std::vector &matchableKeys); + + Future + pushTransaction(const std::vector &transaction); + + FuturePtr + getTransactionByHash(const String &transactionHash) const; virtual Future> - getBalance(const std::vector &addresses) = 0; + getBalance(const TezosLikeKeychain::Address &address) const = 0; virtual Future> - getFees() = 0; + getFees() const = 0; /// Return the gas Price of the last block in picotez (e-12) per gas virtual Future> - getGasPrice() = 0; + getGasPrice() const = 0; + + virtual FuturePtr + getTransactions(const std::string& address, + const Either& token = {}) const = 0; + + virtual FuturePtr + getCurrentBlock() const = 0; virtual Future> - getEstimatedGasLimit(const std::string &address) = 0; + getEstimatedGasLimit(const std::string &address) const = 0; - virtual Future> getEstimatedGasLimit( - const std::shared_ptr &tx) = 0; + virtual Future> + getEstimatedGasLimit(const std::shared_ptr &tx) const = 0; Future> getEstimatedGasLimit( const std::shared_ptr &http, const std::shared_ptr &context, - const std::shared_ptr &transaction); + const std::shared_ptr &transaction) const; Future> getEstimatedGasLimit( const std::shared_ptr &http, const std::shared_ptr &context, const std::shared_ptr &transaction, - const std::string &chainId); + const std::string &chainId) const; Future getChainId( const std::shared_ptr &context, - const std::shared_ptr &http); + const std::shared_ptr &http) const; virtual Future> - getStorage(const std::string &address) = 0; + getStorage(const std::string &address) const = 0; virtual Future> - getCounter(const std::string &address) = 0; + getCounter(const std::string &address) const = 0; - virtual Future> forgeKTOperation(const std::shared_ptr &tx) = 0; + virtual Future> forgeKTOperation(const std::shared_ptr &tx) const = 0; // This a helper to manage legacy KT accounts // WARNING: we will only support removing delegation and transfer from KT to implicit account static Future> forgeKTOperation(const std::shared_ptr &tx, @@ -178,7 +200,7 @@ namespace ledger { const std::shared_ptr &http, const std::string &rpcNode); - virtual Future getManagerKey(const std::string &address) = 0; + virtual Future getManagerKey(const std::string &address) const = 0; // This a helper to manage legacy KT accounts // WARNING: we will only support removing delegation and transfer from KT to implicit account static Future getManagerKey(const std::string &address, @@ -186,22 +208,22 @@ namespace ledger { const std::shared_ptr &http, const std::string &rpcNode); - virtual Future isAllocated(const std::string &address) = 0; + virtual Future isAllocated(const std::string &address) const = 0; static Future isAllocated(const std::string &address, const std::shared_ptr &context, const std::shared_ptr &http, const std::string &rpcNode); - virtual Future getCurrentDelegate(const std::string &address) = 0; + virtual Future getCurrentDelegate(const std::string &address) const = 0; static Future getCurrentDelegate(const std::string &address, const std::shared_ptr &context, const std::shared_ptr &http, const std::string &rpcNode); /// Check that the account is funded. - virtual Future isFunded(const std::string &address) = 0; + virtual Future isFunded(const std::string &address) const = 0; - virtual Future isDelegate(const std::string &address) = 0; + virtual Future isDelegate(const std::string &address) const = 0; /// Get a token balance for an account virtual Future> @@ -209,12 +231,24 @@ namespace ledger { const std::string& tokenAddress) const = 0; protected: - std::string getRPCNodeEndpoint() const { + inline const std::string& getRPCNodeEndpoint() const { return _rpcNode; - }; + } + + inline api::TezosLikeNetworkParameters getNetworkParameters() const override { + return _parameters; + } + + inline std::shared_ptr getExplorerContext() const override { + return getContext(); + } + private: std::string _rpcNode; + api::TezosLikeNetworkParameters _parameters; }; - } -} + + } // namespace core +} // namespace ledger + #endif //LEDGER_CORE_TEZOSLIKEBLOCKCHAINEXPLORER_H diff --git a/core/src/wallet/tezos/factories/TezosLikeWalletFactory.cpp b/core/src/wallet/tezos/factories/TezosLikeWalletFactory.cpp index 82111f8dad..440132ff9d 100644 --- a/core/src/wallet/tezos/factories/TezosLikeWalletFactory.cpp +++ b/core/src/wallet/tezos/factories/TezosLikeWalletFactory.cpp @@ -39,7 +39,7 @@ #include #include #include -#include +#include #define STRING(key, def) entry.configuration->getString(key).value_or(def) @@ -83,7 +83,7 @@ namespace ledger { std::weak_ptr p = pool; synchronizerFactory = Option([p, explorer]() { auto pool = p.lock(); - return std::make_shared(pool, explorer); + return std::make_shared(pool, explorer); }); } } @@ -164,4 +164,4 @@ namespace ledger { } } -} \ No newline at end of file +} diff --git a/core/src/wallet/tezos/factories/TezosLikeWalletFactory.h b/core/src/wallet/tezos/factories/TezosLikeWalletFactory.h index f633eaeff2..16f9009dd7 100644 --- a/core/src/wallet/tezos/factories/TezosLikeWalletFactory.h +++ b/core/src/wallet/tezos/factories/TezosLikeWalletFactory.h @@ -35,7 +35,7 @@ #include #include -#include +#include #include #include diff --git a/core/src/wallet/tezos/synchronizers/TezosLikeAccountSynchronizer.cpp b/core/src/wallet/tezos/synchronizers/TezosLikeAccountSynchronizer.cpp new file mode 100644 index 0000000000..00fda2cdfb --- /dev/null +++ b/core/src/wallet/tezos/synchronizers/TezosLikeAccountSynchronizer.cpp @@ -0,0 +1,251 @@ +/* + * + * TezosLikeAccountSynchronizer + * + * Created by El Khalil Bellakrid on 27/04/2019. + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Ledger + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +#include "TezosLikeAccountSynchronizer.hpp" + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace ledger { +namespace core { + +TezosLikeAccountSynchronizer::TezosLikeAccountSynchronizer( + const std::shared_ptr& pool, + const std::shared_ptr& explorer) + : DedicatedContext(pool->getDispatcher()->getThreadPoolExecutionContext("synchronizers")) + , _explorer(explorer) + , _account(nullptr) + , _notifier(nullptr) + , _logger(nullptr) + , _m() +{} + +auto TezosLikeAccountSynchronizer::synchronize( + const std::shared_ptr& account) + -> std::shared_ptr> +{ + std::lock_guard lock(_m); + if (!_account) { + _account = account; + _notifier = std::make_shared>(); + _logger = logger::trace( + fmt::format("synchronize_{}", account->getAccountUid()), + fmt::format("{}/{}/{}", account->getWallet()->getName(), + account->getWallet()->getName(), account->getIndex()), + account->logger()); + _start = DateUtils::now(); + + performSynchronization().onComplete(getContext(), [this](const Try& result) { + std::lock_guard lock(_m); + if (result.isFailure()) { + _notifier->failure(result.getFailure()); + } else { + _notifier->success(result.getValue()); + } + _account = nullptr; + _notifier = nullptr; + _logger = nullptr; + }); + } + return _notifier; +} + +auto TezosLikeAccountSynchronizer::performSynchronization() const -> Future +{ + const auto state = std::make_shared(_account + ->getInternalPreferences() + ->getSubPreferences("TezosLikeAccountSynchronizerr") + ->getObject("state").getValueOr(SavedState())); + + + _logger->info( + "Starting synchronization for account#{} ({}) of wallet {} at {}.", + _account->getIndex(), + _account->getKeychain()->getRestoreKey(), + _account->getWallet()->getName(), + DateUtils::toJSON(_start)); + + const auto block = updateCurrentBlock(); + + const auto syncs = [this, state]() { + const auto originatedAccounts = _account->getOriginatedAccounts(); + auto syncs = std::vector>(); + // synchronization of all XTZ transactions + syncs.push_back(synchronizeTransactions( + _account->getKeychain()->getAddress()->toString(), state)); + // synchronization of all originated accounts + std::transform( + std::begin(originatedAccounts), + std::end(originatedAccounts), + std::back_inserter(syncs), + [this, state](const auto& account) { + return synchronizeTransactions(account->getAddress(), state); + }); + + return syncs; + }(); + + auto futures = std::make_tuple(block, async::sequence(getContext(), syncs)); + return async::sequence(getContext(), futures) + .map( + getContext(), + [this, state](const auto& tuple) { + const auto duration = std::chrono::duration_cast< + std::chrono::milliseconds>(DateUtils::now() - _start); + _logger->info( + "End synchronization of account#{} of wallet {} in {}.", + _account->getIndex(), + _account->getWallet()->getName(), + DurationUtils::formatDuration(duration)); + + const auto block = std::get<0>(tuple); + const auto syncs = std::get<1>(tuple); + + const auto newOperations = std::accumulate( + std::begin(syncs), std::end(syncs), 0); + + if (block.height > 0) { + _account->getInternalPreferences() + ->editor() + ->putObject("state", *state) + ->commit(); + } + + return Result(block.height, newOperations); + }) + .recoverWith( + ImmediateExecutionContext::INSTANCE, + [this](const Exception& ex) { + const auto duration = std::chrono::duration_cast< + std::chrono::milliseconds>(DateUtils::now() - _start); + _logger->error( + "Error during synchronization for account#{} of wallet {} in {}.", + _account->getIndex(), + _account->getWallet()->getName(), + DurationUtils::formatDuration(duration)); + _logger->error( + "Due to {}, {}.", + api::to_string(ex.getErrorCode()), + ex.getMessage()); + + return Future::failure(ex); + }); +} + +template +auto TezosLikeAccountSynchronizer::synchronizeTransactions( + const std::string& address, + const std::shared_ptr& state, + uint32_t nbTxns) const -> Future +{ + const auto uid = [this, address]() { + if (orig) { + return TezosLikeAccountDatabaseHelper::createOriginatedAccountUid( + _account->getAccountUid(), address); + } + return std::string(""); + }(); + return _explorer->getTransactions({address}, state->getOffset(address)) + .flatMap( + _account->getContext(), + [this, address, state, uid, nbTxns]( + const std::shared_ptr& bulk) + { + auto out = std::vector(); + for (auto& tx : bulk->transactions) { + if (tx.block.hasValue() && tx.block->height > state->blockHeight) { + state->blockHeight = tx.block->height; + state->blockHash = tx.block->hash; + } + + if (orig) { + tx.originatedAccountUid = uid; + tx.originatedAccountAddress = address; + } + _account->interpretTransaction(tx, out); + } + + const auto tryPutTx = _account->bulkInsert(out); + if (tryPutTx.isFailure()) { + _logger->error( + "Insert transaction bulk failed beacause: {}", + tryPutTx.getFailure().getMessage()); + + throw make_exception( + api::ErrorCode::RUNTIME_ERROR, + "Synchronization failed ({})", + tryPutTx.exception().getValue().getMessage()); + } + + const auto count = tryPutTx.getValue(); + state->addOffset(address, count); + + _logger->info( + "Successfully inserted {} transactions for account {}.", + count, _account->getAccountUid()); + + if (bulk->hasNext) { + return synchronizeTransactions(address, state, nbTxns + count); + } else { + return Future::successful(nbTxns + count); + } + }); +} + +auto TezosLikeAccountSynchronizer::updateCurrentBlock() const -> Future +{ + return _explorer->getCurrentBlock() + .map( + _account->getContext(), + [this](const std::shared_ptr& block) { + if (block) { + soci::session sql(_account->getWallet()->getDatabase()->getPool()); + soci::transaction tr(sql); + try { + _account->putBlock(sql, *block); + tr.commit(); + } catch(...) { + tr.rollback(); + } + return *block; + } + return Block(); + }); +} + +} // namespace core +} // namespace ledger diff --git a/core/src/wallet/tezos/synchronizers/TezosLikeAccountSynchronizer.h b/core/src/wallet/tezos/synchronizers/TezosLikeAccountSynchronizer.h deleted file mode 100644 index 9dbae40e4e..0000000000 --- a/core/src/wallet/tezos/synchronizers/TezosLikeAccountSynchronizer.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * - * TezosLikeAccountSynchronizer - * - * Created by El Khalil Bellakrid on 27/04/2019. - * - * The MIT License (MIT) - * - * Copyright (c) 2019 Ledger - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - - -#ifndef LEDGER_CORE_TEZOSLIKEACCOUNTSYNCHRONIZER_H -#define LEDGER_CORE_TEZOSLIKEACCOUNTSYNCHRONIZER_H -#include -namespace ledger { - namespace core { - class TezosLikeAccount; - class TezosLikeAccountSynchronizer : public AbstractAccountSynchronizer {}; - } -} -#endif //LEDGER_CORE_TEZOSLIKEACCOUNTSYNCHRONIZER_H diff --git a/core/src/wallet/tezos/synchronizers/TezosLikeAccountSynchronizer.hpp b/core/src/wallet/tezos/synchronizers/TezosLikeAccountSynchronizer.hpp new file mode 100644 index 0000000000..5481064df6 --- /dev/null +++ b/core/src/wallet/tezos/synchronizers/TezosLikeAccountSynchronizer.hpp @@ -0,0 +1,137 @@ +/* + * + * TezosLikeAccountSynchronizer + * + * Created by El Khalil Bellakrid on 27/04/2019. + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Ledger + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + + +#ifndef LEDGER_CORE_TEZOSLIKEACCOUNTSYNCHRONIZER_H +#define LEDGER_CORE_TEZOSLIKEACCOUNTSYNCHRONIZER_H + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace ledger { +namespace core { + +class TezosLikeAccount; + +class TezosLikeAccountSynchronizer : public DedicatedContext +{ +public: + struct Result + { + uint64_t lastBlockHeight{0}; + uint32_t newOperations{0}; + + Result(uint64_t lastBlockHeight, uint32_t newOperations) + : lastBlockHeight(lastBlockHeight) + , newOperations(newOperations) + {} + }; + + TezosLikeAccountSynchronizer( + const std::shared_ptr& pool, + const std::shared_ptr& explorer); + + auto synchronize(const std::shared_ptr& account) + -> std::shared_ptr>; + +private: + struct SavedState + { + uint64_t blockHeight{0}; + std::string blockHash{""}; + std::unordered_map offsets{}; + + SavedState( + uint64_t blockHeight, + std::string blockHash, + std::unordered_map offsets) + : blockHeight(blockHeight) + , blockHash(blockHash) + , offsets(std::move(offsets)) + {} + + SavedState() = default; + + uint32_t getOffset(const std::string& address) const { + if (offsets.count(address) > 0) { + return offsets.at(address); + } + return 0; + } + + void addOffset(const std::string& address, uint32_t offset) { + if (offsets.count(address) == 0) { + offsets[address] = 0; + } + offsets[address] += offset; + } + + template + void serialize(Archive& archive) + { + archive(blockHeight, blockHash, offsets); + } + }; + + auto performSynchronization() const -> Future; + + template + auto synchronizeTransactions( + const std::string& address, + const std::shared_ptr& state, + uint32_t nbTxns = 0) const -> Future; + + auto updateCurrentBlock() const -> Future; + +private: + std::shared_ptr _explorer; + std::shared_ptr _account; + std::shared_ptr> _notifier; + std::shared_ptr _logger; + std::chrono::system_clock::time_point _start; + std::mutex _m; +}; + +} // namespace core +} // namespace ledger + +#endif //LEDGER_CORE_TEZOSLIKEACCOUNTSYNCHRONIZER_H diff --git a/core/src/wallet/tezos/synchronizers/TezosLikeBlockchainExplorerAccountSynchronizer.cpp b/core/src/wallet/tezos/synchronizers/TezosLikeBlockchainExplorerAccountSynchronizer.cpp deleted file mode 100644 index c1f9ab67ce..0000000000 --- a/core/src/wallet/tezos/synchronizers/TezosLikeBlockchainExplorerAccountSynchronizer.cpp +++ /dev/null @@ -1,110 +0,0 @@ -/* - * - * TezosLikeBlockchainExplorerAccountSynchronizer - * - * Created by El Khalil Bellakrid on 27/04/2019. - * - * The MIT License (MIT) - * - * Copyright (c) 2019 Ledger - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - - -#include "TezosLikeBlockchainExplorerAccountSynchronizer.h" -#include - -namespace ledger { - namespace core { - TezosLikeBlockchainExplorerAccountSynchronizer::TezosLikeBlockchainExplorerAccountSynchronizer( - const std::shared_ptr &pool, - const std::shared_ptr &explorer) : - DedicatedContext(pool->getDispatcher()->getThreadPoolExecutionContext("synchronizers")) { - _explorer = explorer; - } - - void TezosLikeBlockchainExplorerAccountSynchronizer::updateCurrentBlock( - std::shared_ptr &buddy, - const std::shared_ptr &context) { - _explorer->getCurrentBlock().onComplete(context, - [buddy](const TryPtr &block) { - if (block.isSuccess()) { - soci::session sql( - buddy->account->getWallet()->getDatabase()->getPool()); - soci::transaction tr(sql); - try { - buddy->account->putBlock(sql, *block.getValue()); - tr.commit(); - } catch(...) { - tr.rollback(); - } - } - }); - } - - void TezosLikeBlockchainExplorerAccountSynchronizer::updateTransactionsToDrop(soci::session &sql, - std::shared_ptr &buddy, - const std::string &accountUid) { - //Get all transactions in DB that may be dropped (txs without block_uid) - soci::rowset rows = (sql.prepare - << "SELECT op.uid, xtz_op.transaction_hash FROM operations AS op " - "LEFT OUTER JOIN tezos_operations AS xtz_op ON xtz_op.uid = op.uid " - "WHERE op.block_uid IS NULL AND op.account_uid = :uid ", soci::use(accountUid)); - - for (auto &row : rows) { - if (row.get_indicator(0) != soci::i_null && row.get_indicator(1) != soci::i_null) { - buddy->transactionsToDrop.insert( - std::pair(row.get(1), row.get(0))); - } - } - } - - std::shared_ptr> - TezosLikeBlockchainExplorerAccountSynchronizer::synchronize(const std::shared_ptr &account) { - return synchronizeAccount(account); - } - - bool TezosLikeBlockchainExplorerAccountSynchronizer::isSynchronizing() const { - return _notifier != nullptr; - } - - void TezosLikeBlockchainExplorerAccountSynchronizer::reset(const std::shared_ptr &account, - const std::chrono::system_clock::time_point &toDate) { - - } - - std::shared_ptr - TezosLikeBlockchainExplorerAccountSynchronizer::getSharedFromThis() { - return shared_from_this(); - } - - std::shared_ptr - TezosLikeBlockchainExplorerAccountSynchronizer::getSynchronizerContext() { - return getContext(); - } - - void TezosLikeBlockchainExplorerAccountSynchronizer::interpretTransaction( - const ledger::core::TezosLikeBlockchainExplorerTransaction &transaction, - const std::shared_ptr &buddy, std::vector &out) { - buddy->account->interpretTransaction(transaction, out); - } - } -} diff --git a/core/src/wallet/tezos/synchronizers/TezosLikeBlockchainExplorerAccountSynchronizer.h b/core/src/wallet/tezos/synchronizers/TezosLikeBlockchainExplorerAccountSynchronizer.h deleted file mode 100644 index bc4f4b93fa..0000000000 --- a/core/src/wallet/tezos/synchronizers/TezosLikeBlockchainExplorerAccountSynchronizer.h +++ /dev/null @@ -1,85 +0,0 @@ -/* - * - * TezosLikeBlockchainExplorerAccountSynchronizer - * - * Created by El Khalil Bellakrid on 27/04/2019. - * - * The MIT License (MIT) - * - * Copyright (c) 2019 Ledger - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - - -#ifndef LEDGER_CORE_TEZOSLIKEBLOCKCHAINEXPLORERACCOUNTSYNCHRONIZER_H -#define LEDGER_CORE_TEZOSLIKEBLOCKCHAINEXPLORERACCOUNTSYNCHRONIZER_H -#include -#include -#include -#include -#include -#include -#include - -namespace ledger { - namespace core { - - class TezosLikeAccount; - - using TezosBlockchainAccountSynchronizer = AbstractBlockchainExplorerAccountSynchronizer; - - class TezosLikeBlockchainExplorerAccountSynchronizer : public TezosBlockchainAccountSynchronizer, - public TezosLikeAccountSynchronizer, - public DedicatedContext, - public std::enable_shared_from_this { - public: - - TezosLikeBlockchainExplorerAccountSynchronizer(const std::shared_ptr &pool, - const std::shared_ptr &explorer); - - void updateCurrentBlock( - std::shared_ptr &buddy, - const std::shared_ptr &context) override; - - void updateTransactionsToDrop(soci::session &sql, - std::shared_ptr &buddy, - const std::string &accountUid) override; - - std::shared_ptr > - synchronize(const std::shared_ptr &account) override; - - void reset(const std::shared_ptr &account, - const std::chrono::system_clock::time_point &toDate) override; - - bool isSynchronizing() const override; - - void interpretTransaction(const Transaction& transaction, - const std::shared_ptr &buddy, - std::vector& out) override; - - private: - std::shared_ptr getSharedFromThis() override; - - std::shared_ptr getSynchronizerContext() override; - }; - } -} -#endif //LEDGER_CORE_TEZOSLIKEBLOCKCHAINEXPLORERACCOUNTSYNCHRONIZER_H