Browse Source

Integrate KeyManager.

cl-refactor
Gav Wood 10 years ago
parent
commit
68714c59f8
  1. 39
      alethzero/MainWin.cpp
  2. 4
      alethzero/MainWin.h
  3. 69
      alethzero/OurWebThreeStubServer.cpp
  4. 43
      alethzero/OurWebThreeStubServer.h
  5. 56
      eth/main.cpp
  6. 2
      exp/main.cpp
  7. 2
      libdevcore/Common.cpp
  8. 2
      libdevcore/Common.h
  9. 1
      libdevcore/FixedHash.h
  10. 5
      libdevcrypto/SecretStore.h
  11. 11
      libethcore/Common.h
  12. 2
      libethcore/CommonJS.cpp
  13. 14
      libethcore/CommonJS.h
  14. 1
      libethereum/ClientBase.h
  15. 4
      libethereum/Interface.h
  16. 14
      libethereum/KeyManager.cpp
  17. 15
      libethereum/KeyManager.h
  18. 5
      libtestutils/FixedWebThreeServer.cpp
  19. 6
      libtestutils/FixedWebThreeServer.h
  20. 51
      libweb3jsonrpc/AccountHolder.cpp
  21. 85
      libweb3jsonrpc/AccountHolder.h
  22. 4
      libweb3jsonrpc/WebThreeStubServer.cpp
  23. 2
      libweb3jsonrpc/WebThreeStubServer.h
  24. 43
      libweb3jsonrpc/WebThreeStubServerBase.cpp
  25. 10
      libweb3jsonrpc/WebThreeStubServerBase.h
  26. 5
      mix/ClientModel.cpp
  27. 3
      mix/ClientModel.h
  28. 5
      mix/Web3Server.cpp
  29. 3
      mix/Web3Server.h
  30. 28
      test/libweb3jsonrpc/AccountHolder.cpp

39
alethzero/MainWin.cpp

@ -143,6 +143,38 @@ Main::Main(QWidget *parent) :
// ui->log->addItem(QString::fromStdString(s));
};
// Open Key Store
bool opened = false;
if (m_keyManager.exists())
while (!opened)
{
QString s = QInputDialog::getText(nullptr, "Master password", "Enter your MASTER account password.", QLineEdit::Password, QString());
if (m_keyManager.load(s.toStdString()))
opened = true;
else if (QMessageBox::question(
nullptr,
"Invalid password entered",
"The password you entered is incorrect. If you have forgotten your password, and you wish to start afresh, manually remove the file: " + QString::fromStdString(getDataDir("ethereum")) + "/keys.info",
QMessageBox::Retry,
QMessageBox::Abort)
== QMessageBox::Abort)
exit(0);
}
if (!opened)
{
QString password;
while (true)
{
password = QInputDialog::getText(nullptr, "Master password", "Enter a MASTER password for your key store. Make it strong. You probably want to write it down somewhere and keep it safe and secure; your identity will rely on this - you never want to lose it.", QLineEdit::Password, QString());
QString confirm = QInputDialog::getText(nullptr, "Master password", "Confirm this password by typing it again", QLineEdit::Password, QString());
if (password == confirm)
break;
QMessageBox::warning(nullptr, "Try again", "You entered two different passwords - please enter the same password twice.", QMessageBox::Ok);
}
m_keyManager.create(password.toStdString());
m_keyManager.import(Secret::random(), "{\"name\":\"Default identity\"}");
}
#if ETH_DEBUG
m_servers.append("127.0.0.1:30300");
#endif
@ -176,7 +208,7 @@ Main::Main(QWidget *parent) :
m_webThree.reset(new WebThreeDirect(string("AlethZero/v") + dev::Version + "/" DEV_QUOTED(ETH_BUILD_TYPE) "/" DEV_QUOTED(ETH_BUILD_PLATFORM), getDataDir(), WithExisting::Trust, {"eth", "shh"}, p2p::NetworkPreferences(), network));
m_httpConnector.reset(new jsonrpc::HttpServer(SensibleHttpPort, "", "", dev::SensibleHttpThreads));
m_server.reset(new OurWebThreeStubServer(*m_httpConnector, *web3(), keysAsVector(m_myKeys), this));
m_server.reset(new OurWebThreeStubServer(*m_httpConnector, *web3(), this));
connect(&*m_server, SIGNAL(onNewId(QString)), SLOT(addNewId(QString)));
m_server->setIdentities(keysAsVector(owned()));
m_server->StartListening();
@ -690,7 +722,6 @@ void Main::readSettings(bool _skipGeometry)
}
}
ethereum()->setAddress(m_myKeys.back().address());
m_server->setAccounts(keysAsVector(m_myKeys));
}
{
@ -1397,9 +1428,6 @@ void Main::ourAccountsRowsMoved()
myKeys.push_back(i);
}
m_myKeys = myKeys;
if (m_server.get())
m_server->setAccounts(keysAsVector(m_myKeys));
}
void Main::on_inject_triggered()
@ -1837,7 +1865,6 @@ void Main::on_mine_triggered()
void Main::keysChanged()
{
onBalancesChange();
m_server->setAccounts(keysAsVector(m_myKeys));
}
bool beginsWith(Address _a, bytes const& _b)

4
alethzero/MainWin.h

@ -35,6 +35,7 @@
#include <libethcore/Common.h>
#include <libethereum/State.h>
#include <libethereum/Executive.h>
#include <libethereum/KeyManager.h>
#include <libwebthree/WebThree.h>
#include <libsolidity/CompilerStack.h>
#include "Context.h"
@ -90,6 +91,8 @@ public:
dev::u256 gasPrice() const { return 10 * dev::eth::szabo; }
dev::eth::KeyManager& keyManager() { return m_keyManager; }
public slots:
void load(QString _file);
void note(QString _entry);
@ -249,6 +252,7 @@ private:
QStringList m_servers;
QList<dev::KeyPair> m_myKeys;
QList<dev::KeyPair> m_myIdentities;
dev::eth::KeyManager m_keyManager;
QString m_privateChain;
dev::Address m_nameReg;

69
alethzero/OurWebThreeStubServer.cpp

