Browse Source

Implemented account proxy queues.

cl-refactor
Christian 10 years ago
parent
commit
0b014457d4
  1. 102
      libweb3jsonrpc/AccountHolder.cpp
  2. 74
      libweb3jsonrpc/AccountHolder.h
  3. 91
      libweb3jsonrpc/WebThreeStubServerBase.cpp
  4. 37
      libweb3jsonrpc/WebThreeStubServerBase.h
  5. 18
      libweb3jsonrpc/abstractwebthreestubserver.h
  6. 4
      libweb3jsonrpc/spec.json
  7. 74
      test/AccountHolder.cpp
  8. 30
      test/webthreestubclient.h

102
libweb3jsonrpc/AccountHolder.cpp

@ -0,0 +1,102 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file AccountHolder.cpp
* @authors:
* Christian R <c@ethdev.com>
* Lefteris Karapetsas <lefteris@ethdev.com>
* @date 2015
*/
#include "AccountHolder.h"
#include <libethereum/Client.h>
using namespace std;
using namespace dev;
using namespace dev::eth;
vector<TransactionSkeleton> emptyQueue;
void AccountHolder::setAccounts(vector<KeyPair> const& _accounts)
{
m_accounts.clear();
for (auto const& keyPair: _accounts)
{
m_accounts.push_back(keyPair.address());
m_keyPairs[keyPair.address()] = keyPair;
}
}
vector<Address> AccountHolder::getAllAccounts() const
{
vector<Address> accounts = m_accounts;
for (auto const& pair: m_proxyAccounts)
if (!isRealAccount(pair.first))
accounts.push_back(pair.first);
return accounts;
}
Address const& AccountHolder::getDefaultCallAccount() const
{
if (m_accounts.empty())
return ZeroAddress;
Address const* bestMatch = &m_accounts.front();
for (auto const& account: m_accounts)
if (m_client()->balanceAt(account) > m_client()->balanceAt(*bestMatch))
bestMatch = &account;
return *bestMatch;
}
int AccountHolder::addProxyAccount(const Address& _account)
{
int const c_id = m_transactionQueues.empty() ? 1 : m_transactionQueues.rbegin()->first + 1;
if (isProxyAccount(_account))
return 0;
m_proxyAccounts.insert(make_pair(_account, c_id));
m_transactionQueues[c_id].first = _account;
return c_id;
}
bool AccountHolder::removeProxyAccount(unsigned _id)
{
if (!m_transactionQueues.count(_id))
return false;
m_proxyAccounts.erase(m_transactionQueues[_id].first);
m_transactionQueues.erase(_id);
return true;
}
void AccountHolder::queueTransaction(TransactionSkeleton const& _transaction)
{
if (!m_proxyAccounts.count(_transaction.from))
return;
int id = m_proxyAccounts[_transaction.from];
m_transactionQueues[id].second.push_back(_transaction);
}
vector<TransactionSkeleton> const& AccountHolder::getQueuedTransactions(int _id) const
{
if (!m_transactionQueues.count(_id))
return emptyQueue;
return m_transactionQueues.at(_id).second;
}
void AccountHolder::clearQueue(int _id)
{
if (m_transactionQueues.count(_id))
m_transactionQueues.at(_id).second.clear();
}

74
libweb3jsonrpc/AccountHolder.h

@ -0,0 +1,74 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file AccountHolder.h
* @authors:
* Christian R <c@ethdev.com>
* Lefteris Karapetsas <lefteris@ethdev.com>
* @date 2015
*/
#pragma once
#include <functional>
#include <vector>
#include <map>
#include <libdevcrypto/Common.h>
#include <libethcore/CommonJS.h>
namespace dev
{
namespace eth
{
class Interface;
}
/**
* Manages real accounts (where we know the secret key) and proxy accounts (where transactions
* to be sent from these accounts are forwarded to a proxy on the other side).
*/
class AccountHolder
{
public:
explicit AccountHolder(std::function<eth::Interface*()> const& _client): m_client(_client) {}
/// Sets or resets the list of real accounts.
void setAccounts(std::vector<KeyPair> const& _accounts);
std::vector<Address> const& getRealAccounts() const { return m_accounts; }
bool isRealAccount(Address const& _account) const { return m_keyPairs.count(_account) > 0; }
bool isProxyAccount(Address const& _account) const { return m_proxyAccounts.count(_account) > 0; }
Secret const& secretKey(Address const& _account) const { return m_keyPairs.at(_account).secret(); }
std::vector<Address> getAllAccounts() const;
Address const& getDefaultCallAccount() const;
int addProxyAccount(Address const& _account);
bool removeProxyAccount(unsigned _id);
void queueTransaction(eth::TransactionSkeleton const& _transaction);
std::vector<eth::TransactionSkeleton> const& getQueuedTransactions(int _id) const;
void clearQueue(int _id);
private:
using TransactionQueue = std::vector<eth::TransactionSkeleton>;
std::map<Address, KeyPair> m_keyPairs;
std::vector<Address> m_accounts;
std::map<Address, int> m_proxyAccounts;
std::map<int, std::pair<Address, TransactionQueue>> m_transactionQueues;
std::function<eth::Interface*()> m_client;
};
}

