diff --git a/libraries/app/api.cpp b/libraries/app/api.cpp index 62ec8681..8611f4fc 100644 --- a/libraries/app/api.cpp +++ b/libraries/app/api.cpp @@ -564,7 +564,7 @@ namespace graphene { namespace app { _app.p2p_node()->broadcast_transaction(trx); } - void network_broadcast_api::broadcast_transaction_with_callback( confirmation_callback cb, const signed_transaction& trx) + void network_broadcast_api::broadcast_transaction_with_callback(confirmation_callback cb, const signed_transaction& trx) { trx.validate(); _callbacks[trx.id()] = cb; @@ -741,7 +741,7 @@ namespace graphene { namespace app { { if( _account_subscriptions.size() ) { - map > broadcast_queue; + map > broadcast_queue; for( const auto& obj : objs ) { auto relevant = get_relevant_accounts( obj ); @@ -795,7 +795,7 @@ namespace graphene { namespace app { void database_api::on_objects_changed(const vector& ids) { vector my_objects; - map > broadcast_queue; + map > broadcast_queue; map< pair, vector > market_broadcast_queue; for(auto id : ids) { @@ -888,7 +888,7 @@ namespace graphene { namespace app { if(_market_subscriptions.size() == 0) return; - + const auto& ops = _db.get_applied_operations(); map< std::pair, vector> > subscribed_markets_ops; for(const auto& op : ops) diff --git a/programs/light_client/Account.hpp b/programs/light_client/Account.hpp index 943f0d06..4b58ce69 100644 --- a/programs/light_client/Account.hpp +++ b/programs/light_client/Account.hpp @@ -35,6 +35,9 @@ public: m_account.name = name.toStdString(); } void setAccountObject(const account_object& obj); + const account_object& accountObject()const { + return m_account; + } QString name()const { return QString::fromStdString(m_account.name); } QString memoKey()const; diff --git a/programs/light_client/ChainDataModel.cpp b/programs/light_client/ChainDataModel.cpp index 2d42449b..4c5e7592 100644 --- a/programs/light_client/ChainDataModel.cpp +++ b/programs/light_client/ChainDataModel.cpp @@ -1,6 +1,7 @@ #include "ChainDataModel.hpp" #include "Balance.hpp" #include "Operations.hpp" +#include "Transaction.hpp" #include #include @@ -26,11 +27,35 @@ void ChainDataModel::setDatabaseAPI(fc::api dbapi) { m_db_api = dbapi; m_rpc_thread->async([this] { m_global_properties = m_db_api->get_global_properties(); - m_db_api->subscribe_to_objects([this](const variant& v) { m_global_properties = v.as(); }, - {m_global_properties.id}); + m_db_api->subscribe_to_objects([this](const variant& v) { + m_global_properties = v.as(); + }, {m_global_properties.id}); + + m_dynamic_global_properties = m_db_api->get_dynamic_global_properties(); + m_db_api->subscribe_to_objects([this](const variant& d) { + m_dynamic_global_properties = d.as(); + }, {m_dynamic_global_properties.id}); }); } +void ChainDataModel::setNetworkAPI(fc::api napi) +{ + m_net_api = napi; +} + +void ChainDataModel::broadcast(Transaction* transaction) +{ + try { + m_net_api->broadcast_transaction_with_callback([transaction](const fc::variant&) { + transaction->setStatus(Transaction::Complete); + }, transaction->internalTransaction()); + transaction->setStatus(Transaction::Pending); + } catch (const fc::exception& e) { + transaction->setStatus(Transaction::Failed); + Q_EMIT exceptionThrown(QString::fromStdString(e.to_string())); + } +} + Asset* ChainDataModel::getAsset(ObjectId id) { auto& by_id_idx = m_assets.get(); diff --git a/programs/light_client/ChainDataModel.hpp b/programs/light_client/ChainDataModel.hpp index 6e9cf434..465925b3 100644 --- a/programs/light_client/ChainDataModel.hpp +++ b/programs/light_client/ChainDataModel.hpp @@ -34,6 +34,7 @@ typedef multi_index_container< > > account_multi_index_type; +class Transaction; class ChainDataModel : public QObject { Q_OBJECT @@ -52,8 +53,13 @@ public: ChainDataModel(fc::thread& t, QObject* parent = nullptr); void setDatabaseAPI(fc::api dbapi); + void setNetworkAPI(fc::api napi); const graphene::chain::global_property_object& global_properties() const { return m_global_properties; } + const graphene::chain::dynamic_global_property_object& dynamic_global_properties() const { return m_dynamic_global_properties; } + +public Q_SLOTS: + void broadcast(Transaction* transaction); Q_SIGNALS: void queueExecute(const std::function&); @@ -63,8 +69,10 @@ private: fc::thread* m_rpc_thread = nullptr; std::string m_api_url; fc::api m_db_api; + fc::api m_net_api; graphene::chain::global_property_object m_global_properties; + graphene::chain::dynamic_global_property_object m_dynamic_global_properties; ObjectId m_account_query_num = -1; account_multi_index_type m_accounts; diff --git a/programs/light_client/GrapheneApplication.cpp b/programs/light_client/GrapheneApplication.cpp index b0ca88b2..f9c7b643 100644 --- a/programs/light_client/GrapheneApplication.cpp +++ b/programs/light_client/GrapheneApplication.cpp @@ -64,10 +64,12 @@ void GrapheneApplication::start(QString apiurl, QString user, QString pass) Q_EMIT loginFailed(); return; } + auto net_api = remote_api->network_broadcast(); ilog("connecting..."); queueExecute([=](){ m_model->setDatabaseAPI(db_api); + m_model->setNetworkAPI(net_api); }); queueExecute([=](){ setIsConnected(true); }); @@ -87,6 +89,26 @@ Transaction* GrapheneApplication::createTransaction() const return new Transaction; } +void GrapheneApplication::signTransaction(Transaction* transaction) const +{ + if (transaction == nullptr) return; + + auto getActiveAuth = [this](graphene::chain::account_id_type id) { + return &model()->getAccount(id.instance.value)->accountObject().active; + }; + auto getOwnerAuth = [this](graphene::chain::account_id_type id) { + return &model()->getAccount(id.instance.value)->accountObject().owner; + }; + + auto& trx = transaction->internalTransaction(); + trx.set_reference_block(model()->dynamic_global_properties().head_block_id); + flat_set pubKeys = wallet()->getAvailablePrivateKeys(); + auto requiredKeys = trx.get_required_signatures(pubKeys, getActiveAuth, getOwnerAuth); + trx.signatures = wallet()->signDigest(trx.digest(), requiredKeys); + idump((trx)); +} + + Q_SLOT void GrapheneApplication::execute(const std::function& func)const { func(); diff --git a/programs/light_client/GrapheneApplication.hpp b/programs/light_client/GrapheneApplication.hpp index 4d8489a4..f45106ad 100644 --- a/programs/light_client/GrapheneApplication.hpp +++ b/programs/light_client/GrapheneApplication.hpp @@ -68,6 +68,7 @@ public: /// Convenience method to get a Transaction in QML. Caller takes ownership of the new Transaction. Q_INVOKABLE Transaction* createTransaction() const; + Q_INVOKABLE void signTransaction(Transaction* transaction) const; Q_SIGNALS: void exceptionThrown(QString message); diff --git a/programs/light_client/Wallet.cpp b/programs/light_client/Wallet.cpp index c46b9e16..d3429909 100644 --- a/programs/light_client/Wallet.cpp +++ b/programs/light_client/Wallet.cpp @@ -352,16 +352,6 @@ QList> Wallet::getAllPublicKeys(bool only_if_private)cons return result; } -void Wallet::sign(Transaction* transaction) const -{ - if (transaction == nullptr) return; - - auto& trx = transaction->internalTransaction(); - flat_set pubKeys = getAvailablePrivateKeys(); - trx.signatures = signDigest(trx.digest(), set(pubKeys.begin(), pubKeys.end())); - idump((trx)); -} - QString Wallet::getPublicKey(QString label) { if( !isOpen() ) return QString::null; diff --git a/programs/light_client/Wallet.hpp b/programs/light_client/Wallet.hpp index e8a206e2..a71b1b19 100644 --- a/programs/light_client/Wallet.hpp +++ b/programs/light_client/Wallet.hpp @@ -127,7 +127,6 @@ class Wallet : public QObject /** * @pre !isLocked() */ - Q_INVOKABLE void sign(Transaction* transaction) const; vector signDigest(const digest_type& d, const set& keys)const; diff --git a/programs/light_client/qml/TransactionConfirmationForm.qml b/programs/light_client/qml/TransactionConfirmationForm.qml index ff195962..895bfac4 100644 --- a/programs/light_client/qml/TransactionConfirmationForm.qml +++ b/programs/light_client/qml/TransactionConfirmationForm.qml @@ -65,6 +65,7 @@ FormBase { UnlockingFinishButtons { app: base.app Layout.fillWidth: true + rightButtonText: qsTr("Commit") onLeftButtonClicked: { canceled({}) trx = null @@ -73,7 +74,8 @@ FormBase { if (app.wallet.isLocked) app.wallet.unlock(passwordField.text) else { - app.wallet.sign(trx) + app.signTransaction(trx) + app.model.broadcast(trx) completed(trx) } }