@ -20,23 +20,23 @@
*/
#include "OurWebThreeStubServer.h"
#include <QMessageBox>
#include <QAbstractButton>
#include <libwebthree/WebThree.h>
#include <libnatspec/NatspecExpressionEvaluator.h>
#include "MainWin.h"
using namespace std;
using namespace dev;
using namespace dev::eth;
OurWebThreeStubServer::OurWebThreeStubServer(jsonrpc::AbstractServerConnector& _conn, WebThreeDirect& _web3,
vector<KeyPair> const& _accounts, Main* _main):
WebThreeStubServer(_conn, _web3, _accounts), m_web3(&_web3), m_main(_main)
OurWebThreeStubServer::OurWebThreeStubServer(
jsonrpc::AbstractServerConnector& _conn,
WebThreeDirect& _web3,
Main* _main
):
WebThreeStubServer(_conn, _web3, make_shared<OurAccountHolder>(_web3, _main), _main->owned().toVector().toStdVector()),
m_main(_main)
{
connect(_main, SIGNAL(poll()), this, SLOT(doValidations()));
}
string OurWebThreeStubServer::shh_newIdentity()
@ -46,7 +46,18 @@ string OurWebThreeStubServer::shh_newIdentity()
return toJS(kp.pub());
}
bool OurWebThreeStubServer::showAuthenticationPopup(string const& _title, string const& _text)
OurAccountHolder::OurAccountHolder(
WebThreeDirect& _web3,
Main* _main
):
AccountHolder([=](){ return m_web3->ethereum(); }),
m_web3(&_web3),
m_main(_main)
{
connect(_main, SIGNAL(poll()), this, SLOT(doValidations()));
}
bool OurAccountHolder::showAuthenticationPopup(string const& _title, string const& _text)
{
if (!m_main->confirm())
{
@ -66,18 +77,18 @@ bool OurWebThreeStubServer::showAuthenticationPopup(string const& _title, string
//return button == QMessageBox::Ok;
}
bool OurWebThreeStubServer::showCreationNotice(TransactionSkeleton const& _t, bool _toProxy)
bool OurAccountHolder::showCreationNotice(TransactionSkeleton const& _t, bool _toProxy)
{
return showAuthenticationPopup("Contract Creation Transaction", string("ÐApp is attemping to create a contract; ") + (_toProxy ? "(this transaction is not executed directly, but forwarded to another ÐApp) " : "") + "to be endowed with " + formatBalance(_t.value) + ", with additional network fees of up to " + formatBalance(_t.gas * _t.gasPrice) + ".\n\nMaximum total cost is " + formatBalance(_t.value + _t.gas * _t.gasPrice) + ".");
}
bool OurWebThreeStubServer::showSendNotice(TransactionSkeleton const& _t, bool _toProxy)
bool OurAccountHolder::showSendNotice(TransactionSkeleton const& _t, bool _toProxy)
{
return showAuthenticationPopup("Fund Transfer Transaction", "ÐApp is attempting to send " + formatBalance(_t.value) + " to a recipient " + m_main->pretty(_t.to) + (_toProxy ? " (this transaction is not executed directly, but forwarded to another ÐApp)" : "") +
", with additional network fees of up to " + formatBalance(_t.gas * _t.gasPrice) + ".\n\nMaximum total cost is " + formatBalance(_t.value + _t.gas * _t.gasPrice) + ".");
}
bool OurWebThreeStubServer::showUnknownCallNotice(TransactionSkeleton const& _t, bool _toProxy)
bool OurAccountHolder::showUnknownCallNotice(TransactionSkeleton const& _t, bool _toProxy)
{
return showAuthenticationPopup("DANGEROUS! Unknown Contract Transaction!",
"ÐApp is attempting to call into an unknown contract at address " +
@ -93,25 +104,47 @@ bool OurWebThreeStubServer::showUnknownCallNotice(TransactionSkeleton const& _t,
"REJECT UNLESS YOU REALLY KNOW WHAT YOU ARE DOING!");
}
void OurWebThreeStubServer::authenticate(TransactionSkeleton const& _t, bool _toProxy)
void OurAccountHolder::authenticate(TransactionSkeleton const& _t)
{
Guard l(x_queued);
m_queued.push(make_pair(_t, _toProxy));
m_queued.push(_t);
}
void OurWebThreeStubServer::doValidations()
void OurAccountHolder::doValidations()
{
Guard l(x_queued);
while (!m_queued.empty())
{
auto q = m_queued.front();
auto t = m_queued.front();
m_queued.pop();
if (validateTransaction(q.first, q.second))
WebThreeStubServerBase::authenticate(q.first, q.second);
bool proxy = isProxyAccount(t.from);
if (!proxy && !isRealAccount(t.from))
{
cwarn << "Trying to send from non-existant account" << t.from;
return;
}
// TODO: determine gas price.
if (!validateTransaction(t, proxy))
return;
if (proxy)
queueTransaction(t);
else
// sign and submit.
if (Secret s = m_main->keyManager().secret(t.from))
m_web3->ethereum()->submitTransaction(s, t);
}
}
bool OurWebThreeStubServer::validateTransaction(TransactionSkeleton const& _t, bool _toProxy)
AddressHash OurAccountHolder::realAccounts() const
{
return m_main->keyManager().accounts();
}
bool OurAccountHolder::validateTransaction(TransactionSkeleton const& _t, bool _toProxy)
{
if (_t.creation)
{

43
alethzero/OurWebThreeStubServer.h

@ -25,26 +25,29 @@
#include <libethcore/CommonJS.h>
#include <libdevcrypto/Common.h>
#include <libweb3jsonrpc/WebThreeStubServer.h>
#include <libweb3jsonrpc/AccountHolder.h>
class Main;
class OurWebThreeStubServer: public QObject, public WebThreeStubServer
class OurAccountHolder: public QObject, public dev::eth::AccountHolder
{
Q_OBJECT
public:
OurWebThreeStubServer(jsonrpc::AbstractServerConnector& _conn, dev::WebThreeDirect& _web3,
std::vector<dev::KeyPair> const& _accounts, Main* main);
virtual std::string shh_newIdentity() override;
virtual void authenticate(dev::eth::TransactionSkeleton const& _t, bool _toProxy);
signals:
void onNewId(QString _s);
OurAccountHolder(
dev::WebThreeDirect& _web3,
Main* _main
);
public slots:
void doValidations();
protected:
// easiest to return keyManager.addresses();
virtual dev::AddressHash realAccounts() const override;
// use web3 to submit a signed transaction to accept
virtual void authenticate(dev::eth::TransactionSkeleton const& _t) override;
private:
bool showAuthenticationPopup(std::string const& _title, std::string const& _text);
bool showCreationNotice(dev::eth::TransactionSkeleton const& _t, bool _toProxy);
@ -53,9 +56,29 @@ private:
bool validateTransaction(dev::eth::TransactionSkeleton const& _t, bool _toProxy);
std::queue<std::pair<dev::eth::TransactionSkeleton, bool>> m_queued;
std::queue<dev::eth::TransactionSkeleton> m_queued;
dev::Mutex x_queued;
dev::WebThreeDirect* m_web3;
Main* m_main;
};
class OurWebThreeStubServer: public QObject, public WebThreeStubServer
{
Q_OBJECT
public:
OurWebThreeStubServer(
jsonrpc::AbstractServerConnector& _conn,
dev::WebThreeDirect& _web3,
Main* main
);
virtual std::string shh_newIdentity() override;
signals:
void onNewId(QString _s);
private:
Main* m_main;
};

56
eth/main.cpp

@ -37,6 +37,7 @@
#include <libevm/VM.h>
#include <libevm/VMFactory.h>
#include <libethereum/All.h>
#include <libethereum/KeyManager.h>
#include <libwebthree/WebThree.h>
#if ETH_JSCONSOLE || !ETH_TRUE
#include <libjsconsole/JSConsole.h>
@ -46,6 +47,7 @@
#include <readline/history.h>
#endif
#if ETH_JSONRPC || !ETH_TRUE
#include <libweb3jsonrpc/AccountHolder.h>
#include <libweb3jsonrpc/WebThreeStubServer.h>
#include <jsonrpccpp/server/connectors/httpserver.h>
#include <jsonrpccpp/client/connectors/httpclient.h>
@ -1086,13 +1088,53 @@ int main(int argc, char** argv)
if (remoteHost.size())
web3.addNode(p2p::NodeId(), remoteHost + ":" + toString(remotePort));
#if ETH_JSONRPC
KeyManager keyManager;
if (keyManager.exists())
{
while (masterPassword.empty())
{
cout << "Please enter your MASTER password:" << flush;
getline(cin, masterPassword);
if (!keyManager.load(masterPassword))
{
cout << "Password invalid. Try again." << endl;
masterPassword.clear();
}
}
}
else
{
while (masterPassword.empty())
{
cout << "Please enter a MASTER password to protect your key store. Make it strong." << flush;
getline(cin, masterPassword);
string confirm;
cout << "Please confirm the password by entering it again." << flush;
getline(cin, confirm);
if (masterPassword != confirm)
{
cout << "Passwords were different. Try again." << endl;
masterPassword.clear();
}
}
keyManager.create(masterPassword);
}
string logbuf;
bool silence = false;
std::string additional;
g_logPost = [&](std::string const& a, char const*) { if (silence) logbuf += a + "\n"; else cout << "\r \r" << a << endl << additional << flush; };
// TODO: give hints &c.
auto getPassword = [&](Address const& a){ auto s = silence; silence = true; string ret; cout << endl << "Enter password for address " << a.abridged() << ": " << flush; std::getline(cin, ret); silence = s; return ret; };
#if ETH_JSONRPC || !ETH_TRUE
shared_ptr<WebThreeStubServer> jsonrpcServer;
unique_ptr<jsonrpc::AbstractServerConnector> jsonrpcConnector;
if (jsonrpc > -1)
{
jsonrpcConnector = unique_ptr<jsonrpc::AbstractServerConnector>(new jsonrpc::HttpServer(jsonrpc, "", "", SensibleHttpThreads));
jsonrpcServer = shared_ptr<WebThreeStubServer>(new WebThreeStubServer(*jsonrpcConnector.get(), web3, vector<KeyPair>({sigKey})));
jsonrpcServer = shared_ptr<WebThreeStubServer>(new WebThreeStubServer(*jsonrpcConnector.get(), web3, make_shared<SimpleAccountHolder>([&](){return web3.ethereum();}, getPassword, keyManager), vector<KeyPair>({sigKey})));
jsonrpcServer->StartListening();
}
#endif
@ -1103,15 +1145,15 @@ int main(int argc, char** argv)
if (interactive)
{
string logbuf;
additional = "Press Enter";
string l;
while (!g_exit)
{
g_logPost = [](std::string const& a, char const*) { cout << "\r \r" << a << endl << "Press Enter" << flush; };
silence = false;
cout << logbuf << "Press Enter" << flush;
std::getline(cin, l);
logbuf.clear();
g_logPost = [&](std::string const& a, char const*) { logbuf += a + "\n"; };
silence = true;
#if ETH_READLINE
if (l.size())
@ -1224,7 +1266,7 @@ int main(int argc, char** argv)
iss >> g_logVerbosity;
cout << "Verbosity: " << g_logVerbosity << endl;
}
#if ETH_JSONRPC
#if ETH_JSONRPC || !ETH_TRUE
else if (cmd == "jsonport")
{
if (iss.peek() != -1)
@ -1236,7 +1278,7 @@ int main(int argc, char** argv)
if (jsonrpc < 0)
jsonrpc = SensibleHttpPort;
jsonrpcConnector = unique_ptr<jsonrpc::AbstractServerConnector>(new jsonrpc::HttpServer(jsonrpc, "", "", SensibleHttpThreads));
jsonrpcServer = shared_ptr<WebThreeStubServer>(new WebThreeStubServer(*jsonrpcConnector.get(), web3, vector<KeyPair>({sigKey})));
jsonrpcServer = shared_ptr<WebThreeStubServer>(new WebThreeStubServer(*jsonrpcConnector.get(), web3, make_shared<SimpleAccountHolder>([&](){return web3.ethereum();}, getPassword, keyManager), vector<KeyPair>({sigKey})));
jsonrpcServer->StartListening();
}
else if (cmd == "jsonstop")

2
exp/main.cpp

@ -81,7 +81,7 @@ int main()
// cdebug << toString(a2);
Address a2("19c486071651b2650449ba3c6a807f316a73e8fe");
cdebug << keyman.keys();
cdebug << keyman.accountDetails();
cdebug << "Secret key for " << a << "is" << keyman.secret(a, [](){ return "bar"; });
cdebug << "Secret key for " << a2 << "is" << keyman.secret(a2);

2
libdevcore/Common.cpp

@ -30,6 +30,8 @@ namespace dev
char const* Version = "0.9.17";
const u256 UndefinedU256 = ~(u256)0;
void HasInvariants::checkInvariants() const
{
if (!invariants())

2
libdevcore/Common.h

@ -82,6 +82,8 @@ using u160s = std::vector<u160>;
using u256Set = std::set<u256>;
using u160Set = std::set<u160>;
extern const u256 UndefinedU256;
// Map types.
using StringMap = std::map<std::string, std::string>;
using u256Map = std::map<u256, u256>;

1
libdevcore/FixedHash.h

@ -282,6 +282,7 @@ namespace std
{
/// Forward std::hash<dev::FixedHash> to dev::FixedHash::hash.
template<> struct hash<dev::h64>: dev::h64::hash {};
template<> struct hash<dev::h128>: dev::h128::hash {};
template<> struct hash<dev::h160>: dev::h160::hash {};
template<> struct hash<dev::h256>: dev::h256::hash {};
template<> struct hash<dev::h512>: dev::h512::hash {};

5
libdevcrypto/SecretStore.h

@ -23,6 +23,7 @@
#include <functional>
#include <mutex>
#include <libdevcore/FixedHash.h>
#include "Common.h"
#include "FileSystem.h"
@ -48,8 +49,8 @@ private:
static std::string encrypt(bytes const& _v, std::string const& _pass);
static bytes decrypt(std::string const& _v, std::string const& _pass);
mutable std::map<h128, bytes> m_cached;
std::map<h128, std::pair<std::string, std::string>> m_keys;
mutable std::unordered_map<h128, bytes> m_cached;
std::unordered_map<h128, std::pair<std::string, std::string>> m_keys;
};
}

11
libethcore/Common.h

@ -136,5 +136,16 @@ private:
using Handler = std::shared_ptr<Signal::HandlerAux>;
struct TransactionSkeleton
{
bool creation = false;
Address from;
Address to;
u256 value;
bytes data;
u256 gas = UndefinedU256;
u256 gasPrice = UndefinedU256;
};
}
}

2
libethcore/CommonJS.cpp

@ -26,8 +26,6 @@
namespace dev
{
const u256 UndefinedU256 = ~(u256)0;
Address toAddress(std::string const& _sn)
{
if (_sn.size() == 40)

14
libethcore/CommonJS.h

@ -48,8 +48,6 @@ inline Address jsToAddress(std::string const& _s) { return jsToFixed<sizeof(dev:
/// Convert u256 into user-readable string. Returns int/hex value of 64 bits int, hex of 160 bits FixedHash. As a fallback try to handle input as h256.
std::string prettyU256(u256 _n, bool _abridged = true);
extern const u256 UndefinedU256;
}
@ -59,18 +57,6 @@ namespace dev
namespace eth
{
struct TransactionSkeleton
{
bool creation = false;
Address from;
Address to;
u256 value;
bytes data;
u256 gas = UndefinedU256;
u256 gasPrice = UndefinedU256;
};
/// Convert to a block number, a bit like jsToInt, except that it correctly recognises "pending" and "latest".
BlockNumber jsToBlockNumber(std::string const& _js);

1
libethereum/ClientBase.h

@ -81,6 +81,7 @@ public:
/// Submits a new contract-creation transaction.
/// @returns the new contract's address (assuming it all goes through).
virtual Address submitTransaction(Secret _secret, u256 _endowment, bytes const& _init, u256 _gas = 10000, u256 _gasPrice = 10 * szabo) override;
using Interface::submitTransaction;
/// Makes the given call. Nothing is recorded into the state.
virtual ExecutionResult call(Address const& _secret, u256 _value, Address _dest, bytes const& _data = bytes(), u256 _gas = 10000, u256 _gasPrice = 10 * szabo, BlockNumber _blockNumber = PendingBlock, FudgeFactor _ff = FudgeFactor::Strict) override;

4
libethereum/Interface.h

@ -72,6 +72,10 @@ public:
/// @returns the new contract's address (assuming it all goes through).
virtual Address submitTransaction(Secret _secret, u256 _endowment, bytes const& _init, u256 _gas = 10000, u256 _gasPrice = 10 * szabo) = 0;
/// Submits a new contract-creation transaction.
/// @returns the new contract's address (assuming it all goes through).
Address submitTransaction(Secret const& _secret, TransactionSkeleton const& _t) { if (_t.creation) return submitTransaction(_secret, _t.value, _t.data, _t.gas, _t.gasPrice); submitTransaction(_secret, _t.value, _t.to, _t.data, _t.gas, _t.gasPrice); return Address(); }
/// Blocks until all pending transactions have been processed.
virtual void flushTransactions() = 0;

14
libethereum/KeyManager.cpp

@ -28,6 +28,7 @@
#include <libdevcore/RLP.h>
using namespace std;
using namespace dev;
using namespace eth;
namespace fs = boost::filesystem;
KeyManager::KeyManager(std::string const& _keysFile):
@ -151,9 +152,18 @@ void KeyManager::kill(Address const& _a)
m_store.kill(id);
}
std::map<Address, std::pair<std::string, std::string>> KeyManager::keys() const
AddressHash KeyManager::accounts() const
{
std::map<Address, std::pair<std::string, std::string>> ret;
AddressHash ret;
for (auto const& i: m_addrLookup)
if (m_keyInfo.count(i.second) > 0)
ret.insert(i.first);
return ret;
}
std::unordered_map<Address, std::pair<std::string, std::string>> KeyManager::accountDetails() const
{
std::unordered_map<Address, std::pair<std::string, std::string>> ret;
for (auto const& i: m_addrLookup)
if (m_keyInfo.count(i.second) > 0)
ret[i.first] = make_pair(m_keyInfo.at(i.second).info, m_passwordInfo.at(m_keyInfo.at(i.second).passHash));

15
libethereum/KeyManager.h

@ -28,7 +28,8 @@
namespace dev
{
namespace eth
{
class UnknownPassword: public Exception {};
struct KeyInfo
@ -65,7 +66,8 @@ public:
bool load(std::string const& _pass);
void save(std::string const& _pass) const { write(_pass, m_keysFile); }
std::map<Address, std::pair<std::string, std::string>> keys() const;
AddressHash accounts() const;
std::unordered_map<Address, std::pair<std::string, std::string>> accountDetails() const;
h128 uuid(Address const& _a) const;
Address address(h128 const& _uuid) const;
@ -92,12 +94,12 @@ private:
void write(h128 const& _key, std::string const& _keysFile) const;
// Ethereum keys.
std::map<Address, h128> m_addrLookup;
std::map<h128, KeyInfo> m_keyInfo;
std::map<h256, std::string> m_passwordInfo;
std::unordered_map<Address, h128> m_addrLookup;
std::unordered_map<h128, KeyInfo> m_keyInfo;
std::unordered_map<h256, std::string> m_passwordInfo;
// Passwords that we're storing.
mutable std::map<h256, std::string> m_cachedPasswords;
mutable std::unordered_map<h256, std::string> m_cachedPasswords;
// The default password for keys in the keystore - protected by the master password.
std::string m_password;
@ -108,3 +110,4 @@ private:
};
}
}

5
libtestutils/FixedWebThreeServer.cpp

@ -16,7 +16,12 @@
*/
/** @file FixedWebThreeStubServer.cpp
* @author Marek Kotewicz <marek@ethdev.com>
* @author Gav Wood <i@gavwood.com>
* @date 2015
*/
#include "FixedWebThreeServer.h"
#include <libethereum/Interface.h>
using namespace std;
using namespace dev;
using namespace eth;

6
libtestutils/FixedWebThreeServer.h

@ -23,6 +23,7 @@
#include <libdevcore/Exceptions.h>
#include <libweb3jsonrpc/WebThreeStubServerBase.h>
#include <libweb3jsonrpc/AccountHolder.h>
/**
* @brief dummy JSON-RPC api implementation
@ -33,7 +34,10 @@
class FixedWebThreeServer: public dev::WebThreeStubServerBase, public dev::WebThreeStubDatabaseFace
{
public:
FixedWebThreeServer(jsonrpc::AbstractServerConnector& _conn, std::vector<dev::KeyPair> const& _accounts, dev::eth::Interface* _client): WebThreeStubServerBase(_conn, _accounts), m_client(_client) {};
FixedWebThreeServer(jsonrpc::AbstractServerConnector& _conn, std::vector<dev::KeyPair> const& _allAccounts, dev::eth::Interface* _client):
WebThreeStubServerBase(_conn, std::make_shared<dev::eth::FixedAccountHolder>([=](){return _client;}, _allAccounts), _allAccounts),
m_client(_client)
{}
private:
dev::eth::Interface* client() override { return m_client; }

51
libweb3jsonrpc/AccountHolder.cpp

@ -26,6 +26,7 @@
#include <ctime>
#include <libdevcore/Guards.h>
#include <libethereum/Client.h>
#include <libethereum/KeyManager.h>
using namespace std;
using namespace dev;
@ -35,31 +36,23 @@ vector<TransactionSkeleton> g_emptyQueue;
static std::mt19937 g_randomNumberGenerator(time(0));
static Mutex x_rngMutex;
void AccountHolder::setAccounts(vector<KeyPair> const& _accounts)
vector<Address> AccountHolder::allAccounts() const
{
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;
vector<Address> accounts;
accounts += realAccounts();
for (auto const& pair: m_proxyAccounts)
if (!isRealAccount(pair.first))
accounts.push_back(pair.first);
return accounts;
}
Address const& AccountHolder::getDefaultTransactAccount() const
Address const& AccountHolder::defaultTransactAccount() const
{
if (m_accounts.empty())
auto accounts = realAccounts();
if (accounts.empty())
return ZeroAddress;
Address const* bestMatch = &m_accounts.front();
for (auto const& account: m_accounts)
Address const* bestMatch = &*accounts.begin();
for (auto const& account: accounts)
if (m_client()->balanceAt(account) > m_client()->balanceAt(*bestMatch))
bestMatch = &account;
return *bestMatch;
@ -94,7 +87,7 @@ void AccountHolder::queueTransaction(TransactionSkeleton const& _transaction)
m_transactionQueues[id].second.push_back(_transaction);
}
vector<TransactionSkeleton> const& AccountHolder::getQueuedTransactions(int _id) const
vector<TransactionSkeleton> const& AccountHolder::queuedTransactions(int _id) const
{
if (!m_transactionQueues.count(_id))
return g_emptyQueue;
@ -106,3 +99,27 @@ void AccountHolder::clearQueue(int _id)
if (m_transactionQueues.count(_id))
m_transactionQueues.at(_id).second.clear();
}
AddressHash SimpleAccountHolder::realAccounts() const
{
return m_keyManager.accounts();
}
void SimpleAccountHolder::authenticate(dev::eth::TransactionSkeleton const& _t)
{
if (isRealAccount(_t.from))
m_client()->submitTransaction(m_keyManager.secret(_t.from, [&](){ return m_getPassword(_t.from); }), _t);
else if (isProxyAccount(_t.from))
queueTransaction(_t);
}
void FixedAccountHolder::authenticate(dev::eth::TransactionSkeleton const& _t)
{
if (isRealAccount(_t.from))
m_client()->submitTransaction(m_accounts[_t.from], _t);
else if (isProxyAccount(_t.from))
queueTransaction(_t);
}

85
libweb3jsonrpc/AccountHolder.h

@ -24,17 +24,20 @@
#pragma once
#include <functional>
#include <algorithm>
#include <vector>
#include <map>
#include <libdevcrypto/Common.h>
#include <libethcore/CommonJS.h>
#include <libethereum/Transaction.h>
namespace dev
{
namespace eth
{
class KeyManager;
class Interface;
}
/**
* Manages real accounts (where we know the secret key) and proxy accounts (where transactions
@ -43,32 +46,84 @@ class Interface;
class AccountHolder
{
public:
explicit AccountHolder(std::function<eth::Interface*()> const& _client): m_client(_client) {}
explicit AccountHolder(std::function<Interface*()> const& _client): m_client(_client) {}
// easiest to return keyManager.addresses();
virtual AddressHash realAccounts() const = 0;
// use m_web3's submitTransaction
// or use AccountHolder::queueTransaction(_t) to accept
virtual void authenticate(dev::eth::TransactionSkeleton const& _t) = 0;
/// 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; }
Addresses allAccounts() const;
bool isRealAccount(Address const& _account) const { return realAccounts().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& getDefaultTransactAccount() const;
Address const& defaultTransactAccount() 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;
std::vector<eth::TransactionSkeleton> const& queuedTransactions(int _id) const;
void clearQueue(int _id);
protected:
std::function<Interface*()> m_client;
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;
std::unordered_map<Address, int> m_proxyAccounts;
std::unordered_map<int, std::pair<Address, TransactionQueue>> m_transactionQueues;
};
class SimpleAccountHolder: public AccountHolder
{
public:
SimpleAccountHolder(std::function<Interface*()> const& _client, std::function<std::string(Address)> const& _getPassword, KeyManager& _keyman):
AccountHolder(_client),
m_getPassword(_getPassword),
m_keyManager(_keyman)
{}
AddressHash realAccounts() const override;
void authenticate(dev::eth::TransactionSkeleton const& _t) override;
private:
std::function<std::string(Address)> m_getPassword;
KeyManager& m_keyManager;
};
class FixedAccountHolder: public AccountHolder
{
public:
FixedAccountHolder(std::function<Interface*()> const& _client, std::vector<dev::KeyPair> const& _accounts):
AccountHolder(_client)
{
setAccounts(_accounts);
}
void setAccounts(std::vector<dev::KeyPair> const& _accounts)
{
for (auto const& i: _accounts)
m_accounts[i.address()] = i.secret();
}
dev::AddressHash realAccounts() const override
{
dev::AddressHash ret;
for (auto const& i: m_accounts)
ret.insert(i.first);
return ret;
}
// use m_web3's submitTransaction
// or use AccountHolder::queueTransaction(_t) to accept
void authenticate(dev::eth::TransactionSkeleton const& _t) override;
private:
std::unordered_map<dev::Address, dev::Secret> m_accounts;
};
}
}

4
libweb3jsonrpc/WebThreeStubServer.cpp

@ -33,8 +33,8 @@ using namespace std;
using namespace dev;
using namespace dev::eth;
WebThreeStubServer::WebThreeStubServer(jsonrpc::AbstractServerConnector& _conn, WebThreeDirect& _web3, std::vector<dev::KeyPair> const& _accounts):
WebThreeStubServerBase(_conn, _accounts),
WebThreeStubServer::WebThreeStubServer(jsonrpc::AbstractServerConnector& _conn, WebThreeDirect& _web3, shared_ptr<AccountHolder> const& _ethAccounts, std::vector<dev::KeyPair> const& _shhAccounts):
WebThreeStubServerBase(_conn, _ethAccounts, _shhAccounts),
m_web3(_web3)
{
auto path = getDataDir() + "/.web3";

2
libweb3jsonrpc/WebThreeStubServer.h

@ -41,7 +41,7 @@ class WebThreeDirect;
class WebThreeStubServer: public dev::WebThreeStubServerBase, public dev::WebThreeStubDatabaseFace
{
public:
WebThreeStubServer(jsonrpc::AbstractServerConnector& _conn, dev::WebThreeDirect& _web3, std::vector<dev::KeyPair> const& _accounts);
WebThreeStubServer(jsonrpc::AbstractServerConnector& _conn, dev::WebThreeDirect& _web3, std::shared_ptr<dev::eth::AccountHolder> const& _ethAccounts, std::vector<dev::KeyPair> const& _shhAccounts);
virtual std::string web3_clientVersion();

43
libweb3jsonrpc/WebThreeStubServerBase.cpp

@ -297,10 +297,11 @@ static Json::Value toJson(h256 const& _h, shh::Envelope const& _e, shh::Message
return res;
}
WebThreeStubServerBase::WebThreeStubServerBase(AbstractServerConnector& _conn, vector<dev::KeyPair> const& _accounts):
AbstractWebThreeStubServer(_conn), m_ethAccounts(make_shared<AccountHolder>(bind(&WebThreeStubServerBase::client, this)))
WebThreeStubServerBase::WebThreeStubServerBase(AbstractServerConnector& _conn, std::shared_ptr<dev::eth::AccountHolder> const& _ethAccounts, vector<dev::KeyPair> const& _sshAccounts):
AbstractWebThreeStubServer(_conn),
m_ethAccounts(_ethAccounts)
{
m_ethAccounts->setAccounts(_accounts);
setIdentities(_sshAccounts);
}
void WebThreeStubServerBase::setIdentities(vector<dev::KeyPair> const& _ids)
@ -353,7 +354,7 @@ string WebThreeStubServerBase::eth_gasPrice()
Json::Value WebThreeStubServerBase::eth_accounts()
{
Json::Value ret(Json::arrayValue);
for (auto const& i: m_ethAccounts->getAllAccounts())
for (auto const& i: m_ethAccounts->allAccounts())
ret.append(toJS(i));
return ret;
}
@ -499,7 +500,7 @@ string WebThreeStubServerBase::eth_sendTransaction(Json::Value const& _json)
TransactionSkeleton t = toTransaction(_json);
if (!t.from)
t.from = m_ethAccounts->getDefaultTransactAccount();
t.from = m_ethAccounts->defaultTransactAccount();
if (t.creation)
ret = toJS(right160(sha3(rlpList(t.from, client()->countAt(t.from)))));;
if (t.gasPrice == UndefinedU256)
@ -507,10 +508,7 @@ string WebThreeStubServerBase::eth_sendTransaction(Json::Value const& _json)
if (t.gas == UndefinedU256)
t.gas = min<u256>(client()->gasLimitRemaining() / 5, client()->balanceAt(t.from) / t.gasPrice);
if (m_ethAccounts->isRealAccount(t.from))
authenticate(t, false);
else if (m_ethAccounts->isProxyAccount(t.from))
authenticate(t, true);
m_ethAccounts->authenticate(t);
return ret;
}
@ -528,7 +526,7 @@ string WebThreeStubServerBase::eth_signTransaction(Json::Value const& _json)
TransactionSkeleton t = toTransaction(_json);
if (!t.from)
t.from = m_ethAccounts->getDefaultTransactAccount();
t.from = m_ethAccounts->defaultTransactAccount();
if (t.creation)
ret = toJS(right160(sha3(rlpList(t.from, client()->countAt(t.from)))));;
if (t.gasPrice == UndefinedU256)
@ -536,10 +534,7 @@ string WebThreeStubServerBase::eth_signTransaction(Json::Value const& _json)
if (t.gas == UndefinedU256)
t.gas = min<u256>(client()->gasLimitRemaining() / 5, client()->balanceAt(t.from) / t.gasPrice);
if (m_ethAccounts->isRealAccount(t.from))
authenticate(t, false);
else if (m_ethAccounts->isProxyAccount(t.from))
authenticate(t, true);
m_ethAccounts->authenticate(t);
return toJS((t.creation ? Transaction(t.value, t.gasPrice, t.gas, t.data) : Transaction(t.value, t.gasPrice, t.gas, t.to, t.data)).sha3(WithoutSignature));
}
@ -579,7 +574,7 @@ string WebThreeStubServerBase::eth_call(Json::Value const& _json, string const&
{
TransactionSkeleton t = toTransaction(_json);
if (!t.from)
t.from = m_ethAccounts->getDefaultTransactAccount();
t.from = m_ethAccounts->defaultTransactAccount();
// if (!m_accounts->isRealAccount(t.from))
// return ret;
if (t.gasPrice == UndefinedU256)
@ -593,7 +588,6 @@ string WebThreeStubServerBase::eth_call(Json::Value const& _json, string const&
{
BOOST_THROW_EXCEPTION(JsonRpcException(Errors::ERROR_RPC_INVALID_PARAMS));
}
}
bool WebThreeStubServerBase::eth_flush()
@ -906,7 +900,7 @@ Json::Value WebThreeStubServerBase::eth_fetchQueuedTransactions(string const& _a
auto id = jsToInt(_accountId);
Json::Value ret(Json::arrayValue);
// TODO: throw an error on no account with given id
for (TransactionSkeleton const& t: m_ethAccounts->getQueuedTransactions(id))
for (TransactionSkeleton const& t: m_ethAccounts->queuedTransactions(id))
ret.append(toJson(t));
m_ethAccounts->clearQueue(id);
return ret;
@ -1077,18 +1071,3 @@ Json::Value WebThreeStubServerBase::shh_getMessages(string const& _filterId)
BOOST_THROW_EXCEPTION(JsonRpcException(Errors::ERROR_RPC_INVALID_PARAMS));
}
}
void WebThreeStubServerBase::authenticate(TransactionSkeleton const& _t, bool _toProxy)
{
if (_toProxy)
m_ethAccounts->queueTransaction(_t);
else if (_t.to)
client()->submitTransaction(m_ethAccounts->secretKey(_t.from), _t.value, _t.to, _t.data, _t.gas, _t.gasPrice);
else
client()->submitTransaction(m_ethAccounts->secretKey(_t.from), _t.value, _t.data, _t.gas, _t.gasPrice);
}
void WebThreeStubServerBase::setAccounts(const vector<KeyPair>& _accounts)
{
m_ethAccounts->setAccounts(_accounts);
}

10
libweb3jsonrpc/WebThreeStubServerBase.h

@ -36,10 +36,10 @@
namespace dev
{
class WebThreeNetworkFace;
class AccountHolder;
class KeyPair;
namespace eth
{
class AccountHolder;
struct TransactionSkeleton;
class Interface;
}
@ -68,7 +68,7 @@ public:
class WebThreeStubServerBase: public AbstractWebThreeStubServer
{
public:
WebThreeStubServerBase(jsonrpc::AbstractServerConnector& _conn, std::vector<dev::KeyPair> const& _accounts);
WebThreeStubServerBase(jsonrpc::AbstractServerConnector& _conn, std::shared_ptr<dev::eth::AccountHolder> const& _ethAccounts, std::vector<dev::KeyPair> const& _sshAccounts);
virtual std::string web3_sha3(std::string const& _param1);
virtual std::string web3_clientVersion() { return "C++ (ethereum-cpp)"; }
@ -134,20 +134,16 @@ public:
virtual Json::Value shh_getFilterChanges(std::string const& _filterId);
virtual Json::Value shh_getMessages(std::string const& _filterId);
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_shhIds; }
protected:
virtual void authenticate(dev::eth::TransactionSkeleton const& _t, bool _toProxy);
protected:
virtual dev::eth::Interface* client() = 0;
virtual std::shared_ptr<dev::shh::Interface> face() = 0;
virtual dev::WebThreeNetworkFace* network() = 0;
virtual dev::WebThreeStubDatabaseFace* db() = 0;
std::shared_ptr<dev::AccountHolder> m_ethAccounts;
std::shared_ptr<dev::eth::AccountHolder> m_ethAccounts;
std::map<dev::Public, dev::Secret> m_shhIds;
std::map<unsigned, dev::Public> m_shhWatches;

5
mix/ClientModel.cpp

@ -85,7 +85,8 @@ ClientModel::ClientModel():
connect(this, &ClientModel::runComplete, this, &ClientModel::showDebugger, Qt::QueuedConnection);
m_client.reset(new MixClient(QStandardPaths::writableLocation(QStandardPaths::TempLocation).toStdString()));
m_web3Server.reset(new Web3Server(*m_rpcConnector.get(), std::vector<KeyPair>(), m_client.get()));
m_ethAccounts = make_shared<FixedAccountHolder>([=](){return m_client.get();}, std::vector<KeyPair>());
m_web3Server.reset(new Web3Server(*m_rpcConnector.get(), m_ethAccounts, std::vector<KeyPair>(), m_client.get()));
connect(m_web3Server.get(), &Web3Server::newTransaction, this, &ClientModel::onNewTransaction, Qt::DirectConnection);
}
@ -280,7 +281,7 @@ void ClientModel::setupState(QVariantMap _state)
transactionSequence.push_back(transactionSettings);
}
}
m_web3Server->setAccounts(userAccounts);
m_ethAccounts->setAccounts(userAccounts);
executeSequence(transactionSequence, accounts, Secret(_state.value("miner").toMap().value("secret").toString().toStdString()));
}

3
mix/ClientModel.h

@ -35,7 +35,7 @@
namespace dev
{
namespace eth { class Account; }
namespace eth { class Account; class FixedAccountHolder; }
namespace mix
{
@ -235,6 +235,7 @@ private:
std::unique_ptr<MixClient> m_client;
std::unique_ptr<RpcConnector> m_rpcConnector;
std::unique_ptr<Web3Server> m_web3Server;
std::shared_ptr<eth::FixedAccountHolder> m_ethAccounts;
QList<u256> m_gasCosts;
std::map<QString, Address> m_contractAddresses;
std::map<Address, QString> m_contractNames;

5
mix/Web3Server.cpp

@ -24,6 +24,7 @@
#include <libdevcore/Log.h>
#include <libethereum/Interface.h>
#include <libwebthree/WebThree.h>
#include <libweb3jsonrpc/AccountHolder.h>
#include "Web3Server.h"
using namespace dev::mix;
@ -108,8 +109,8 @@ class EmptyNetwork : public dev::WebThreeNetworkFace
}
Web3Server::Web3Server(jsonrpc::AbstractServerConnector& _conn, std::vector<dev::KeyPair> const& _accounts, dev::eth::Interface* _client):
WebThreeStubServerBase(_conn, _accounts),
Web3Server::Web3Server(jsonrpc::AbstractServerConnector& _conn, std::shared_ptr<eth::AccountHolder> const& _ethAccounts, std::vector<dev::KeyPair> const& _shhAccounts, dev::eth::Interface* _client):
WebThreeStubServerBase(_conn, _ethAccounts, _shhAccounts),
m_client(_client),
m_network(new EmptyNetwork())
{

3
mix/Web3Server.h

@ -25,6 +25,7 @@
#include <map>
#include <string>
#include <QObject>
#include <libweb3jsonrpc/AccountHolder.h>
#include <libweb3jsonrpc/WebThreeStubServerBase.h>
namespace dev
@ -38,7 +39,7 @@ class Web3Server: public QObject, public dev::WebThreeStubServerBase, public dev
Q_OBJECT
public:
Web3Server(jsonrpc::AbstractServerConnector& _conn, std::vector<dev::KeyPair> const& _accounts, dev::eth::Interface* _client);
Web3Server(jsonrpc::AbstractServerConnector& _conn, std::shared_ptr<eth::AccountHolder> const& _ethAccounts, std::vector<dev::KeyPair> const& _shhAccounts, dev::eth::Interface* _client);
virtual ~Web3Server();
signals:

28
test/libweb3jsonrpc/AccountHolder.cpp

@ -22,6 +22,9 @@
#include <boost/test/unit_test.hpp>
#include <libweb3jsonrpc/AccountHolder.h>
using namespace std;
using namespace dev;
using namespace eth;
namespace dev
{
@ -32,16 +35,17 @@ 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());
FixedAccountHolder h = FixedAccountHolder(function<Interface*()>(), vector<KeyPair>());
BOOST_CHECK(h.allAccounts().empty());
BOOST_CHECK(h.realAccounts().empty());
Address addr("abababababababababababababababababababab");
Address addr2("abababababababababababababababababababab");
int id = h.addProxyAccount(addr);
BOOST_CHECK(h.getQueuedTransactions(id).empty());
BOOST_CHECK(h.queuedTransactions(id).empty());
// register it again
int secondID = h.addProxyAccount(addr);
BOOST_CHECK(h.getQueuedTransactions(secondID).empty());
BOOST_CHECK(h.queuedTransactions(secondID).empty());
eth::TransactionSkeleton t1;
eth::TransactionSkeleton t2;
@ -49,20 +53,20 @@ BOOST_AUTO_TEST_CASE(ProxyAccountUseCase)
t1.data = fromHex("12345678");
t2.from = addr;
t2.data = fromHex("abcdef");
BOOST_CHECK(h.getQueuedTransactions(id).empty());
BOOST_CHECK(h.queuedTransactions(id).empty());
h.queueTransaction(t1);
BOOST_CHECK_EQUAL(1, h.getQueuedTransactions(id).size());
BOOST_CHECK_EQUAL(1, h.queuedTransactions(id).size());
h.queueTransaction(t2);
BOOST_REQUIRE_EQUAL(2, h.getQueuedTransactions(id).size());
BOOST_REQUIRE_EQUAL(2, h.queuedTransactions(id).size());
// second proxy should not see transactions
BOOST_CHECK(h.getQueuedTransactions(secondID).empty());
BOOST_CHECK(h.queuedTransactions(secondID).empty());
BOOST_CHECK(h.getQueuedTransactions(id)[0].data == t1.data);
BOOST_CHECK(h.getQueuedTransactions(id)[1].data == t2.data);
BOOST_CHECK(h.queuedTransactions(id)[0].data == t1.data);
BOOST_CHECK(h.queuedTransactions(id)[1].data == t2.data);
h.clearQueue(id);
BOOST_CHECK(h.getQueuedTransactions(id).empty());
BOOST_CHECK(h.queuedTransactions(id).empty());
// removing fails because it never existed
BOOST_CHECK(!h.removeProxyAccount(secondID));
BOOST_CHECK(h.removeProxyAccount(id));

Loading…
Cancel
Save