91
libweb3jsonrpc/WebThreeStubServerBase.cpp

@ -35,6 +35,7 @@
#include <libserpent/funcs.h>
#endif
#include "WebThreeStubServerBase.h"
#include "AccountHolder.h"
using namespace std;
using namespace dev;
@ -72,6 +73,18 @@ static Json::Value toJson(dev::eth::Transaction const& _t)
return res;
}
static Json::Value toJson(dev::eth::TransactionSkeleton const& _t)
{
Json::Value res;
res["to"] = toJS(_t.to);
res["from"] = toJS(_t.from);
res["gas"] = (int)_t.gas;
res["gasPrice"] = toJS(_t.gasPrice);
res["value"] = toJS(_t.value);
res["data"] = jsFromBinary(_t.data);
return res;
}
static Json::Value toJson(dev::eth::LocalisedLogEntry const& _e)
{
Json::Value res;
@ -211,31 +224,10 @@ static Json::Value toJson(h256 const& _h, shh::Envelope const& _e, shh::Message
return res;
}
void AccountHolder::setAccounts(std::vector<dev::KeyPair> const& _accounts)
{
m_accounts.clear();
for (auto const& keyPair: _accounts)
{
m_accounts.push_back(keyPair.address());
m_keyPairs[keyPair.address()] = keyPair;
}
}
Address const& AccountHolder::getDefaultCallAccount() const
{
if (m_accounts.empty())
return ZeroAddress;
Address const* bestMatch = &m_accounts.front();
for (auto const& account: m_accounts)
if (m_client()->balanceAt(account) > m_client()->balanceAt(*bestMatch))
bestMatch = &account;
return *bestMatch;
}
WebThreeStubServerBase::WebThreeStubServerBase(jsonrpc::AbstractServerConnector& _conn, std::vector<dev::KeyPair> const& _accounts):
AbstractWebThreeStubServer(_conn), m_accounts(std::bind(&WebThreeStubServerBase::client, this))
AbstractWebThreeStubServer(_conn), m_accounts(make_shared<AccountHolder>(std::bind(&WebThreeStubServerBase::client, this)))
{
m_accounts.setAccounts(_accounts);
m_accounts->setAccounts(_accounts);
}
void WebThreeStubServerBase::setIdentities(std::vector<dev::KeyPair> const& _ids)
@ -253,7 +245,7 @@ std::string WebThreeStubServerBase::web3_sha3(std::string const& _param1)
Json::Value WebThreeStubServerBase::eth_accounts()
{
Json::Value ret(Json::arrayValue);
for (auto const& i: m_accounts.getAllAccounts())
for (auto const& i: m_accounts->getAllAccounts())
ret.append(toJS(i));
return ret;
}
@ -338,14 +330,14 @@ std::string WebThreeStubServerBase::eth_call(Json::Value const& _json)
std::string ret;
TransactionSkeleton t = toTransaction(_json);
if (!t.from)
t.from = m_accounts.getDefaultCallAccount();
if (!m_accounts.isRealAccount(t.from))
t.from = m_accounts->getDefaultCallAccount();
if (!m_accounts->isRealAccount(t.from))
return ret;
if (!t.gasPrice)
t.gasPrice = 10 * dev::eth::szabo;
if (!t.gas)
t.gas = min<u256>(client()->gasLimitRemaining(), client()->balanceAt(t.from) / t.gasPrice);
ret = toJS(client()->call(m_accounts.secretKey(t.from), t.value, t.to, t.data, t.gas, t.gasPrice));
ret = toJS(client()->call(m_accounts->secretKey(t.from), t.value, t.to, t.data, t.gas, t.gasPrice));
return ret;
}
@ -469,6 +461,25 @@ bool WebThreeStubServerBase::eth_submitWork(std::string const& _nonce)
return client()->submitNonce(jsToFixed<32>(_nonce));
}
int WebThreeStubServerBase::eth_register(std::string const& _address)
{
return m_accounts->addProxyAccount(jsToAddress(_address));
}
bool WebThreeStubServerBase::eth_unregister(int _id)
{
return m_accounts->removeProxyAccount(_id);
}
Json::Value WebThreeStubServerBase::eth_queuedTransactions(int _id)
{
Json::Value ret(Json::arrayValue);
for (TransactionSkeleton const& t: m_accounts->getQueuedTransactions(_id))
ret.append(toJson(t));
m_accounts->clearQueue(_id);
return ret;
}
std::string WebThreeStubServerBase::shh_newGroup(std::string const& _id, std::string const& _who)
{
(void)_id;
@ -690,24 +701,27 @@ std::string WebThreeStubServerBase::eth_transact(Json::Value const& _json)
std::string ret;
TransactionSkeleton t = toTransaction(_json);
if (!t.from)
t.from = m_accounts.getDefaultCallAccount();
if (!m_accounts.isRealAccount(t.from))
{
// if (m_accounts.isProxyAccount(t.from))
// m_accounts.storeTransaction(t);
return ret;
}
t.from = m_accounts->getDefaultCallAccount();
if (!t.gasPrice)
t.gasPrice = 10 * dev::eth::szabo;
if (!t.gas)
t.gas = min<u256>(client()->gasLimitRemaining(), client()->balanceAt(t.from) / t.gasPrice);
if (!m_accounts->isRealAccount(t.from))
{
if (m_accounts->isProxyAccount(t.from))
{
// todo "authenticate for proxy"
m_accounts->queueTransaction(t);
}
return ret;
}
if (authenticate(t))
{
if (t.to)
// TODO: from qethereum, insert validification hook here.
client()->transact(m_accounts.secretKey(t.from), t.value, t.to, t.data, t.gas, t.gasPrice);
client()->transact(m_accounts->secretKey(t.from), t.value, t.to, t.data, t.gas, t.gasPrice);
else
ret = toJS(client()->transact(m_accounts.secretKey(t.from), t.value, t.data, t.gas, t.gasPrice));
ret = toJS(client()->transact(m_accounts->secretKey(t.from), t.value, t.data, t.gas, t.gasPrice));
client()->flushTransactions();
}
return ret;
@ -744,3 +758,8 @@ bool WebThreeStubServerBase::eth_uninstallFilter(int _id)
client()->uninstallWatch(_id);
return true;
}
void WebThreeStubServerBase::setAccounts(const std::vector<KeyPair>& _accounts)
{
m_accounts->setAccounts(_accounts);
}

