[GUI] Implement transaction broadcasting
Still need to set expiration, so none of the transactions I broadcast work yet... :( Sadly there is no testnet so I can't finish this. Oh well. I'm sure it'll be much easier on Monday.
This commit is contained in:
parent
02682e761c
commit
99d6450473
9 changed files with 68 additions and 18 deletions
|
|
@ -564,7 +564,7 @@ namespace graphene { namespace app {
|
||||||
_app.p2p_node()->broadcast_transaction(trx);
|
_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();
|
trx.validate();
|
||||||
_callbacks[trx.id()] = cb;
|
_callbacks[trx.id()] = cb;
|
||||||
|
|
@ -741,7 +741,7 @@ namespace graphene { namespace app {
|
||||||
{
|
{
|
||||||
if( _account_subscriptions.size() )
|
if( _account_subscriptions.size() )
|
||||||
{
|
{
|
||||||
map<account_id_type, vector<variant> > broadcast_queue;
|
map<account_id_type, vector<variant> > broadcast_queue;
|
||||||
for( const auto& obj : objs )
|
for( const auto& obj : objs )
|
||||||
{
|
{
|
||||||
auto relevant = get_relevant_accounts( obj );
|
auto relevant = get_relevant_accounts( obj );
|
||||||
|
|
@ -795,7 +795,7 @@ namespace graphene { namespace app {
|
||||||
void database_api::on_objects_changed(const vector<object_id_type>& ids)
|
void database_api::on_objects_changed(const vector<object_id_type>& ids)
|
||||||
{
|
{
|
||||||
vector<object_id_type> my_objects;
|
vector<object_id_type> my_objects;
|
||||||
map<account_id_type, vector<variant> > broadcast_queue;
|
map<account_id_type, vector<variant> > broadcast_queue;
|
||||||
map< pair<asset_id_type, asset_id_type>, vector<variant> > market_broadcast_queue;
|
map< pair<asset_id_type, asset_id_type>, vector<variant> > market_broadcast_queue;
|
||||||
for(auto id : ids)
|
for(auto id : ids)
|
||||||
{
|
{
|
||||||
|
|
@ -888,7 +888,7 @@ namespace graphene { namespace app {
|
||||||
if(_market_subscriptions.size() == 0)
|
if(_market_subscriptions.size() == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
||||||
const auto& ops = _db.get_applied_operations();
|
const auto& ops = _db.get_applied_operations();
|
||||||
map< std::pair<asset_id_type,asset_id_type>, vector<pair<operation, operation_result>> > subscribed_markets_ops;
|
map< std::pair<asset_id_type,asset_id_type>, vector<pair<operation, operation_result>> > subscribed_markets_ops;
|
||||||
for(const auto& op : ops)
|
for(const auto& op : ops)
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,9 @@ public:
|
||||||
m_account.name = name.toStdString();
|
m_account.name = name.toStdString();
|
||||||
}
|
}
|
||||||
void setAccountObject(const account_object& obj);
|
void setAccountObject(const account_object& obj);
|
||||||
|
const account_object& accountObject()const {
|
||||||
|
return m_account;
|
||||||
|
}
|
||||||
|
|
||||||
QString name()const { return QString::fromStdString(m_account.name); }
|
QString name()const { return QString::fromStdString(m_account.name); }
|
||||||
QString memoKey()const;
|
QString memoKey()const;
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
#include "ChainDataModel.hpp"
|
#include "ChainDataModel.hpp"
|
||||||
#include "Balance.hpp"
|
#include "Balance.hpp"
|
||||||
#include "Operations.hpp"
|
#include "Operations.hpp"
|
||||||
|
#include "Transaction.hpp"
|
||||||
|
|
||||||
#include <graphene/app/api.hpp>
|
#include <graphene/app/api.hpp>
|
||||||
#include <graphene/chain/protocol/protocol.hpp>
|
#include <graphene/chain/protocol/protocol.hpp>
|
||||||
|
|
@ -26,11 +27,35 @@ void ChainDataModel::setDatabaseAPI(fc::api<database_api> dbapi) {
|
||||||
m_db_api = dbapi;
|
m_db_api = dbapi;
|
||||||
m_rpc_thread->async([this] {
|
m_rpc_thread->async([this] {
|
||||||
m_global_properties = m_db_api->get_global_properties();
|
m_global_properties = m_db_api->get_global_properties();
|
||||||
m_db_api->subscribe_to_objects([this](const variant& v) { m_global_properties = v.as<global_property_object>(); },
|
m_db_api->subscribe_to_objects([this](const variant& v) {
|
||||||
{m_global_properties.id});
|
m_global_properties = v.as<global_property_object>();
|
||||||
|
}, {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<dynamic_global_property_object>();
|
||||||
|
}, {m_dynamic_global_properties.id});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ChainDataModel::setNetworkAPI(fc::api<network_broadcast_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)
|
Asset* ChainDataModel::getAsset(ObjectId id)
|
||||||
{
|
{
|
||||||
auto& by_id_idx = m_assets.get<by_id>();
|
auto& by_id_idx = m_assets.get<by_id>();
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,7 @@ typedef multi_index_container<
|
||||||
>
|
>
|
||||||
> account_multi_index_type;
|
> account_multi_index_type;
|
||||||
|
|
||||||
|
class Transaction;
|
||||||
class ChainDataModel : public QObject {
|
class ChainDataModel : public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
|
|
@ -52,8 +53,13 @@ public:
|
||||||
ChainDataModel(fc::thread& t, QObject* parent = nullptr);
|
ChainDataModel(fc::thread& t, QObject* parent = nullptr);
|
||||||
|
|
||||||
void setDatabaseAPI(fc::api<graphene::app::database_api> dbapi);
|
void setDatabaseAPI(fc::api<graphene::app::database_api> dbapi);
|
||||||
|
void setNetworkAPI(fc::api<graphene::app::network_broadcast_api> napi);
|
||||||
|
|
||||||
const graphene::chain::global_property_object& global_properties() const { return m_global_properties; }
|
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:
|
Q_SIGNALS:
|
||||||
void queueExecute(const std::function<void()>&);
|
void queueExecute(const std::function<void()>&);
|
||||||
|
|
@ -63,8 +69,10 @@ private:
|
||||||
fc::thread* m_rpc_thread = nullptr;
|
fc::thread* m_rpc_thread = nullptr;
|
||||||
std::string m_api_url;
|
std::string m_api_url;
|
||||||
fc::api<graphene::app::database_api> m_db_api;
|
fc::api<graphene::app::database_api> m_db_api;
|
||||||
|
fc::api<graphene::app::network_broadcast_api> m_net_api;
|
||||||
|
|
||||||
graphene::chain::global_property_object m_global_properties;
|
graphene::chain::global_property_object m_global_properties;
|
||||||
|
graphene::chain::dynamic_global_property_object m_dynamic_global_properties;
|
||||||
|
|
||||||
ObjectId m_account_query_num = -1;
|
ObjectId m_account_query_num = -1;
|
||||||
account_multi_index_type m_accounts;
|
account_multi_index_type m_accounts;
|
||||||
|
|
|
||||||
|
|
@ -64,10 +64,12 @@ void GrapheneApplication::start(QString apiurl, QString user, QString pass)
|
||||||
Q_EMIT loginFailed();
|
Q_EMIT loginFailed();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
auto net_api = remote_api->network_broadcast();
|
||||||
|
|
||||||
ilog("connecting...");
|
ilog("connecting...");
|
||||||
queueExecute([=](){
|
queueExecute([=](){
|
||||||
m_model->setDatabaseAPI(db_api);
|
m_model->setDatabaseAPI(db_api);
|
||||||
|
m_model->setNetworkAPI(net_api);
|
||||||
});
|
});
|
||||||
|
|
||||||
queueExecute([=](){ setIsConnected(true); });
|
queueExecute([=](){ setIsConnected(true); });
|
||||||
|
|
@ -87,6 +89,26 @@ Transaction* GrapheneApplication::createTransaction() const
|
||||||
return new Transaction;
|
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<public_key_type> 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<void()>& func)const
|
Q_SLOT void GrapheneApplication::execute(const std::function<void()>& func)const
|
||||||
{
|
{
|
||||||
func();
|
func();
|
||||||
|
|
|
||||||
|
|
@ -68,6 +68,7 @@ public:
|
||||||
|
|
||||||
/// Convenience method to get a Transaction in QML. Caller takes ownership of the new Transaction.
|
/// Convenience method to get a Transaction in QML. Caller takes ownership of the new Transaction.
|
||||||
Q_INVOKABLE Transaction* createTransaction() const;
|
Q_INVOKABLE Transaction* createTransaction() const;
|
||||||
|
Q_INVOKABLE void signTransaction(Transaction* transaction) const;
|
||||||
|
|
||||||
Q_SIGNALS:
|
Q_SIGNALS:
|
||||||
void exceptionThrown(QString message);
|
void exceptionThrown(QString message);
|
||||||
|
|
|
||||||
|
|
@ -352,16 +352,6 @@ QList<QPair<QString,QString>> Wallet::getAllPublicKeys(bool only_if_private)cons
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Wallet::sign(Transaction* transaction) const
|
|
||||||
{
|
|
||||||
if (transaction == nullptr) return;
|
|
||||||
|
|
||||||
auto& trx = transaction->internalTransaction();
|
|
||||||
flat_set<public_key_type> pubKeys = getAvailablePrivateKeys();
|
|
||||||
trx.signatures = signDigest(trx.digest(), set<public_key_type>(pubKeys.begin(), pubKeys.end()));
|
|
||||||
idump((trx));
|
|
||||||
}
|
|
||||||
|
|
||||||
QString Wallet::getPublicKey(QString label)
|
QString Wallet::getPublicKey(QString label)
|
||||||
{
|
{
|
||||||
if( !isOpen() ) return QString::null;
|
if( !isOpen() ) return QString::null;
|
||||||
|
|
|
||||||
|
|
@ -127,7 +127,6 @@ class Wallet : public QObject
|
||||||
/**
|
/**
|
||||||
* @pre !isLocked()
|
* @pre !isLocked()
|
||||||
*/
|
*/
|
||||||
Q_INVOKABLE void sign(Transaction* transaction) const;
|
|
||||||
vector<signature_type> signDigest(const digest_type& d,
|
vector<signature_type> signDigest(const digest_type& d,
|
||||||
const set<public_key_type>& keys)const;
|
const set<public_key_type>& keys)const;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -65,6 +65,7 @@ FormBase {
|
||||||
UnlockingFinishButtons {
|
UnlockingFinishButtons {
|
||||||
app: base.app
|
app: base.app
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
rightButtonText: qsTr("Commit")
|
||||||
onLeftButtonClicked: {
|
onLeftButtonClicked: {
|
||||||
canceled({})
|
canceled({})
|
||||||
trx = null
|
trx = null
|
||||||
|
|
@ -73,7 +74,8 @@ FormBase {
|
||||||
if (app.wallet.isLocked)
|
if (app.wallet.isLocked)
|
||||||
app.wallet.unlock(passwordField.text)
|
app.wallet.unlock(passwordField.text)
|
||||||
else {
|
else {
|
||||||
app.wallet.sign(trx)
|
app.signTransaction(trx)
|
||||||
|
app.model.broadcast(trx)
|
||||||
completed(trx)
|
completed(trx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue