You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
519 lines
13 KiB
519 lines
13 KiB
#if ETH_QTQML
|
|
#include <QtQml/QtQml>
|
|
#endif
|
|
#include <QtCore/QtCore>
|
|
#include <QtWebKitWidgets/QWebFrame>
|
|
#include <libethcore/FileSystem.h>
|
|
#include <libethcore/Dagger.h>
|
|
#include <libevmface/Instruction.h>
|
|
#include <liblll/Compiler.h>
|
|
#include <libethereum/Client.h>
|
|
#include <libethereum/PeerServer.h>
|
|
#include "QEthereum.h"
|
|
using namespace std;
|
|
|
|
// types
|
|
using eth::bytes;
|
|
using eth::bytesConstRef;
|
|
using eth::h160;
|
|
using eth::h256;
|
|
using eth::u160;
|
|
using eth::u256;
|
|
using eth::u256s;
|
|
using eth::Address;
|
|
using eth::BlockInfo;
|
|
using eth::Client;
|
|
using eth::Instruction;
|
|
using eth::KeyPair;
|
|
using eth::NodeMode;
|
|
using eth::PeerInfo;
|
|
using eth::RLP;
|
|
using eth::Secret;
|
|
using eth::Transaction;
|
|
|
|
// functions
|
|
using eth::toHex;
|
|
using eth::disassemble;
|
|
using eth::formatBalance;
|
|
using eth::fromHex;
|
|
using eth::right160;
|
|
using eth::simpleDebugOut;
|
|
using eth::toLog2;
|
|
using eth::toString;
|
|
using eth::units;
|
|
|
|
// vars
|
|
using eth::g_logPost;
|
|
using eth::g_logVerbosity;
|
|
using eth::c_instructionInfo;
|
|
|
|
// Horrible global for the mainwindow. Needed for the QmlEthereums to find the Main window which acts as multiplexer for now.
|
|
// Can get rid of this once we've sorted out ITC for signalling & multiplexed querying.
|
|
eth::Client* g_qmlClient;
|
|
QObject* g_qmlMain;
|
|
|
|
QmlAccount::QmlAccount(QObject*)
|
|
{
|
|
}
|
|
|
|
QmlAccount::~QmlAccount()
|
|
{
|
|
}
|
|
|
|
void QmlAccount::setEthereum(QmlEthereum* _eth)
|
|
{
|
|
if (m_eth == _eth)
|
|
return;
|
|
if (m_eth)
|
|
disconnect(m_eth, SIGNAL(changed()), this, SIGNAL(changed()));
|
|
m_eth = _eth;
|
|
if (m_eth)
|
|
connect(m_eth, SIGNAL(changed()), this, SIGNAL(changed()));
|
|
ethChanged();
|
|
changed();
|
|
}
|
|
|
|
eth::u256 QmlAccount::balance() const
|
|
{
|
|
if (m_eth)
|
|
return m_eth->balanceAt(m_address);
|
|
return u256(0);
|
|
}
|
|
|
|
double QmlAccount::txCount() const
|
|
{
|
|
if (m_eth)
|
|
return m_eth->txCountAt(m_address);
|
|
return 0;
|
|
}
|
|
|
|
bool QmlAccount::isContract() const
|
|
{
|
|
if (m_eth)
|
|
return m_eth->isContractAt(m_address);
|
|
return 0;
|
|
}
|
|
|
|
QmlEthereum::QmlEthereum(QObject* _p): QObject(_p)
|
|
{
|
|
connect(g_qmlMain, SIGNAL(changed()), SIGNAL(changed()));
|
|
}
|
|
|
|
QmlEthereum::~QmlEthereum()
|
|
{
|
|
}
|
|
|
|
Client* QmlEthereum::client() const
|
|
{
|
|
return g_qmlClient;
|
|
}
|
|
|
|
Address QmlEthereum::coinbase() const
|
|
{
|
|
return client()->address();
|
|
}
|
|
|
|
void QmlEthereum::setCoinbase(Address _a)
|
|
{
|
|
if (client()->address() != _a)
|
|
{
|
|
client()->setAddress(_a);
|
|
changed();
|
|
}
|
|
}
|
|
|
|
u256 QmlEthereum::balanceAt(Address _a) const
|
|
{
|
|
return client()->postState().balance(_a);
|
|
}
|
|
|
|
bool QmlEthereum::isContractAt(Address _a) const
|
|
{
|
|
return client()->postState().addressHasCode(_a);
|
|
}
|
|
|
|
bool QmlEthereum::isMining() const
|
|
{
|
|
return client()->isMining();
|
|
}
|
|
|
|
bool QmlEthereum::isListening() const
|
|
{
|
|
return client()->haveNetwork();
|
|
}
|
|
|
|
void QmlEthereum::setMining(bool _l)
|
|
{
|
|
if (_l)
|
|
client()->startMining();
|
|
else
|
|
client()->stopMining();
|
|
}
|
|
|
|
void QmlEthereum::setListening(bool _l)
|
|
{
|
|
if (_l)
|
|
client()->startNetwork();
|
|
else
|
|
client()->stopNetwork();
|
|
}
|
|
|
|
double QmlEthereum::txCountAt(Address _a) const
|
|
{
|
|
return (double)client()->postState().transactionsFrom(_a);
|
|
}
|
|
|
|
unsigned QmlEthereum::peerCount() const
|
|
{
|
|
return (unsigned)client()->peerCount();
|
|
}
|
|
|
|
void QmlEthereum::transact(Secret _secret, u256 _amount, u256 _gasPrice, u256 _gas, QByteArray _init)
|
|
{
|
|
client()->transact(_secret, _amount, bytes(_init.data(), _init.data() + _init.size()), _gas, _gasPrice);
|
|
}
|
|
|
|
void QmlEthereum::transact(Secret _secret, Address _dest, u256 _amount, u256 _gasPrice, u256 _gas, QByteArray _data)
|
|
{
|
|
client()->transact(_secret, _amount, _dest, bytes(_data.data(), _data.data() + _data.size()), _gas, _gasPrice);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
eth::bytes toBytes(QString const& _s)
|
|
{
|
|
if (_s.startsWith("0x"))
|
|
// Hex
|
|
return eth::fromHex(_s.mid(2).toStdString());
|
|
else if (!_s.contains(QRegExp("[^0-9]")))
|
|
// Decimal
|
|
return eth::toCompactBigEndian(eth::bigint(_s.toStdString()));
|
|
else
|
|
// Binary
|
|
return asBytes(_s);
|
|
}
|
|
|
|
QString padded(QString const& _s, unsigned _l, unsigned _r)
|
|
{
|
|
eth::bytes b = toBytes(_s);
|
|
while (b.size() < _l)
|
|
b.insert(b.begin(), 0);
|
|
while (b.size() < _r)
|
|
b.push_back(0);
|
|
return asQString(eth::asBytes(eth::asString(b).substr(b.size() - max(_l, _r))));
|
|
}
|
|
|
|
//"0xff".bin().unbin()
|
|
|
|
QString QEthereum::secretToAddress(QString _s) const
|
|
{
|
|
return toQJS(KeyPair(toSecret(_s)).address());
|
|
}
|
|
|
|
QString padded(QString const& _s, unsigned _l)
|
|
{
|
|
if (_s.startsWith("0x") || !_s.contains(QRegExp("[^0-9]")))
|
|
// Numeric: pad to right
|
|
return padded(_s, _l, _l);
|
|
else
|
|
// Text: pad to the left
|
|
return padded(_s, 0, _l);
|
|
}
|
|
|
|
QString unpadded(QString _s)
|
|
{
|
|
while (_s.size() && _s.endsWith(QChar(0)))
|
|
_s.chop(1);
|
|
return _s;
|
|
}
|
|
|
|
QEthereum::QEthereum(QObject* _p, Client* _c, QList<eth::KeyPair> _accounts): QObject(_p), m_client(_c), m_accounts(_accounts)
|
|
{
|
|
connect(_p, SIGNAL(changed()), SIGNAL(changed()));
|
|
}
|
|
|
|
QEthereum::~QEthereum()
|
|
{
|
|
}
|
|
|
|
void QEthereum::setAccounts(QList<eth::KeyPair> _l)
|
|
{
|
|
cnote << "WAS:";
|
|
for (auto i: m_accounts)
|
|
cnote << i.sec();
|
|
cnote << "NOW:";
|
|
for (auto i: _l)
|
|
cnote << i.sec();
|
|
m_accounts = _l;
|
|
changed();
|
|
}
|
|
|
|
void QEthereum::setup(QWebFrame* _e)
|
|
{
|
|
// disconnect
|
|
disconnect(SIGNAL(changed()));
|
|
_e->addToJavaScriptWindowObject("eth", this, QWebFrame::ScriptOwnership);
|
|
/* _e->addToJavaScriptWindowObject("u256", new U256Helper, QWebFrame::ScriptOwnership);
|
|
_e->addToJavaScriptWindowObject("key", new KeyHelper, QWebFrame::ScriptOwnership);
|
|
_e->addToJavaScriptWindowObject("bytes", new BytesHelper, QWebFrame::ScriptOwnership);*/
|
|
_e->evaluateJavaScript("eth.newBlock = function(f) { eth.changed.connect(f) }");
|
|
_e->evaluateJavaScript("eth.watch = function(a, s, f) { eth.changed.connect(f ? f : s) }");
|
|
_e->evaluateJavaScript("eth.create = function(s, v, c, g, p, f) { var v = eth.doCreate(s, v, c, g, p); if (f) f(v) }");
|
|
_e->evaluateJavaScript("eth.transact = function(s, v, t, d, g, p, f) { eth.doTransact(s, v, t, d, g, p); if (f) f() }");
|
|
_e->evaluateJavaScript("eth.transactions = function(a) { return JSON.parse(eth.getTransactions(JSON.stringify(a))); }");
|
|
_e->evaluateJavaScript("String.prototype.pad = function(l, r) { return eth.pad(this, l, r) }");
|
|
_e->evaluateJavaScript("String.prototype.bin = function() { return eth.toBinary(this) }");
|
|
_e->evaluateJavaScript("String.prototype.unbin = function(l) { return eth.fromBinary(this) }");
|
|
_e->evaluateJavaScript("String.prototype.unpad = function(l) { return eth.unpad(this) }");
|
|
_e->evaluateJavaScript("String.prototype.dec = function() { return eth.toDecimal(this) }");
|
|
_e->evaluateJavaScript("String.prototype.sha3 = function() { return eth.sha3(this) }");
|
|
}
|
|
|
|
void QEthereum::teardown(QWebFrame*)
|
|
{
|
|
}
|
|
|
|
Client* QEthereum::client() const
|
|
{
|
|
return m_client;
|
|
}
|
|
|
|
QString QEthereum::lll(QString _s) const
|
|
{
|
|
return asQString(eth::compileLLL(_s.toStdString()));
|
|
}
|
|
|
|
QString QEthereum::sha3(QString _s) const
|
|
{
|
|
return toQJS(eth::sha3(asBytes(_s)));
|
|
}
|
|
|
|
QString QEthereum::coinbase() const
|
|
{
|
|
return toQJS(client()->address());
|
|
}
|
|
|
|
QString QEthereum::number() const
|
|
{
|
|
return QString::number(client()->blockChain().number() + 1);
|
|
}
|
|
|
|
QString QEthereum::account() const
|
|
{
|
|
if (m_accounts.empty())
|
|
return toQJS(Address());
|
|
return toQJS(m_accounts[0].address());
|
|
}
|
|
|
|
QStringList QEthereum::accounts() const
|
|
{
|
|
QStringList ret;
|
|
for (auto i: m_accounts)
|
|
ret.push_back(toQJS(i.address()));
|
|
return ret;
|
|
}
|
|
|
|
QString QEthereum::key() const
|
|
{
|
|
if (m_accounts.empty())
|
|
return toQJS(KeyPair().sec());
|
|
return toQJS(m_accounts[0].sec());
|
|
}
|
|
|
|
QStringList QEthereum::keys() const
|
|
{
|
|
QStringList ret;
|
|
for (auto i: m_accounts)
|
|
ret.push_back(toQJS(i.sec()));
|
|
return ret;
|
|
}
|
|
|
|
void QEthereum::setCoinbase(QString _a)
|
|
{
|
|
if (client()->address() != toAddress(_a))
|
|
{
|
|
client()->setAddress(toAddress(_a));
|
|
changed();
|
|
}
|
|
}
|
|
|
|
QString QEthereum::balanceAt(QString _a) const
|
|
{
|
|
return toQJS(client()->postState().balance(toAddress(_a)));
|
|
}
|
|
|
|
QString QEthereum::storageAt(QString _a, QString _p) const
|
|
{
|
|
eth::ClientGuard l(const_cast<Client*>(m_client));
|
|
return toQJS(client()->postState().storage(toAddress(_a), toU256(_p)));
|
|
}
|
|
|
|
double QEthereum::txCountAt(QString _a) const
|
|
{
|
|
return (double)client()->postState().transactionsFrom(toAddress(_a));
|
|
}
|
|
|
|
bool QEthereum::isContractAt(QString _a) const
|
|
{
|
|
return client()->postState().addressHasCode(toAddress(_a));
|
|
}
|
|
|
|
u256 QEthereum::balanceAt(Address _a) const
|
|
{
|
|
return client()->postState().balance(_a);
|
|
}
|
|
|
|
double QEthereum::txCountAt(Address _a) const
|
|
{
|
|
return (double)client()->postState().transactionsFrom(_a);
|
|
}
|
|
|
|
bool QEthereum::isContractAt(Address _a) const
|
|
{
|
|
return client()->postState().addressHasCode(_a);
|
|
}
|
|
|
|
QString QEthereum::balanceAt(QString _a, int _block) const
|
|
{
|
|
return toQJS(client()->balanceAt(toAddress(_a), _block));
|
|
}
|
|
|
|
QString QEthereum::stateAt(QString _a, QString _p, int _block) const
|
|
{
|
|
return toQJS(client()->stateAt(toAddress(_a), toU256(_p), _block));
|
|
}
|
|
|
|
QString QEthereum::codeAt(QString _a, int _block) const
|
|
{
|
|
return ::fromBinary(client()->codeAt(toAddress(_a), _block));
|
|
}
|
|
|
|
double QEthereum::countAt(QString _a, int _block) const
|
|
{
|
|
return (double)(uint64_t)client()->countAt(toAddress(_a), _block);
|
|
}
|
|
|
|
QString QEthereum::getTransactions(QString _a) const
|
|
{
|
|
eth::TransactionFilter filter;
|
|
|
|
QJsonObject f = QJsonDocument::fromJson(_a.toUtf8()).object();
|
|
if (f.contains("earliest"))
|
|
filter.withEarliest(f["earliest"].toInt());
|
|
if (f.contains("latest"))
|
|
filter.withLatest(f["latest"].toInt());
|
|
if (f.contains("max"))
|
|
filter.withMax(f["max"].toInt());
|
|
if (f.contains("skip"))
|
|
filter.withSkip(f["skip"].toInt());
|
|
if (f.contains("from"))
|
|
{
|
|
if (f["from"].isArray())
|
|
for (auto i: f["from"].toArray())
|
|
filter.from(toAddress(i.toString()));
|
|
else
|
|
filter.from(toAddress(f["from"].toString()));
|
|
}
|
|
if (f.contains("to"))
|
|
{
|
|
if (f["to"].isArray())
|
|
for (auto i: f["to"].toArray())
|
|
filter.to(toAddress(i.toString()));
|
|
else
|
|
filter.to(toAddress(f["to"].toString()));
|
|
}
|
|
if (f.contains("altered"))
|
|
{
|
|
if (f["altered"].isArray())
|
|
for (auto i: f["altered"].toArray())
|
|
if (i.isObject())
|
|
filter.altered(toAddress(i.toObject()["id"].toString()), toU256(i.toObject()["at"].toString()));
|
|
else
|
|
filter.altered(toAddress(i.toString()));
|
|
else
|
|
if (f["altered"].isObject())
|
|
filter.altered(toAddress(f["altered"].toObject()["id"].toString()), toU256(f["altered"].toObject()["at"].toString()));
|
|
else
|
|
filter.altered(toAddress(f["altered"].toString()));
|
|
}
|
|
|
|
QJsonArray ret;
|
|
for (eth::PastTransaction const& t: m_client->transactions(filter))
|
|
{
|
|
QJsonObject v;
|
|
v["data"] = ::fromBinary(t.data);
|
|
v["gas"] = toQJS(t.gas);
|
|
v["gasPrice"] = toQJS(t.gasPrice);
|
|
v["nonce"] = (int)t.nonce;
|
|
v["to"] = toQJS(t.receiveAddress);
|
|
v["value"] = toQJS(t.value);
|
|
v["from"] = toQJS(t.sender());
|
|
v["timestamp"] = (int)t.timestamp;
|
|
v["block"] = toQJS(t.block);
|
|
v["index"] = (int)t.index;
|
|
v["age"] = (int)t.age;
|
|
ret.append(v);
|
|
}
|
|
return QString::fromUtf8(QJsonDocument(ret).toJson());
|
|
}
|
|
|
|
bool QEthereum::isMining() const
|
|
{
|
|
return client()->isMining();
|
|
}
|
|
|
|
bool QEthereum::isListening() const
|
|
{
|
|
return client()->haveNetwork();
|
|
}
|
|
|
|
void QEthereum::setMining(bool _l)
|
|
{
|
|
if (_l)
|
|
client()->startMining();
|
|
else
|
|
client()->stopMining();
|
|
}
|
|
|
|
void QEthereum::setListening(bool _l)
|
|
{
|
|
if (_l)
|
|
client()->startNetwork();
|
|
else
|
|
client()->stopNetwork();
|
|
}
|
|
|
|
unsigned QEthereum::peerCount() const
|
|
{
|
|
return (unsigned)client()->peerCount();
|
|
}
|
|
|
|
QString QEthereum::doCreate(QString _secret, QString _amount, QString _init, QString _gas, QString _gasPrice)
|
|
{
|
|
client()->changed();
|
|
auto ret = toQJS(client()->transact(toSecret(_secret), toU256(_amount), toBytes(_init), toU256(_gas), toU256(_gasPrice)));
|
|
while (!client()->peekChanged())
|
|
this_thread::sleep_for(chrono::milliseconds(10));
|
|
return ret;
|
|
}
|
|
|
|
void QEthereum::doTransact(QString _secret, QString _amount, QString _dest, QString _data, QString _gas, QString _gasPrice)
|
|
{
|
|
client()->changed();
|
|
client()->transact(toSecret(_secret), toU256(_amount), toAddress(_dest), toBytes(_data), toU256(_gas), toU256(_gasPrice));
|
|
while (!client()->peekChanged())
|
|
this_thread::sleep_for(chrono::milliseconds(10));
|
|
}
|
|
|
|
// extra bits needed to link on VS
|
|
#ifdef _MSC_VER
|
|
|
|
// include moc file, ofuscated to hide from automoc
|
|
#include\
|
|
"moc_QEthereum.cpp"
|
|
|
|
#endif
|
|
|