37
libweb3jsonrpc/WebThreeStubServerBase.h

@ -23,8 +23,8 @@
#pragma once
#include <memory>
#include <iostream>
#include <functional>
#include <jsonrpccpp/server.h>
#include <libdevcrypto/Common.h>
#pragma GCC diagnostic push
@ -36,6 +36,7 @@
namespace dev
{
class WebThreeNetworkFace;
class AccountHolder;
class KeyPair;
namespace eth
{
@ -54,32 +55,6 @@ public:
virtual void put(std::string const& _name, std::string const& _key, std::string const& _value) = 0;
};
/**
* Manages real accounts (where we know the secret key) and proxy accounts (where transactions
* to be sent from these accounts are forwarded to a proxy on the other side).
*/
class AccountHolder
{
public:
explicit AccountHolder(std::function<dev::eth::Interface*()> const& _client): m_client(_client) {}
/// Sets or resets the list of real accounts.
void setAccounts(std::vector<dev::KeyPair> const& _accounts);
std::vector<dev::Address> const& getRealAccounts() const { return m_accounts; }
bool isRealAccount(dev::Address const& _account) const { return m_keyPairs.count(_account) > 0; }
Secret const& secretKey(dev::Address const& _account) const { return m_keyPairs.at(_account).secret(); }
std::vector<dev::Address> const& getAllAccounts() const { return m_accounts; /*todo */}
dev::Address const& getDefaultCallAccount() const;
private:
std::map<dev::Address, dev::KeyPair> m_keyPairs;
std::vector<dev::Address> m_accounts;
std::function<dev::eth::Interface*()> m_client;
};
/**
* @brief JSON-RPC api implementation
* @todo filters should work on unsigned instead of int
@ -137,6 +112,10 @@ public:
virtual Json::Value eth_getWork();
virtual bool eth_submitWork(std::string const& _nonce);
virtual int eth_register(std::string const& _address);
virtual bool eth_unregister(int _id);
virtual Json::Value eth_queuedTransactions(int _id);
virtual std::string db_get(std::string const& _name, std::string const& _key);
virtual std::string db_getString(std::string const& _name, std::string const& _key);
virtual bool db_put(std::string const& _name, std::string const& _key, std::string const& _value);
@ -152,7 +131,7 @@ public:
virtual bool shh_post(Json::Value const& _json);
virtual bool shh_uninstallFilter(int _id);
void setAccounts(std::vector<dev::KeyPair> const& _accounts) { m_accounts.setAccounts(_accounts); }
void setAccounts(std::vector<dev::KeyPair> const& _accounts);
void setIdentities(std::vector<dev::KeyPair> const& _ids);
std::map<dev::Public, dev::Secret> const& ids() const { return m_ids; }
@ -167,7 +146,7 @@ protected:
std::map<dev::Public, dev::Secret> m_ids;
std::map<unsigned, dev::Public> m_shhWatches;
AccountHolder m_accounts;
std::shared_ptr<dev::AccountHolder> m_accounts;
};
} //namespace dev

18
libweb3jsonrpc/abstractwebthreestubserver.h

@ -55,6 +55,9 @@ class AbstractWebThreeStubServer : public jsonrpc::AbstractServer<AbstractWebThr
this->bindAndAddMethod(jsonrpc::Procedure("eth_logs", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_ARRAY, "param1",jsonrpc::JSON_OBJECT, NULL), &AbstractWebThreeStubServer::eth_logsI);
this->bindAndAddMethod(jsonrpc::Procedure("eth_getWork", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_ARRAY, NULL), &AbstractWebThreeStubServer::eth_getWorkI);
this->bindAndAddMethod(jsonrpc::Procedure("eth_submitWork", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, "param1",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::eth_submitWorkI);
this->bindAndAddMethod(jsonrpc::Procedure("eth_register", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_INTEGER, "param1",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::eth_registerI);
this->bindAndAddMethod(jsonrpc::Procedure("eth_unregister", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, "param1",jsonrpc::JSON_INTEGER, NULL), &AbstractWebThreeStubServer::eth_unregisterI);
this->bindAndAddMethod(jsonrpc::Procedure("eth_queuedTransactions", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_ARRAY, "param1",jsonrpc::JSON_INTEGER, NULL), &AbstractWebThreeStubServer::eth_queuedTransactionsI);
this->bindAndAddMethod(jsonrpc::Procedure("db_put", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, "param1",jsonrpc::JSON_STRING,"param2",jsonrpc::JSON_STRING,"param3",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::db_putI);
this->bindAndAddMethod(jsonrpc::Procedure("db_get", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_STRING, "param1",jsonrpc::JSON_STRING,"param2",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::db_getI);
this->bindAndAddMethod(jsonrpc::Procedure("db_putString", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, "param1",jsonrpc::JSON_STRING,"param2",jsonrpc::JSON_STRING,"param3",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::db_putStringI);
@ -253,6 +256,18 @@ class AbstractWebThreeStubServer : public jsonrpc::AbstractServer<AbstractWebThr
{
response = this->eth_submitWork(request[0u].asString());
}
inline virtual void eth_registerI(const Json::Value &request, Json::Value &response)
{
response = this->eth_register(request[0u].asString());
}
inline virtual void eth_unregisterI(const Json::Value &request, Json::Value &response)
{
response = this->eth_unregister(request[0u].asInt());
}
inline virtual void eth_queuedTransactionsI(const Json::Value &request, Json::Value &response)
{
response = this->eth_queuedTransactions(request[0u].asInt());
}
inline virtual void db_putI(const Json::Value &request, Json::Value &response)
{
response = this->db_put(request[0u].asString(), request[1u].asString(), request[2u].asString());
@ -349,6 +364,9 @@ class AbstractWebThreeStubServer : public jsonrpc::AbstractServer<AbstractWebThr
virtual Json::Value eth_logs(const Json::Value& param1) = 0;
virtual Json::Value eth_getWork() = 0;
virtual bool eth_submitWork(const std::string& param1) = 0;
virtual int eth_register(const std::string& param1) = 0;
virtual bool eth_unregister(int param1) = 0;
virtual Json::Value eth_queuedTransactions(int param1) = 0;
virtual bool db_put(const std::string& param1, const std::string& param2, const std::string& param3) = 0;
virtual std::string db_get(const std::string& param1, const std::string& param2) = 0;
virtual bool db_putString(const std::string& param1, const std::string& param2, const std::string& param3) = 0;

4
libweb3jsonrpc/spec.json

@ -50,6 +50,10 @@
{ "name": "eth_getWork", "params": [], "order": [], "returns": []},
{ "name": "eth_submitWork", "params": [""], "order": [], "returns": true},
{ "name": "eth_register", "params": [""], "order": [], "returns": 0},
{ "name": "eth_unregister", "params": [0], "order": [], "returns": true},
{ "name": "eth_queuedTransactions", "params": [0], "order": [], "returns": []},
{ "name": "db_put", "params": ["", "", ""], "order": [], "returns": true},
{ "name": "db_get", "params": ["", ""], "order": [], "returns": ""},
{ "name": "db_putString", "params": ["", "", ""], "order": [], "returns": true},

74
test/AccountHolder.cpp

@ -0,0 +1,74 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @author Christian R <c@ethdev.com>
* @date 2015
* Unit tests for the account holder used by the WebThreeStubServer.
*/
#include <boost/test/unit_test.hpp>
#include <libweb3jsonrpc/AccountHolder.h>
namespace dev
{
namespace test
{
BOOST_AUTO_TEST_SUITE(AccountHolderTest)
BOOST_AUTO_TEST_CASE(ProxyAccountUseCase)
{
AccountHolder h = AccountHolder(std::function<eth::Interface*()>());
BOOST_CHECK(h.getAllAccounts().empty());
BOOST_CHECK(h.getRealAccounts().empty());
Address addr("abababababababababababababababababababab");
Address addr2("abababababababababababababababababababab");
int id = h.addProxyAccount(addr);
BOOST_CHECK(h.getQueuedTransactions(id).empty());
// register it again
int secondID = h.addProxyAccount(addr);
BOOST_CHECK(h.getQueuedTransactions(secondID).empty());
eth::TransactionSkeleton t1;
eth::TransactionSkeleton t2;
t1.from = addr;
t1.data = fromHex("12345678");
t2.from = addr;
t2.data = fromHex("abcdef");
BOOST_CHECK(h.getQueuedTransactions(id).empty());
h.queueTransaction(t1);
BOOST_CHECK_EQUAL(1, h.getQueuedTransactions(id).size());
h.queueTransaction(t2);
BOOST_REQUIRE_EQUAL(2, h.getQueuedTransactions(id).size());
// second proxy should not see transactions
BOOST_CHECK(h.getQueuedTransactions(secondID).empty());
BOOST_CHECK(h.getQueuedTransactions(id)[0].data == t1.data);
BOOST_CHECK(h.getQueuedTransactions(id)[1].data == t2.data);
h.clearQueue(id);
BOOST_CHECK(h.getQueuedTransactions(id).empty());
// removing fails because it never existed
BOOST_CHECK(!h.removeProxyAccount(secondID));
BOOST_CHECK(h.removeProxyAccount(id));
}
BOOST_AUTO_TEST_SUITE_END()
}
}

30
test/webthreestubclient.h

@ -447,6 +447,36 @@ class WebThreeStubClient : public jsonrpc::Client
else
throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString());
}
int eth_register(const std::string& param1) throw (jsonrpc::JsonRpcException)
{
Json::Value p;
p.append(param1);
Json::Value result = this->CallMethod("eth_register",p);
if (result.isInt())
return result.asInt();
else
throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString());
}
bool eth_unregister(int param1) throw (jsonrpc::JsonRpcException)
{
Json::Value p;
p.append(param1);
Json::Value result = this->CallMethod("eth_unregister",p);
if (result.isBool())
return result.asBool();
else
throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString());
}
Json::Value eth_queuedTransactions(int param1) throw (jsonrpc::JsonRpcException)
{
Json::Value p;
p.append(param1);
Json::Value result = this->CallMethod("eth_queuedTransactions",p);
if (result.isArray())
return result;
else
throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString());
}
bool db_put(const std::string& param1, const std::string& param2, const std::string& param3) throw (jsonrpc::JsonRpcException)
{
Json::Value p;

Loading…
Cancel
Save