diff --git a/libethcore/CommonData.h b/libethcore/CommonData.h index 260fb3c2a..cfb5b80bb 100644 --- a/libethcore/CommonData.h +++ b/libethcore/CommonData.h @@ -105,6 +105,18 @@ inline std::string toBigEndianString(u160 _val) { std::string ret(20, '\0'); toB inline bytes toBigEndian(u256 _val) { bytes ret(32); toBigEndian(_val, ret); return ret; } inline bytes toBigEndian(u160 _val) { bytes ret(20); toBigEndian(_val, ret); return ret; } +/// Convenience function for toBigEndian. +/// @returns a byte array just big enough to represent @a _val. +template +inline bytes toCompactBigEndian(_T _val) +{ + int i = 0; + for (_T v = _val; v; ++i, v >>= 8) {} + bytes ret(i, 0); + toBigEndian(_val, ret); + return ret; +} + /// Convenience function for toBigEndian. /// @returns a string just big enough to represent @a _val. template diff --git a/libethereum/Instruction.cpp b/libethereum/Instruction.cpp index d486ba97c..8f30a8728 100644 --- a/libethereum/Instruction.cpp +++ b/libethereum/Instruction.cpp @@ -57,6 +57,8 @@ const std::map eth::c_instructions = { "CALLDATALOAD", Instruction::CALLDATALOAD }, { "CALLDATASIZE", Instruction::CALLDATASIZE }, { "CALLDATACOPY", Instruction::CALLDATACOPY }, + { "CODESIZE", Instruction::CODESIZE }, + { "CODECOPY", Instruction::CODECOPY }, { "GASPRICE", Instruction::GASPRICE }, { "PREVHASH", Instruction::PREVHASH }, { "COINBASE", Instruction::COINBASE }, @@ -144,6 +146,8 @@ const std::map eth::c_instructionInfo = { Instruction::CALLDATALOAD, { "CALLDATALOAD", 0, 1, 1 } }, { Instruction::CALLDATASIZE, { "CALLDATASIZE", 0, 0, 1 } }, { Instruction::CALLDATACOPY, { "CALLDATACOPY", 0, 3, 0 } }, + { Instruction::CODESIZE, { "CODESIZE", 0, 0, 1 } }, + { Instruction::CODECOPY, { "CODECOPY", 0, 3, 0 } }, { Instruction::GASPRICE, { "GASPRICE", 0, 0, 1 } }, { Instruction::PREVHASH, { "PREVHASH", 0, 0, 1 } }, { Instruction::COINBASE, { "COINBASE", 0, 0, 1 } }, @@ -783,6 +787,7 @@ static int compileLispFragment(char const*& d, char const* e, bool _quiet, bytes { // Check if true - predicate appendCode(o_code, o_locs, codes[i - 1], locs[i - 1]); + o_code.push_back((byte)Instruction::NOT); // Push the false location. ends.push_back((unsigned)o_code.size()); @@ -790,7 +795,6 @@ static int compileLispFragment(char const*& d, char const* e, bool _quiet, bytes pushLocation(o_code, 0); // Jump to end... - o_code.push_back((byte)Instruction::NOT); o_code.push_back((byte)Instruction::JUMPI); } o_code.push_back((byte)Instruction::POP); diff --git a/libethereum/Instruction.h b/libethereum/Instruction.h index 342076399..f49100a2e 100644 --- a/libethereum/Instruction.h +++ b/libethereum/Instruction.h @@ -61,6 +61,8 @@ enum class Instruction: uint8_t CALLDATALOAD, CALLDATASIZE, CALLDATACOPY, + CODESIZE, + CODECOPY, GASPRICE, PREVHASH = 0x40, diff --git a/libethereum/VM.h b/libethereum/VM.h index e24cf9465..2d3b8fffb 100644 --- a/libethereum/VM.h +++ b/libethereum/VM.h @@ -137,6 +137,10 @@ template eth::bytesConstRef eth::VM::go(Ext& _ext, uint64_t _steps) require(3); newTempSize = (unsigned)m_stack.back() + (unsigned)m_stack[m_stack.size() - 3]; break; + case Instruction::CODECOPY: + require(3); + newTempSize = (unsigned)m_stack.back() + (unsigned)m_stack[m_stack.size() - 3]; + break; case Instruction::BALANCE: runGas = c_balanceGas; @@ -328,7 +332,26 @@ template eth::bytesConstRef eth::VM::go(Ext& _ext, uint64_t _steps) m_stack.pop_back(); unsigned l = (unsigned)m_stack.back(); m_stack.pop_back(); - memcpy(m_temp.data() + mf, _ext.data.data() + cf, l); + unsigned el = cf + l > _ext.data.size() ? _ext.data.size() - cf : l; + memcpy(m_temp.data() + mf, _ext.data.data() + cf, el); + memset(m_temp.data() + mf + el, 0, l - el); + break; + } + case Instruction::CODESIZE: + m_stack.push_back(_ext.code.size()); + break; + case Instruction::CODECOPY: + { + require(3); + unsigned mf = (unsigned)m_stack.back(); + m_stack.pop_back(); + unsigned cf = (unsigned)m_stack.back(); + m_stack.pop_back(); + unsigned l = (unsigned)m_stack.back(); + m_stack.pop_back(); + unsigned el = cf + l > _ext.code.size() ? _ext.code.size() - cf : l; + memcpy(m_temp.data() + mf, _ext.code.data() + cf, el); + memset(m_temp.data() + mf + el, 0, l - el); break; } case Instruction::GASPRICE: diff --git a/libqethereum/QEthereum.cpp b/libqethereum/QEthereum.cpp index fab626593..163028a4d 100644 --- a/libqethereum/QEthereum.cpp +++ b/libqethereum/QEthereum.cpp @@ -189,12 +189,8 @@ eth::bytes toBytes(QString const& _s) // Hex return eth::fromHex(_s.mid(2).toStdString()); else if (!_s.contains(QRegExp("[^0-9]"))) - { // Decimal - eth::bytes ret; - eth::toBigEndian(eth::bigint(_s.toStdString()), ret); - return ret; - } + return eth::toCompactBigEndian(eth::bigint(_s.toStdString())); else // Binary return eth::asBytes(_s.toStdString()); @@ -207,8 +203,24 @@ QString padded(QString const& _s, unsigned _l, unsigned _r) b.insert(b.begin(), 0); while (b.size() < _r) b.push_back(0); - b.resize(_r); - return QString::fromStdString(eth::asString(b)); + return QString::fromStdString(eth::asString(b).substr(b.size() - max(_l, _r))); +} + +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 _accounts): QObject(_p), m_client(_c), m_accounts(_accounts) @@ -225,9 +237,9 @@ void QEthereum::setup(QWebFrame* _e) // disconnect disconnect(SIGNAL(changed())); _e->addToJavaScriptWindowObject("eth", this, QWebFrame::ScriptOwnership); - _e->addToJavaScriptWindowObject("u256", new U256Helper, QWebFrame::ScriptOwnership); +/* _e->addToJavaScriptWindowObject("u256", new U256Helper, QWebFrame::ScriptOwnership); _e->addToJavaScriptWindowObject("key", new KeyHelper, QWebFrame::ScriptOwnership); - _e->addToJavaScriptWindowObject("bytes", new BytesHelper, 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) { eth.doCreate(s, v, c, g, p); if (f) f() }"); @@ -235,6 +247,7 @@ void QEthereum::setup(QWebFrame* _e) _e->evaluateJavaScript("String.prototype.pad = function(l, r) { return eth.pad(this, l, r) }"); _e->evaluateJavaScript("String.prototype.bin = function(l) { 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) }"); } void QEthereum::teardown(QWebFrame*) @@ -246,58 +259,58 @@ Client* QEthereum::client() const return m_client; } -QVariant QEthereum::coinbase() const +QString QEthereum::coinbase() const { return toQJS(client()->address()); } -QVariant QEthereum::account() const +QString QEthereum::account() const { if (m_accounts.empty()) return toQJS(Address()); return toQJS(m_accounts[0].address()); } -QList QEthereum::accounts() const +QStringList QEthereum::accounts() const { - QList ret; + QStringList ret; for (auto i: m_accounts) ret.push_back(toQJS(i.address())); return ret; } -QVariant QEthereum::key() const +QString QEthereum::key() const { if (m_accounts.empty()) - return toQJS(KeyPair()); - return toQJS(m_accounts[0]); + return toQJS(KeyPair().sec()); + return toQJS(m_accounts[0].sec()); } -QList QEthereum::keys() const +QStringList QEthereum::keys() const { - QList ret; + QStringList ret; for (auto i: m_accounts) - ret.push_back(toQJS(i)); + ret.push_back(toQJS(i.sec())); return ret; } -void QEthereum::setCoinbase(QVariant _a) +void QEthereum::setCoinbase(QString _a) { - if (client()->address() != to
(_a)) + if (client()->address() != toAddress(_a)) { - client()->setAddress(to
(_a)); + client()->setAddress(toAddress(_a)); changed(); } } -QVariant QEthereum::balanceAt(QVariant _a) const +QString QEthereum::balanceAt(QString _a) const { - return toQJS(client()->postState().balance(to
(_a))); + return toQJS(client()->postState().balance(toAddress(_a))); } -QVariant QEthereum::storageAt(QVariant _a, QVariant _p) const +QString QEthereum::storageAt(QString _a, QString _p) const { - return toQJS(client()->postState().storage(to
(_a), to(_p))); + return toQJS(client()->postState().storage(toAddress(_a), toU256(_p))); } u256 QEthereum::balanceAt(Address _a) const @@ -305,9 +318,9 @@ u256 QEthereum::balanceAt(Address _a) const return client()->postState().balance(_a); } -bool QEthereum::isContractAt(QVariant _a) const +bool QEthereum::isContractAt(QString _a) const { - return client()->postState().addressHasCode(to
(_a)); + return client()->postState().addressHasCode(toAddress(_a)); } bool QEthereum::isContractAt(Address _a) const @@ -341,9 +354,9 @@ void QEthereum::setListening(bool _l) client()->stopNetwork(); } -double QEthereum::txCountAt(QVariant _a) const +double QEthereum::txCountAt(QString _a) const { - return (double)client()->postState().transactionsFrom(to
(_a)); + return (double)client()->postState().transactionsFrom(toAddress(_a)); } double QEthereum::txCountAt(Address _a) const @@ -356,14 +369,14 @@ unsigned QEthereum::peerCount() const return (unsigned)client()->peerCount(); } -QVariant QEthereum::doCreate(QVariant _secret, QVariant _amount, QByteArray _init, QVariant _gas, QVariant _gasPrice) +QString QEthereum::doCreate(QString _secret, QString _amount, QString _init, QString _gas, QString _gasPrice) { - return toQJS(client()->transact(to(_secret), to(_amount), bytes(_init.data(), _init.data() + _init.size()), to(_gas), to(_gasPrice))); + return toQJS(client()->transact(toSecret(_secret), toU256(_amount), toBytes(_init), toU256(_gas), toU256(_gasPrice))); } -void QEthereum::doTransact(QVariant _secret, QVariant _amount, QVariant _dest, QByteArray _data, QVariant _gas, QVariant _gasPrice) +void QEthereum::doTransact(QString _secret, QString _amount, QString _dest, QString _data, QString _gas, QString _gasPrice) { - client()->transact(to(_secret), to(_amount), to
(_dest), bytes(_data.data(), _data.data() + _data.size()), to(_gas), to(_gasPrice)); + client()->transact(toSecret(_secret), toU256(_amount), toAddress(_dest), toBytes(_data), toU256(_gas), toU256(_gasPrice)); } // extra bits needed to link on VS diff --git a/libqethereum/QEthereum.h b/libqethereum/QEthereum.h index 8355795cc..1de5aab59 100644 --- a/libqethereum/QEthereum.h +++ b/libqethereum/QEthereum.h @@ -159,6 +159,7 @@ private: Q_PROPERTY(bool mining READ isMining WRITE setMining) }; +#if 0 template T to(QVariant const& _s) { if (_s.type() != QVariant::String) return T(); auto s = _s.toString().toLatin1(); assert(s.size() == sizeof(T)); return *(T*)s.data(); } template QVariant toQJS(T const& _s) { QLatin1String ret((char*)&_s, sizeof(T)); assert(QVariant(QString(ret)).toString().toLatin1().size() == sizeof(T)); assert(*(T*)(QVariant(QString(ret)).toString().toLatin1().data()) == _s); return QVariant(QString(ret)); } @@ -282,33 +283,43 @@ public: return toQJS(ret); } }; +#endif eth::bytes toBytes(QString const& _s); QString padded(QString const& _s, unsigned _l, unsigned _r); +QString padded(QString const& _s, unsigned _l); +QString unpadded(QString _s); -template H toFixed(QString const& _s) +template eth::FixedHash toFixed(QString const& _s) { if (_s.startsWith("0x")) // Hex - return H(_s.mid(2)); + return eth::FixedHash(_s.mid(2).toStdString()); else if (!_s.contains(QRegExp("[^0-9]"))) // Decimal - return (typename H::Arith)(_s); + return (typename eth::FixedHash::Arith)(_s.toStdString()); else // Binary - return H(toBytes(_s)); + return eth::FixedHash(eth::asBytes(padded(_s, N).toStdString())); } -template I toInt(QString const& _s) +template boost::multiprecision::number> toInt(QString const& _s) { if (_s.startsWith("0x") || !_s.contains(QRegExp("[^0-9]"))) // Hex or Decimal - return (I)eth::bigint(_s); + return boost::multiprecision::number>(_s.toStdString()); else // Binary - return eth::fromBigEndian(toBytes(_s)); + return eth::fromBigEndian>>(eth::asBytes(padded(_s, N).toStdString())); } +inline eth::Address toAddress(QString const& _s) { return toFixed<20>(_s); } +inline eth::Secret toSecret(QString const& _s) { return toFixed<32>(_s); } +inline eth::u256 toU256(QString const& _s) { return toInt<32>(_s); } + +template QString toQJS(eth::FixedHash const& _h) { return QString::fromStdString("0x" + toHex(_h.ref())); } +template QString toQJS(boost::multiprecision::number> const& _n) { return QString::fromStdString("0x" + eth::toHex(eth::toCompactBigEndian(_n))); } + inline QString toBinary(QString const& _s) { return QString::fromStdString(eth::asString(toBytes(_s))); @@ -335,7 +346,9 @@ public: Q_INVOKABLE QString ethTest() const { return "Hello world!"; } Q_INVOKABLE QEthereum* self() { return this; } + Q_INVOKABLE QString pad(QString _s, unsigned _l) const { return padded(_s, _l); } Q_INVOKABLE QString pad(QString _s, unsigned _l, unsigned _r) const { return padded(_s, _l, _r); } + Q_INVOKABLE QString unpad(QString _s) const { return unpadded(_s); } Q_INVOKABLE QString toBinary(QString _s) const { return ::toBinary(_s); } Q_INVOKABLE QString fromBinary(QString _s) const { return ::fromBinary(_s); } @@ -351,7 +364,7 @@ public: bool isMining() const; QString/*eth::Address*/ coinbase() const; - QString/*eth::u256*/ gasPrice() const { return toString(10 * eth::szabo); } + QString/*eth::u256*/ gasPrice() const { return toQJS(10 * eth::szabo); } eth::u256 balanceAt(eth::Address _a) const; double txCountAt(eth::Address _a) const; @@ -380,7 +393,7 @@ private: Q_PROPERTY(bool mining READ isMining WRITE setMining) Q_PROPERTY(QString gasPrice READ gasPrice NOTIFY changed) Q_PROPERTY(QString key READ key NOTIFY changed) - Q_PROPERTY(QString keys READ keys NOTIFY changed) + Q_PROPERTY(QStringList keys READ keys NOTIFY changed) Q_PROPERTY(unsigned peerCount READ peerCount) eth::Client* m_client;