From 94df0ea77f01f31fa458a147a7be1badaffe9afe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Fri, 5 Dec 2014 16:27:35 +0100 Subject: [PATCH 01/40] Introducing VMFace - common VM interface --- libevm/VM.cpp | 42 ++++++++++++-------- libevm/VM.h | 36 +++++++---------- libevm/VMFace.h | 61 +++++++++++++++++++++++++++++ windows/LibEthereum.vcxproj | 3 +- windows/LibEthereum.vcxproj.filters | 11 +++--- 5 files changed, 107 insertions(+), 46 deletions(-) create mode 100644 libevm/VMFace.h diff --git a/libevm/VM.cpp b/libevm/VM.cpp index bded9a10d..996e51eea 100644 --- a/libevm/VM.cpp +++ b/libevm/VM.cpp @@ -1,33 +1,41 @@ /* - This file is part of cpp-ethereum. +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 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. +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 . +You should have received a copy of the GNU General Public License +along with cpp-ethereum. If not, see . */ /** @file VM.cpp - * @author Gav Wood - * @date 2014 - */ +* @author Gav Wood +* @date 2014 +*/ #include "VM.h" +#include -using namespace std; using namespace dev; using namespace dev::eth; -void VM::reset(u256 _gas) +void VM::reset(u256 _gas) noexcept { - m_gas = _gas; + VMFace::reset(_gas); m_curPC = 0; m_jumpDests.clear(); } + +bytesConstRef VM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _steps) +{ + if (auto defaultExt = dynamic_cast(&_ext)) + return goImpl(*defaultExt, _onOp, _steps); + else + return goImpl(_ext, _onOp, _steps); +} diff --git a/libevm/VM.h b/libevm/VM.h index 5fb46fc68..bb34bceb2 100644 --- a/libevm/VM.h +++ b/libevm/VM.h @@ -28,21 +28,13 @@ #include #include #include "FeeStructure.h" -#include "ExtVMFace.h" +#include "VMFace.h" namespace dev { namespace eth { -struct VMException: virtual Exception {}; -struct StepsDone: virtual VMException {}; -struct BreakPointHit: virtual VMException {}; -struct BadInstruction: virtual VMException {}; -struct BadJumpDestination: virtual VMException {}; -struct OutOfGas: virtual VMException {}; -struct StackTooSmall: virtual public VMException {}; - // Convert from a 256-bit integer stack/memory entry into a 160-bit Address hash. // Currently we just pull out the right (low-order in BE) 160-bits. inline Address asAddress(u256 _item) @@ -57,28 +49,28 @@ inline u256 fromAddress(Address _a) /** */ -class VM +class VM : public VMFace { public: /// Construct VM object. - explicit VM(u256 _gas = 0) { reset(_gas); } + explicit VM(u256 _gas = 0) : VMFace(_gas) {} - void reset(u256 _gas = 0); + virtual void reset(u256 _gas = 0) noexcept override final; - template - bytesConstRef go(Ext& _ext, OnOpFunc const& _onOp = OnOpFunc(), uint64_t _steps = (uint64_t)-1); + virtual bytesConstRef go(ExtVMFace& _ext, OnOpFunc const& _onOp = {}, uint64_t _steps = (uint64_t)-1) override final; void require(u256 _n) { if (m_stack.size() < _n) { if (m_onFail) m_onFail(); BOOST_THROW_EXCEPTION(StackTooSmall() << RequirementError((bigint)_n, (bigint)m_stack.size())); } } void requireMem(unsigned _n) { if (m_temp.size() < _n) { m_temp.resize(_n); } } - u256 gas() const { return m_gas; } u256 curPC() const { return m_curPC; } bytes const& memory() const { return m_temp; } u256s const& stack() const { return m_stack; } private: - u256 m_gas = 0; + template + bytesConstRef goImpl(Ext& _ext, OnOpFunc const& _onOp = OnOpFunc(), uint64_t _steps = (uint64_t)-1); + u256 m_curPC = 0; bytes m_temp; u256s m_stack; @@ -89,7 +81,7 @@ private: } // INLINE: -template dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc const& _onOp, uint64_t _steps) +template dev::bytesConstRef dev::eth::VM::goImpl(Ext& _ext, OnOpFunc const& _onOp, uint64_t _steps) { auto memNeed = [](dev::u256 _offset, dev::u256 _size) { return _size ? (bigint)_offset + _size : (bigint)0; }; @@ -164,7 +156,7 @@ template dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con case Instruction::SLOAD: require(1); - runGas = c_sloadGas; + runGas = c_sloadGas; break; // These all operate on memory and therefore potentially expand it: @@ -412,7 +404,7 @@ template dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con m_stack.pop_back(); break; case Instruction::SDIV: - m_stack[m_stack.size() - 2] = m_stack[m_stack.size() - 2] ? s2u(u2s(m_stack.back()) / u2s(m_stack[m_stack.size() - 2])) : 0; + m_stack[m_stack.size() - 2] = m_stack[m_stack.size() - 2] ? s2u(u2s(m_stack.back()) / u2s(m_stack[m_stack.size() - 2])) : 0; m_stack.pop_back(); break; case Instruction::MOD: @@ -420,7 +412,7 @@ template dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con m_stack.pop_back(); break; case Instruction::SMOD: - m_stack[m_stack.size() - 2] = m_stack[m_stack.size() - 2] ? s2u(u2s(m_stack.back()) % u2s(m_stack[m_stack.size() - 2])) : 0; + m_stack[m_stack.size() - 2] = m_stack[m_stack.size() - 2] ? s2u(u2s(m_stack.back()) % u2s(m_stack[m_stack.size() - 2])) : 0; m_stack.pop_back(); break; case Instruction::EXP: @@ -443,11 +435,11 @@ template dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con m_stack.pop_back(); break; case Instruction::SLT: - m_stack[m_stack.size() - 2] = u2s(m_stack.back()) < u2s(m_stack[m_stack.size() - 2]) ? 1 : 0; + m_stack[m_stack.size() - 2] = u2s(m_stack.back()) < u2s(m_stack[m_stack.size() - 2]) ? 1 : 0; m_stack.pop_back(); break; case Instruction::SGT: - m_stack[m_stack.size() - 2] = u2s(m_stack.back()) > u2s(m_stack[m_stack.size() - 2]) ? 1 : 0; + m_stack[m_stack.size() - 2] = u2s(m_stack.back()) > u2s(m_stack[m_stack.size() - 2]) ? 1 : 0; m_stack.pop_back(); break; case Instruction::EQ: diff --git a/libevm/VMFace.h b/libevm/VMFace.h new file mode 100644 index 000000000..f6db7d0b5 --- /dev/null +++ b/libevm/VMFace.h @@ -0,0 +1,61 @@ +/* + 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 . +*/ + +#pragma once + +#include +#include +#include "ExtVMFace.h" + +namespace dev +{ +namespace eth +{ + +struct VMException : virtual Exception {}; +struct StepsDone : virtual VMException {}; +struct BreakPointHit : virtual VMException {}; +struct BadInstruction : virtual VMException {}; +struct BadJumpDestination : virtual VMException {}; +struct OutOfGas : virtual VMException {}; +struct StackTooSmall : virtual public VMException {}; + +/** + */ +class VMFace +{ +public: + /// Construct VM object. + explicit VMFace(u256 _gas = 0): m_gas(_gas) {} + + virtual ~VMFace() = default; + + VMFace(VMFace const&) = delete; + void operator=(VMFace const&) = delete; + + virtual void reset(u256 _gas = 0) noexcept { m_gas = _gas; } + + virtual bytesConstRef go(ExtVMFace& _ext, OnOpFunc const& _onOp = {}, uint64_t _steps = (uint64_t)-1) = 0; + + u256 gas() const { return m_gas; } + +protected: + u256 m_gas = 0; +}; + +} +} diff --git a/windows/LibEthereum.vcxproj b/windows/LibEthereum.vcxproj index 06f868023..cbacd51b5 100644 --- a/windows/LibEthereum.vcxproj +++ b/windows/LibEthereum.vcxproj @@ -368,6 +368,7 @@ true true + @@ -568,4 +569,4 @@ - + \ No newline at end of file diff --git a/windows/LibEthereum.vcxproj.filters b/windows/LibEthereum.vcxproj.filters index 114364008..76201d823 100644 --- a/windows/LibEthereum.vcxproj.filters +++ b/windows/LibEthereum.vcxproj.filters @@ -190,18 +190,14 @@ libdevcrypto - - libdevcrypto - - - libdevcrypto - libethereum libevmcore + + @@ -438,6 +434,9 @@ libevmcore + + libevm + From 160aa47288a2dfb71706ef91eb5cefd72448e7db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Fri, 5 Dec 2014 17:00:29 +0100 Subject: [PATCH 02/40] Use safe pointers in Executive --- libethereum/Executive.cpp | 15 ++++----------- libethereum/Executive.h | 17 +++++++++-------- libethereum/State.cpp | 1 + libethereum/State.h | 1 - test/vm.cpp | 1 + 5 files changed, 15 insertions(+), 20 deletions(-) diff --git a/libethereum/Executive.cpp b/libethereum/Executive.cpp index 6a123875c..db77d349a 100644 --- a/libethereum/Executive.cpp +++ b/libethereum/Executive.cpp @@ -32,13 +32,6 @@ using namespace dev::eth; #define ETH_VMTRACE 1 -Executive::~Executive() -{ - // TODO: Make safe. - delete m_ext; - delete m_vm; -} - u256 Executive::gasUsed() const { return m_t.gas() - m_endGas; @@ -112,9 +105,9 @@ bool Executive::call(Address _receiveAddress, Address _senderAddress, u256 _valu if (m_s.addressHasCode(_receiveAddress)) { - m_vm = new VM(_gas); + m_vm.reset(new VM(_gas)); bytes const& c = m_s.code(_receiveAddress); - m_ext = new ExtVM(m_s, _receiveAddress, _senderAddress, _originAddress, _value, _gasPrice, _data, &c, m_ms); + m_ext.reset(new ExtVM(m_s, _receiveAddress, _senderAddress, _originAddress, _value, _gasPrice, _data, &c, m_ms)); } else m_endGas = _gas; @@ -131,8 +124,8 @@ bool Executive::create(Address _sender, u256 _endowment, u256 _gasPrice, u256 _g m_s.m_cache[m_newAddress] = Account(m_s.balance(m_newAddress) + _endowment, Account::ContractConception); // Execute _init. - m_vm = new VM(_gas); - m_ext = new ExtVM(m_s, m_newAddress, _sender, _origin, _endowment, _gasPrice, bytesConstRef(), _init, m_ms); + m_vm.reset(new VM(_gas)); + m_ext.reset(new ExtVM(m_s, m_newAddress, _sender, _origin, _endowment, _gasPrice, bytesConstRef(), _init, m_ms)); return _init.empty(); } diff --git a/libethereum/Executive.h b/libethereum/Executive.h index 930c2859b..f2ee6a77d 100644 --- a/libethereum/Executive.h +++ b/libethereum/Executive.h @@ -25,18 +25,17 @@ #include #include #include -#include +#include #include "Transaction.h" -#include "Manifest.h" +#include "ExtVM.h" namespace dev { namespace eth { -class VM; -class ExtVM; class State; +struct Manifest; struct VMTraceChannel: public LogChannel { static const char* name() { return "EVM"; } static const int verbosity = 11; }; @@ -44,7 +43,9 @@ class Executive { public: Executive(State& _s, Manifest* o_ms = nullptr): m_s(_s), m_ms(o_ms) {} - ~Executive(); + ~Executive() = default; + Executive(Executive const&) = delete; + void operator=(Executive) = delete; bool setup(bytesConstRef _transaction); bool create(Address _txSender, u256 _endowment, u256 _gasPrice, u256 _gas, bytesConstRef _code, Address _originAddress); @@ -63,14 +64,14 @@ public: h160 newAddress() const { return m_newAddress; } LogEntries const& logs() const { return m_logs; } - VM const& vm() const { return *m_vm; } + VMFace const& vm() const { return *m_vm; } State const& state() const { return m_s; } ExtVM const& ext() const { return *m_ext; } private: State& m_s; - ExtVM* m_ext = nullptr; // TODO: make safe. - VM* m_vm = nullptr; + std::unique_ptr m_ext; + std::unique_ptr m_vm; Manifest* m_ms = nullptr; bytesConstRef m_out; Address m_newAddress; diff --git a/libethereum/State.cpp b/libethereum/State.cpp index 9a0426e18..335b7ff04 100644 --- a/libethereum/State.cpp +++ b/libethereum/State.cpp @@ -33,6 +33,7 @@ #include "BlockChain.h" #include "Defaults.h" #include "ExtVM.h" +#include "Executive.h" using namespace std; using namespace dev; using namespace dev::eth; diff --git a/libethereum/State.h b/libethereum/State.h index 9c41843ff..5fcfa1cf7 100644 --- a/libethereum/State.h +++ b/libethereum/State.h @@ -36,7 +36,6 @@ #include "Account.h" #include "Transaction.h" #include "TransactionReceipt.h" -#include "Executive.h" #include "AccountDiff.h" namespace dev diff --git a/test/vm.cpp b/test/vm.cpp index d7bc0612a..67102dcd2 100644 --- a/test/vm.cpp +++ b/test/vm.cpp @@ -21,6 +21,7 @@ */ #include +#include #include "vm.h" using namespace std; From f97826d07147ccab522220e84f5259867cfc8e87 Mon Sep 17 00:00:00 2001 From: CJentzsch Date: Thu, 4 Dec 2014 17:55:04 +0100 Subject: [PATCH 03/40] fix stackoverflow in calldataload, codecopy, extcodecopy + some tests --- libevm/VM.h | 40 +++---- test/stPreCompiledContractsFiller.json | 35 ++++++ test/vm.cpp | 20 ++++ test/vmEnvironmentalInfoTestFiller.json | 140 ++++++++++++++++++++++++ 4 files changed, 215 insertions(+), 20 deletions(-) diff --git a/libevm/VM.h b/libevm/VM.h index 5fb46fc68..0e27db02d 100644 --- a/libevm/VM.h +++ b/libevm/VM.h @@ -524,12 +524,12 @@ template dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con break; case Instruction::CALLDATALOAD: { - if ((unsigned)m_stack.back() + 31 < _ext.data.size()) + if ((unsigned)m_stack.back() + (uint64_t)31 < _ext.data.size()) m_stack.back() = (u256)*(h256 const*)(_ext.data.data() + (unsigned)m_stack.back()); else { h256 r; - for (unsigned i = (unsigned)m_stack.back(), e = (unsigned)m_stack.back() + 32, j = 0; i < e; ++i, ++j) + for (uint64_t i = (unsigned)m_stack.back(), e = (unsigned)m_stack.back() + 32, j = 0; i < e; ++i, ++j) r[j] = i < _ext.data.size() ? _ext.data[i] : 0; m_stack.back() = (u256)r; } @@ -540,15 +540,15 @@ template dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con break; case Instruction::CALLDATACOPY: { - unsigned mf = (unsigned)m_stack.back(); + unsigned offset = (unsigned)m_stack.back(); m_stack.pop_back(); - u256 cf = m_stack.back(); + u256 dataIndex = m_stack.back(); m_stack.pop_back(); - unsigned l = (unsigned)m_stack.back(); + unsigned size = (unsigned)m_stack.back(); m_stack.pop_back(); - unsigned el = cf + l > (u256)_ext.data.size() ? (u256)_ext.data.size() < cf ? 0 : _ext.data.size() - (unsigned)cf : l; - memcpy(m_temp.data() + mf, _ext.data.data() + (unsigned)cf, el); - memset(m_temp.data() + mf + el, 0, l - el); + unsigned el = dataIndex + (bigint)size > (u256)_ext.data.size() ? (u256)_ext.data.size() < dataIndex ? 0 : _ext.data.size() - (unsigned)dataIndex : size; + memcpy(m_temp.data() + offset, _ext.data.data() + (unsigned)dataIndex, el); + memset(m_temp.data() + offset + el, 0, size - el); break; } case Instruction::CODESIZE: @@ -556,15 +556,15 @@ template dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con break; case Instruction::CODECOPY: { - unsigned mf = (unsigned)m_stack.back(); + unsigned offset = (unsigned)m_stack.back(); m_stack.pop_back(); - u256 cf = (u256)m_stack.back(); + u256 dataIndex = (u256)m_stack.back(); m_stack.pop_back(); - unsigned l = (unsigned)m_stack.back(); + unsigned size = (unsigned)m_stack.back(); m_stack.pop_back(); - unsigned el = cf + l > (u256)_ext.code.size() ? (u256)_ext.code.size() < cf ? 0 : _ext.code.size() - (unsigned)cf : l; - memcpy(m_temp.data() + mf, _ext.code.data() + (unsigned)cf, el); - memset(m_temp.data() + mf + el, 0, l - el); + unsigned el = dataIndex + (bigint)size > (u256)_ext.code.size() ? (u256)_ext.code.size() < dataIndex ? 0 : _ext.code.size() - (unsigned)dataIndex : size; + memcpy(m_temp.data() + offset, _ext.code.data() + (unsigned)dataIndex, el); + memset(m_temp.data() + offset + el, 0, size - el); break; } case Instruction::EXTCODESIZE: @@ -574,15 +574,15 @@ template dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con { Address a = asAddress(m_stack.back()); m_stack.pop_back(); - unsigned mf = (unsigned)m_stack.back(); + unsigned offset = (unsigned)m_stack.back(); m_stack.pop_back(); - u256 cf = m_stack.back(); + u256 dataIndex = m_stack.back(); m_stack.pop_back(); - unsigned l = (unsigned)m_stack.back(); + unsigned size = (unsigned)m_stack.back(); m_stack.pop_back(); - unsigned el = cf + l > (u256)_ext.codeAt(a).size() ? (u256)_ext.codeAt(a).size() < cf ? 0 : _ext.codeAt(a).size() - (unsigned)cf : l; - memcpy(m_temp.data() + mf, _ext.codeAt(a).data() + (unsigned)cf, el); - memset(m_temp.data() + mf + el, 0, l - el); + unsigned el = dataIndex + (bigint)size > (u256)_ext.codeAt(a).size() ? (u256)_ext.codeAt(a).size() < dataIndex ? 0 : _ext.codeAt(a).size() - (unsigned)dataIndex : size; + memcpy(m_temp.data() + offset, _ext.codeAt(a).data() + (unsigned)dataIndex, el); + memset(m_temp.data() + offset + el, 0, size - el); break; } case Instruction::GASPRICE: diff --git a/test/stPreCompiledContractsFiller.json b/test/stPreCompiledContractsFiller.json index 9c65ad37b..62a3a1623 100644 --- a/test/stPreCompiledContractsFiller.json +++ b/test/stPreCompiledContractsFiller.json @@ -33,6 +33,41 @@ } }, + "CallEcrecover0_overlappingInputOutput": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "10000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { + "balance" : "20000000", + "nonce" : 0, + "code": "{ (MSTORE 0 0x18c547e4f7b0f325ad1e56f57e26c745b09a3e503d86e00e5255ff7f715d3d1c) (MSTORE 32 28) (MSTORE 64 0x73b1693892219d736caba55bdb67216e485557ea6b6af75f37096c9aa6a5a75f) (MSTORE 96 0xeeb940b1d03b21e36b0e47e79769f095fe2ab855bd91e3a38756b7d75a9c4549) [[ 2 ]] (CALL 1000 1 0 0 128 64 32) [[ 0 ]] (MOD (MLOAD 64) (EXP 2 160)) [[ 1 ]] (EQ (ORIGIN) (SLOAD 0)) }", + "storage": {} + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "", + "storage": {} + } + }, + "transaction" : { + "nonce" : "0", + "gasPrice" : "1", + "gasLimit" : "365224", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "100000", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "data" : "" + } + }, + + "CallEcrecover0_completeReturnValue": { "env" : { "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", diff --git a/test/vm.cpp b/test/vm.cpp index d7bc0612a..e674d6de3 100644 --- a/test/vm.cpp +++ b/test/vm.cpp @@ -488,6 +488,26 @@ BOOST_AUTO_TEST_CASE(vmLogTest) dev::test::executeTests("vmLogTest", "/VMTests", dev::test::doVMTests); } +BOOST_AUTO_TEST_CASE(vmPerformanceTest) +{ + for (int i = 1; i < boost::unit_test::framework::master_test_suite().argc; ++i) + { + string arg = boost::unit_test::framework::master_test_suite().argv[i]; + if (arg == "--performance") + dev::test::executeTests("vmPerformanceTest", "/VMTests", dev::test::doVMTests); + } +} + +BOOST_AUTO_TEST_CASE(vmArithPerformanceTest) +{ + for (int i = 1; i < boost::unit_test::framework::master_test_suite().argc; ++i) + { + string arg = boost::unit_test::framework::master_test_suite().argv[i]; + if (arg == "--performance") + dev::test::executeTests("vmArithPerformanceTest", "/VMTests", dev::test::doVMTests); + } +} + BOOST_AUTO_TEST_CASE(vmRandom) { string testPath = getTestPath(); diff --git a/test/vmEnvironmentalInfoTestFiller.json b/test/vmEnvironmentalInfoTestFiller.json index 95e7936aa..abeed9945 100644 --- a/test/vmEnvironmentalInfoTestFiller.json +++ b/test/vmEnvironmentalInfoTestFiller.json @@ -338,6 +338,33 @@ } }, + "calldataloadSizeTooHigh": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (CALLDATALOAD 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa)}", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "0x123456789abcdef0000000000000000000000000000000000000000000000000024", + "gasPrice" : "1000000000", + "gas" : "100000000000" + } + }, "calldatasize0": { "env" : { @@ -451,6 +478,62 @@ } }, + "calldatacopy_DataIndexTooHigh": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ (CALLDATACOPY 0 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa 0xff ) [[ 0 ]] @0}", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "0x1234567890abcdef01234567890abcdef", + "gasPrice" : "1000000000", + "gas" : "100000000000" + } + }, + + "calldatacopy_DataIndexTooHigh2": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ (CALLDATACOPY 0 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa 9 ) [[ 0 ]] @0}", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "0x1234567890abcdef01234567890abcdef", + "gasPrice" : "1000000000", + "gas" : "100000000000" + } + }, + "calldatacopy1": { "env" : { "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", @@ -535,6 +618,34 @@ } }, + "codecopy_DataIndexTooHigh": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ (CODECOPY 0 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa 8 ) [[ 0 ]] @0}", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "0x1234567890abcdef01234567890abcdef", + "gasPrice" : "1000000000", + "gas" : "100000000000" + } + }, + "codecopy0": { "env" : { "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", @@ -686,6 +797,35 @@ } }, + "extcodecopy_DataIndexTooHigh": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ (EXTCODECOPY (ADDRESS) 0 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa 8 ) [[ 0 ]] @0}", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "0x1234567890abcdef01234567890abcdef", + "gasPrice" : "1000000000", + "gas" : "100000000000" + } + }, + + "extcodecopy0": { "env" : { "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", From 80f69b7383b24c4a9a27110b4f6cd4dcd0e311ea Mon Sep 17 00:00:00 2001 From: CJentzsch Date: Thu, 4 Dec 2014 18:17:03 +0100 Subject: [PATCH 04/40] avoid code repetition in vm --- libevm/VM.h | 58 +++++++++++++++++++++++++---------------------------- 1 file changed, 27 insertions(+), 31 deletions(-) diff --git a/libevm/VM.h b/libevm/VM.h index 0e27db02d..d846ff079 100644 --- a/libevm/VM.h +++ b/libevm/VM.h @@ -538,50 +538,46 @@ template dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con case Instruction::CALLDATASIZE: m_stack.push_back(_ext.data.size()); break; - case Instruction::CALLDATACOPY: - { - unsigned offset = (unsigned)m_stack.back(); - m_stack.pop_back(); - u256 dataIndex = m_stack.back(); - m_stack.pop_back(); - unsigned size = (unsigned)m_stack.back(); - m_stack.pop_back(); - unsigned el = dataIndex + (bigint)size > (u256)_ext.data.size() ? (u256)_ext.data.size() < dataIndex ? 0 : _ext.data.size() - (unsigned)dataIndex : size; - memcpy(m_temp.data() + offset, _ext.data.data() + (unsigned)dataIndex, el); - memset(m_temp.data() + offset + el, 0, size - el); - break; - } case Instruction::CODESIZE: m_stack.push_back(_ext.code.size()); break; - case Instruction::CODECOPY: - { - unsigned offset = (unsigned)m_stack.back(); - m_stack.pop_back(); - u256 dataIndex = (u256)m_stack.back(); - m_stack.pop_back(); - unsigned size = (unsigned)m_stack.back(); - m_stack.pop_back(); - unsigned el = dataIndex + (bigint)size > (u256)_ext.code.size() ? (u256)_ext.code.size() < dataIndex ? 0 : _ext.code.size() - (unsigned)dataIndex : size; - memcpy(m_temp.data() + offset, _ext.code.data() + (unsigned)dataIndex, el); - memset(m_temp.data() + offset + el, 0, size - el); - break; - } case Instruction::EXTCODESIZE: m_stack.back() = _ext.codeAt(asAddress(m_stack.back())).size(); break; + case Instruction::CALLDATACOPY: + case Instruction::CODECOPY: case Instruction::EXTCODECOPY: { - Address a = asAddress(m_stack.back()); - m_stack.pop_back(); + Address a; + if (inst == Instruction::EXTCODECOPY) + { + a = asAddress(m_stack.back()); + m_stack.pop_back(); + } unsigned offset = (unsigned)m_stack.back(); m_stack.pop_back(); - u256 dataIndex = m_stack.back(); + u256 index = m_stack.back(); m_stack.pop_back(); unsigned size = (unsigned)m_stack.back(); m_stack.pop_back(); - unsigned el = dataIndex + (bigint)size > (u256)_ext.codeAt(a).size() ? (u256)_ext.codeAt(a).size() < dataIndex ? 0 : _ext.codeAt(a).size() - (unsigned)dataIndex : size; - memcpy(m_temp.data() + offset, _ext.codeAt(a).data() + (unsigned)dataIndex, el); + unsigned el; + switch(inst) + { + case Instruction::CALLDATACOPY: + el = index + (bigint)size > (u256)_ext.data.size() ? (u256)_ext.data.size() < index ? 0 : _ext.data.size() - (unsigned)index : size; + memcpy(m_temp.data() + offset, _ext.data.data() + (unsigned)index, el); + break; + case Instruction::CODECOPY: + el = index + (bigint)size > (u256)_ext.code.size() ? (u256)_ext.code.size() < index ? 0 : _ext.code.size() - (unsigned)index : size; + memcpy(m_temp.data() + offset, _ext.code.data() + (unsigned)index, el); + break; + case Instruction::EXTCODECOPY: + el = index + (bigint)size > (u256)_ext.codeAt(a).size() ? (u256)_ext.codeAt(a).size() < index ? 0 : _ext.codeAt(a).size() - (unsigned)index : size; + memcpy(m_temp.data() + offset, _ext.codeAt(a).data() + (unsigned)index, el); + break; + default: + break; + } memset(m_temp.data() + offset + el, 0, size - el); break; } From 2d4d9c7485463d8d65e8a0898a35558d2a871221 Mon Sep 17 00:00:00 2001 From: CJentzsch Date: Thu, 4 Dec 2014 19:23:48 +0100 Subject: [PATCH 05/40] minor fix --- libevm/VM.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libevm/VM.h b/libevm/VM.h index d846ff079..f5feead40 100644 --- a/libevm/VM.h +++ b/libevm/VM.h @@ -529,7 +529,7 @@ template dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con else { h256 r; - for (uint64_t i = (unsigned)m_stack.back(), e = (unsigned)m_stack.back() + 32, j = 0; i < e; ++i, ++j) + for (uint64_t i = (unsigned)m_stack.back(), e = (unsigned)m_stack.back() + (uint64_t)32, j = 0; i < e; ++i, ++j) r[j] = i < _ext.data.size() ? _ext.data[i] : 0; m_stack.back() = (u256)r; } From 86794daded9bd911883fcf190eb100f1331f6290 Mon Sep 17 00:00:00 2001 From: CJentzsch Date: Fri, 5 Dec 2014 11:34:30 +0100 Subject: [PATCH 06/40] even less code --- libevm/VM.h | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/libevm/VM.h b/libevm/VM.h index f5feead40..be64c0ad1 100644 --- a/libevm/VM.h +++ b/libevm/VM.h @@ -560,24 +560,23 @@ template dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con m_stack.pop_back(); unsigned size = (unsigned)m_stack.back(); m_stack.pop_back(); - unsigned el; + bytes toBeCopied; switch(inst) { case Instruction::CALLDATACOPY: - el = index + (bigint)size > (u256)_ext.data.size() ? (u256)_ext.data.size() < index ? 0 : _ext.data.size() - (unsigned)index : size; - memcpy(m_temp.data() + offset, _ext.data.data() + (unsigned)index, el); + toBeCopied = _ext.data.toBytes(); break; case Instruction::CODECOPY: - el = index + (bigint)size > (u256)_ext.code.size() ? (u256)_ext.code.size() < index ? 0 : _ext.code.size() - (unsigned)index : size; - memcpy(m_temp.data() + offset, _ext.code.data() + (unsigned)index, el); + toBeCopied = _ext.code; break; case Instruction::EXTCODECOPY: - el = index + (bigint)size > (u256)_ext.codeAt(a).size() ? (u256)_ext.codeAt(a).size() < index ? 0 : _ext.codeAt(a).size() - (unsigned)index : size; - memcpy(m_temp.data() + offset, _ext.codeAt(a).data() + (unsigned)index, el); + toBeCopied = _ext.codeAt(a); break; default: break; } + unsigned el = index + (bigint)size > (u256)toBeCopied.size() ? (u256)toBeCopied.size() < index ? 0 : toBeCopied.size() - (unsigned)index : size; + memcpy(m_temp.data() + offset, toBeCopied.data() + (unsigned)index, el); memset(m_temp.data() + offset + el, 0, size - el); break; } From debc585fc553b4ffbd40fcf775721282380d9679 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Tue, 9 Dec 2014 14:11:45 +0100 Subject: [PATCH 07/40] Revert unnecessary whitespace changes --- libevm/VM.cpp | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/libevm/VM.cpp b/libevm/VM.cpp index 996e51eea..44620a3d5 100644 --- a/libevm/VM.cpp +++ b/libevm/VM.cpp @@ -1,23 +1,23 @@ /* -This file is part of cpp-ethereum. + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . */ /** @file VM.cpp -* @author Gav Wood -* @date 2014 -*/ + * @author Gav Wood + * @date 2014 + */ #include "VM.h" #include From 4261a7159d0c130ea976742fe616d601c43f3b42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Tue, 9 Dec 2014 15:28:31 +0100 Subject: [PATCH 08/40] Fix alethzero build --- alethzero/MainWin.h | 1 + 1 file changed, 1 insertion(+) diff --git a/alethzero/MainWin.h b/alethzero/MainWin.h index 50b9df413..fffc5843f 100644 --- a/alethzero/MainWin.h +++ b/alethzero/MainWin.h @@ -33,6 +33,7 @@ #include #include #include +#include #include #include From 781d58d705ab02dcf934c88789b0e02042fc062e Mon Sep 17 00:00:00 2001 From: Christian Date: Tue, 9 Dec 2014 18:46:18 +0100 Subject: [PATCH 09/40] String types. --- libsolidity/AST.cpp | 2 +- libsolidity/Compiler.cpp | 6 +++- libsolidity/ExpressionCompiler.cpp | 22 +++++++++--- libsolidity/ExpressionCompiler.h | 3 +- libsolidity/Token.h | 32 +++++++++++++++++ libsolidity/Types.cpp | 43 ++++++++++++++++++++++- libsolidity/Types.h | 31 ++++++++++++++++- test/solidityEndToEndTest.cpp | 56 ++++++++++++++++++++++++++++++ 8 files changed, 186 insertions(+), 9 deletions(-) diff --git a/libsolidity/AST.cpp b/libsolidity/AST.cpp index 697ffe8e3..97b39eed0 100644 --- a/libsolidity/AST.cpp +++ b/libsolidity/AST.cpp @@ -582,7 +582,7 @@ void Literal::checkTypeRequirements() { m_type = Type::forLiteral(*this); if (!m_type) - BOOST_THROW_EXCEPTION(createTypeError("Literal value too large.")); + BOOST_THROW_EXCEPTION(createTypeError("Literal value too large or too small.")); } } diff --git a/libsolidity/Compiler.cpp b/libsolidity/Compiler.cpp index a8a074034..781f36800 100644 --- a/libsolidity/Compiler.cpp +++ b/libsolidity/Compiler.cpp @@ -142,6 +142,9 @@ unsigned Compiler::appendCalldataUnpacker(FunctionDefinition const& _function, b << errinfo_comment("Type " + var->getType()->toString() + " not yet supported.")); if (numBytes == 32) m_context << u256(dataOffset) << load; + else if (var->getType()->getCategory() == Type::Category::STRING) + m_context << (u256(1) << ((32 - numBytes) * 8)) << eth::Instruction::DUP1 + << u256(dataOffset) << load << eth::Instruction::DIV << eth::Instruction::MUL; else m_context << (u256(1) << ((32 - numBytes) * 8)) << u256(dataOffset) << load << eth::Instruction::DIV; @@ -166,7 +169,8 @@ void Compiler::appendReturnValuePacker(FunctionDefinition const& _function) << errinfo_comment("Type " + paramType.toString() + " not yet supported.")); CompilerUtils(m_context).copyToStackTop(stackDepth, paramType); if (numBytes != 32) - m_context << (u256(1) << ((32 - numBytes) * 8)) << eth::Instruction::MUL; + if (paramType.getCategory() != Type::Category::STRING) + m_context << (u256(1) << ((32 - numBytes) * 8)) << eth::Instruction::MUL; m_context << u256(dataOffset) << eth::Instruction::MSTORE; stackDepth -= paramType.getSizeOnStack(); dataOffset += numBytes; diff --git a/libsolidity/ExpressionCompiler.cpp b/libsolidity/ExpressionCompiler.cpp index f1086c143..5e688f6bd 100644 --- a/libsolidity/ExpressionCompiler.cpp +++ b/libsolidity/ExpressionCompiler.cpp @@ -226,7 +226,8 @@ bool ExpressionCompiler::visit(FunctionCall& _functionCall) << errinfo_sourceLocation(arguments[i]->getLocation()) << errinfo_comment("Type " + type.toString() + " not yet supported.")); if (numBytes != 32) - m_context << (u256(1) << ((32 - numBytes) * 8)) << eth::Instruction::MUL; + if (type.getCategory() != Type::Category::STRING) + m_context << (u256(1) << ((32 - numBytes) * 8)) << eth::Instruction::MUL; m_context << u256(dataOffset) << eth::Instruction::MSTORE; dataOffset += numBytes; } @@ -243,8 +244,15 @@ bool ExpressionCompiler::visit(FunctionCall& _functionCall) if (retSize == 32) m_context << u256(0) << eth::Instruction::MLOAD; else if (retSize > 0) - m_context << (u256(1) << ((32 - retSize) * 8)) - << u256(0) << eth::Instruction::MLOAD << eth::Instruction::DIV; + { + if (function.getReturnParameterTypes().front()->getCategory() == Type::Category::STRING) + m_context << (u256(1) << ((32 - retSize) * 8)) << eth::Instruction::DUP1 + << u256(0) << eth::Instruction::MLOAD + << eth::Instruction::DIV << eth::Instruction::MUL; + else + m_context << (u256(1) << ((32 - retSize) * 8)) + << u256(0) << eth::Instruction::MLOAD << eth::Instruction::DIV; + } break; } case Location::SEND: @@ -411,10 +419,11 @@ void ExpressionCompiler::endVisit(Literal& _literal) { case Type::Category::INTEGER: case Type::Category::BOOL: + case Type::Category::STRING: m_context << _literal.getType()->literalValue(_literal); break; default: - BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Only integer and boolean literals implemented for now.")); + BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Only integer, boolean and string literals implemented for now.")); } } @@ -550,6 +559,11 @@ void ExpressionCompiler::appendTypeConversion(Type const& _typeOnStack, Type con return; if (_typeOnStack.getCategory() == Type::Category::INTEGER) appendHighBitsCleanup(dynamic_cast(_typeOnStack)); + else if (_typeOnStack.getCategory() == Type::Category::STRING) + { + // nothing to do, strings are high-order-bit-aligned + //@todo clear lower-order bytes if we allow explicit conversion to shorter strings + } else if (_typeOnStack != _targetType) // All other types should not be convertible to non-equal types. BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Invalid type conversion requested.")); diff --git a/libsolidity/ExpressionCompiler.h b/libsolidity/ExpressionCompiler.h index 966be30e2..386a71165 100644 --- a/libsolidity/ExpressionCompiler.h +++ b/libsolidity/ExpressionCompiler.h @@ -35,6 +35,7 @@ namespace solidity { class CompilerContext; class Type; class IntegerType; +class StaticStringType; /** * Compiler for expressions, i.e. converts an AST tree whose root is an Expression into a stream @@ -75,7 +76,7 @@ private: /// @} /// Appends an implicit or explicit type conversion. For now this comprises only erasing - /// higher-order bits (@see appendHighBitCleanup) when widening integer types. + /// higher-order bits (@see appendHighBitCleanup) when widening integer. /// If @a _cleanupNeeded, high order bits cleanup is also done if no type conversion would be /// necessary. void appendTypeConversion(Type const& _typeOnStack, Type const& _targetType, bool _cleanupNeeded = false); diff --git a/libsolidity/Token.h b/libsolidity/Token.h index f1a94af35..f9ded3114 100644 --- a/libsolidity/Token.h +++ b/libsolidity/Token.h @@ -269,6 +269,38 @@ namespace solidity K(ADDRESS, "address", 0) \ K(BOOL, "bool", 0) \ K(STRING_TYPE, "string", 0) \ + K(STRING1, "string1", 0) \ + K(STRING2, "string2", 0) \ + K(STRING3, "string3", 0) \ + K(STRING4, "string4", 0) \ + K(STRING5, "string5", 0) \ + K(STRING6, "string6", 0) \ + K(STRING7, "string7", 0) \ + K(STRING8, "string8", 0) \ + K(STRING9, "string9", 0) \ + K(STRING10, "string10", 0) \ + K(STRING11, "string11", 0) \ + K(STRING12, "string12", 0) \ + K(STRING13, "string13", 0) \ + K(STRING14, "string14", 0) \ + K(STRING15, "string15", 0) \ + K(STRING16, "string16", 0) \ + K(STRING17, "string17", 0) \ + K(STRING18, "string18", 0) \ + K(STRING19, "string19", 0) \ + K(STRING20, "string20", 0) \ + K(STRING21, "string21", 0) \ + K(STRING22, "string22", 0) \ + K(STRING23, "string23", 0) \ + K(STRING24, "string24", 0) \ + K(STRING25, "string25", 0) \ + K(STRING26, "string26", 0) \ + K(STRING27, "string27", 0) \ + K(STRING28, "string28", 0) \ + K(STRING29, "string29", 0) \ + K(STRING30, "string30", 0) \ + K(STRING31, "string31", 0) \ + K(STRING32, "string32", 0) \ K(TEXT, "text", 0) \ K(REAL, "real", 0) \ K(UREAL, "ureal", 0) \ diff --git a/libsolidity/Types.cpp b/libsolidity/Types.cpp index 3829016f5..f7446dea9 100644 --- a/libsolidity/Types.cpp +++ b/libsolidity/Types.cpp @@ -53,6 +53,8 @@ shared_ptr Type::fromElementaryTypeName(Token::Value _typeToken) return make_shared(0, IntegerType::Modifier::ADDRESS); else if (_typeToken == Token::BOOL) return make_shared(); + else if (Token::STRING1 <= _typeToken && _typeToken <= Token::STRING32) + return make_shared(int(_typeToken) - int(Token::STRING1) + 1); else BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unable to convert elementary typename " + std::string(Token::toString(_typeToken)) + " to type.")); @@ -91,7 +93,8 @@ shared_ptr Type::forLiteral(Literal const& _literal) case Token::NUMBER: return IntegerType::smallestTypeForLiteral(_literal.getValue()); case Token::STRING_LITERAL: - return shared_ptr(); // @todo add string literals + //@todo put larger strings into dynamic strings + return StaticStringType::smallestTypeForLiteral(_literal.getValue()); default: return shared_ptr(); } @@ -194,6 +197,44 @@ const MemberList IntegerType::AddressMemberList = {"send", make_shared(TypePointers({make_shared(256)}), TypePointers(), FunctionType::Location::SEND)}}); +shared_ptr StaticStringType::smallestTypeForLiteral(string const& _literal) +{ + if (0 < _literal.length() && _literal.length() <= 32) + return make_shared(_literal.length()); + return shared_ptr(); +} + +StaticStringType::StaticStringType(int _bytes): m_bytes(_bytes) +{ + if (asserts(m_bytes > 0 && m_bytes <= 32)) + BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Invalid byte number for static string type: " + + dev::toString(m_bytes))); +} + +bool StaticStringType::isImplicitlyConvertibleTo(Type const& _convertTo) const +{ + if (_convertTo.getCategory() != getCategory()) + return false; + StaticStringType const& convertTo = dynamic_cast(_convertTo); + return convertTo.m_bytes >= m_bytes; +} + +bool StaticStringType::operator==(Type const& _other) const +{ + if (_other.getCategory() != getCategory()) + return false; + StaticStringType const& other = dynamic_cast(_other); + return other.m_bytes == m_bytes; +} + +u256 StaticStringType::literalValue(const Literal& _literal) const +{ + u256 value = 0; + for (char c: _literal.getValue()) + value = (value << 8) | byte(c); + return value << ((32 - _literal.getValue().length()) * 8); +} + bool BoolType::isExplicitlyConvertibleTo(Type const& _convertTo) const { // conversion to integer is fine, but not to address diff --git a/libsolidity/Types.h b/libsolidity/Types.h index 8e2f4803b..c96d0bb1f 100644 --- a/libsolidity/Types.h +++ b/libsolidity/Types.h @@ -36,7 +36,7 @@ namespace dev namespace solidity { -// @todo realMxN, string +// @todo realMxN, dynamic strings, text, arrays class Type; // forward using TypePointer = std::shared_ptr; @@ -178,6 +178,35 @@ private: static const MemberList AddressMemberList; }; +/** + * String type with fixed length, up to 32 bytes. + */ +class StaticStringType: public Type +{ +public: + virtual Category getCategory() const override { return Category::STRING; } + + /// @returns the smallest string type for the given literal or an empty pointer + /// if no type fits. + static std::shared_ptr smallestTypeForLiteral(std::string const& _literal); + + StaticStringType(int _bytes); + + virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override; + virtual bool operator==(Type const& _other) const override; + + virtual unsigned getCalldataEncodedSize() const override { return m_bytes; } + virtual bool isValueType() const override { return true; } + + virtual std::string toString() const override { return "string" + dev::toString(m_bytes); } + virtual u256 literalValue(Literal const& _literal) const override; + + int getNumBytes() const { return m_bytes; } + +private: + int m_bytes; +}; + /** * The boolean type. */ diff --git a/test/solidityEndToEndTest.cpp b/test/solidityEndToEndTest.cpp index 3c2bb0814..940ccac63 100644 --- a/test/solidityEndToEndTest.cpp +++ b/test/solidityEndToEndTest.cpp @@ -482,6 +482,34 @@ BOOST_AUTO_TEST_CASE(small_signed_types) testSolidityAgainstCpp(0, small_signed_types_cpp); } +BOOST_AUTO_TEST_CASE(strings) +{ + char const* sourceCode = "contract test {\n" + " function fixed() returns(string32 ret) {\n" + " return \"abc\\x00\\xff__\";\n" + " }\n" + " function pipeThrough(string2 small, bool one) returns(string16 large, bool oneRet) {\n" + " oneRet = one;\n" + " large = small;\n" + " }\n" + "}\n"; + compileAndRun(sourceCode); + bytes expectation(32, 0); + expectation[0] = byte('a'); + expectation[1] = byte('b'); + expectation[2] = byte('c'); + expectation[3] = byte(0); + expectation[4] = byte(0xff); + expectation[5] = byte('_'); + expectation[6] = byte('_'); + BOOST_CHECK(callContractFunction(0, bytes()) == expectation); + expectation = bytes(17, 0); + expectation[0] = 0; + expectation[1] = 2; + expectation[16] = 1; + BOOST_CHECK(callContractFunction(1, bytes({0x00, 0x02, 0x01})) == expectation); +} + BOOST_AUTO_TEST_CASE(state_smoke_test) { char const* sourceCode = "contract test {\n" @@ -1060,6 +1088,34 @@ BOOST_AUTO_TEST_CASE(inter_contract_calls_with_local_vars) BOOST_REQUIRE(callContractFunction(0, a, b) == toBigEndian(a * b + 9)); } +BOOST_AUTO_TEST_CASE(strings_in_calls) +{ + char const* sourceCode = R"( + contract Helper { + function invoke(string3 x, bool stop) returns (string4 ret) { + return x; + } + } + contract Main { + Helper h; + function callHelper(string2 x, bool stop) returns (string5 ret) { + return h.invoke(x, stop); + } + function getHelper() returns (address addr) { + return address(h); + } + function setHelper(address addr) { + h = Helper(addr); + } + })"; + compileAndRun(sourceCode, 0, "Helper"); + u160 const helperAddress = m_contractAddress; + compileAndRun(sourceCode, 0, "Main"); + BOOST_REQUIRE(callContractFunction(2, helperAddress) == bytes()); + BOOST_REQUIRE(callContractFunction(1, helperAddress) == toBigEndian(helperAddress)); + BOOST_CHECK(callContractFunction(0, bytes({0, 'a', 1})) == bytes({0, 'a', 0, 0, 0})); +} + BOOST_AUTO_TEST_SUITE_END() } From 0a4ed446797ac43255da00f5aee979476cf72025 Mon Sep 17 00:00:00 2001 From: Christian Date: Wed, 10 Dec 2014 17:15:10 +0100 Subject: [PATCH 10/40] Tests for empty and too long strings. --- test/solidityNameAndTypeResolution.cpp | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/test/solidityNameAndTypeResolution.cpp b/test/solidityNameAndTypeResolution.cpp index 783972296..7357471cc 100644 --- a/test/solidityNameAndTypeResolution.cpp +++ b/test/solidityNameAndTypeResolution.cpp @@ -226,6 +226,22 @@ BOOST_AUTO_TEST_CASE(type_inference_explicit_conversion) BOOST_CHECK_NO_THROW(parseTextAndResolveNames(text)); } +BOOST_AUTO_TEST_CASE(empty_string_literal) +{ + char const* text = "contract test {\n" + " function f() { var x = \"\"; }" + "}\n"; + BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError); +} + +BOOST_AUTO_TEST_CASE(large_string_literal) +{ + char const* text = "contract test {\n" + " function f() { var x = \"123456789012345678901234567890123\"; }" + "}\n"; + BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError); +} + BOOST_AUTO_TEST_CASE(balance) { char const* text = "contract test {\n" From 584242357a86ad9ddd051a13d65373578f7a8fc7 Mon Sep 17 00:00:00 2001 From: Christian Date: Wed, 10 Dec 2014 17:15:17 +0100 Subject: [PATCH 11/40] Helper functions to access memory. --- libsolidity/Compiler.cpp | 17 ++++---------- libsolidity/CompilerUtils.cpp | 30 +++++++++++++++++++++++++ libsolidity/CompilerUtils.h | 12 ++++++++++ libsolidity/ExpressionCompiler.cpp | 36 +++++++++++++----------------- 4 files changed, 61 insertions(+), 34 deletions(-) diff --git a/libsolidity/Compiler.cpp b/libsolidity/Compiler.cpp index 781f36800..578d63bbd 100644 --- a/libsolidity/Compiler.cpp +++ b/libsolidity/Compiler.cpp @@ -130,7 +130,6 @@ unsigned Compiler::appendCalldataUnpacker(FunctionDefinition const& _function, b { // We do not check the calldata size, everything is zero-padded. unsigned dataOffset = 1; - eth::Instruction load = _fromMemory ? eth::Instruction::MLOAD : eth::Instruction::CALLDATALOAD; //@todo this can be done more efficiently, saving some CALLDATALOAD calls for (ASTPointer const& var: _function.getParameters()) @@ -140,14 +139,8 @@ unsigned Compiler::appendCalldataUnpacker(FunctionDefinition const& _function, b BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(var->getLocation()) << errinfo_comment("Type " + var->getType()->toString() + " not yet supported.")); - if (numBytes == 32) - m_context << u256(dataOffset) << load; - else if (var->getType()->getCategory() == Type::Category::STRING) - m_context << (u256(1) << ((32 - numBytes) * 8)) << eth::Instruction::DUP1 - << u256(dataOffset) << load << eth::Instruction::DIV << eth::Instruction::MUL; - else - m_context << (u256(1) << ((32 - numBytes) * 8)) << u256(dataOffset) - << load << eth::Instruction::DIV; + bool leftAligned = var->getType()->getCategory() == Type::Category::STRING; + CompilerUtils(m_context).loadFromMemory(dataOffset, numBytes, leftAligned, !_fromMemory); dataOffset += numBytes; } return dataOffset; @@ -168,10 +161,8 @@ void Compiler::appendReturnValuePacker(FunctionDefinition const& _function) << errinfo_sourceLocation(parameters[i]->getLocation()) << errinfo_comment("Type " + paramType.toString() + " not yet supported.")); CompilerUtils(m_context).copyToStackTop(stackDepth, paramType); - if (numBytes != 32) - if (paramType.getCategory() != Type::Category::STRING) - m_context << (u256(1) << ((32 - numBytes) * 8)) << eth::Instruction::MUL; - m_context << u256(dataOffset) << eth::Instruction::MSTORE; + bool const leftAligned = paramType.getCategory() == Type::Category::STRING; + CompilerUtils(m_context).storeInMemory(dataOffset, numBytes, leftAligned); stackDepth -= paramType.getSizeOnStack(); dataOffset += numBytes; } diff --git a/libsolidity/CompilerUtils.cpp b/libsolidity/CompilerUtils.cpp index d4dfbe3c0..0ee4a53cb 100644 --- a/libsolidity/CompilerUtils.cpp +++ b/libsolidity/CompilerUtils.cpp @@ -31,6 +31,36 @@ namespace dev namespace solidity { +void CompilerUtils::loadFromMemory(unsigned _offset, unsigned _bytes, bool _leftAligned, bool _fromCalldata) +{ + eth::Instruction load = _fromCalldata ? eth::Instruction::CALLDATALOAD : eth::Instruction::MLOAD; + if (asserts(0 < _bytes && _bytes <= 32)) + BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Memory load of 0 or more than 32 bytes requested.")); + if (_bytes == 32) + m_context << u256(_offset) << load; + else + { + // load data and add leading or trailing zeros by dividing/multiplying depending on alignment + u256 shiftFactor = u256(1) << ((32 - _bytes) * 8); + m_context << shiftFactor; + if (_leftAligned) + m_context << eth::Instruction::DUP1; + m_context << u256(_offset) << load << eth::Instruction::DIV; + if (_leftAligned) + m_context << eth::Instruction::MUL; + } +} + +void CompilerUtils::storeInMemory(unsigned _offset, unsigned _bytes, bool _leftAligned) +{ + if (asserts(0 < _bytes && _bytes <= 32)) + BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Memory store of 0 or more than 32 bytes requested.")); + if (_bytes != 32 && !_leftAligned) + // shift the value accordingly before storing + m_context << (u256(1) << ((32 - _bytes) * 8)) << eth::Instruction::MUL; + m_context << u256(_offset) << eth::Instruction::MSTORE; +} + void CompilerUtils::moveToStackVariable(VariableDeclaration const& _variable) { unsigned const stackPosition = m_context.baseToCurrentStackOffset(m_context.getBaseStackOffsetOfVariable(_variable)); diff --git a/libsolidity/CompilerUtils.h b/libsolidity/CompilerUtils.h index 4da533752..928f0e2d3 100644 --- a/libsolidity/CompilerUtils.h +++ b/libsolidity/CompilerUtils.h @@ -35,6 +35,18 @@ class CompilerUtils public: CompilerUtils(CompilerContext& _context): m_context(_context) {} + /// Loads data from memory to the stack. + /// @param _offset offset in memory (or calldata) + /// @param _bytes number of bytes to load + /// @param _leftAligned if true, store left aligned on stack (otherwise right aligned) + /// @param _fromCalldata if true, load from calldata, not from memory + void loadFromMemory(unsigned _offset, unsigned _bytes = 32, bool _leftAligned = false, bool _fromCalldata = false); + /// Stores data from stack in memory. + /// @param _offset offset in memory + /// @param _bytes number of bytes to store + /// @param _leftAligned if true, data is left aligned on stack (otherwise right aligned) + void storeInMemory(unsigned _offset, unsigned _bytes = 32, bool _leftAligned = false); + /// Moves the value that is at the top of the stack to a stack variable. void moveToStackVariable(VariableDeclaration const& _variable); /// Copies a variable of type @a _type from a stack depth of @a _stackDepth to the top of the stack. diff --git a/libsolidity/ExpressionCompiler.cpp b/libsolidity/ExpressionCompiler.cpp index 5e688f6bd..d71d98cd6 100644 --- a/libsolidity/ExpressionCompiler.cpp +++ b/libsolidity/ExpressionCompiler.cpp @@ -225,15 +225,14 @@ bool ExpressionCompiler::visit(FunctionCall& _functionCall) BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(arguments[i]->getLocation()) << errinfo_comment("Type " + type.toString() + " not yet supported.")); - if (numBytes != 32) - if (type.getCategory() != Type::Category::STRING) - m_context << (u256(1) << ((32 - numBytes) * 8)) << eth::Instruction::MUL; - m_context << u256(dataOffset) << eth::Instruction::MSTORE; + bool const leftAligned = type.getCategory() == Type::Category::STRING; + CompilerUtils(m_context).storeInMemory(dataOffset, numBytes, leftAligned); dataOffset += numBytes; } //@todo only return the first return value for now - unsigned retSize = function.getReturnParameterTypes().empty() ? 0 - : function.getReturnParameterTypes().front()->getCalldataEncodedSize(); + Type const* firstType = function.getReturnParameterTypes().empty() ? nullptr : + function.getReturnParameterTypes().front().get(); + unsigned retSize = firstType ? firstType->getCalldataEncodedSize() : 0; // CALL arguments: outSize, outOff, inSize, inOff, value, addr, gas (stack top) m_context << u256(retSize) << u256(0) << u256(dataOffset) << u256(0) << u256(0); _functionCall.getExpression().accept(*this); // pushes addr and function index @@ -241,17 +240,10 @@ bool ExpressionCompiler::visit(FunctionCall& _functionCall) << u256(25) << eth::Instruction::GAS << eth::Instruction::SUB << eth::Instruction::CALL << eth::Instruction::POP; // @todo do not ignore failure indicator - if (retSize == 32) - m_context << u256(0) << eth::Instruction::MLOAD; - else if (retSize > 0) + if (retSize > 0) { - if (function.getReturnParameterTypes().front()->getCategory() == Type::Category::STRING) - m_context << (u256(1) << ((32 - retSize) * 8)) << eth::Instruction::DUP1 - << u256(0) << eth::Instruction::MLOAD - << eth::Instruction::DIV << eth::Instruction::MUL; - else - m_context << (u256(1) << ((32 - retSize) * 8)) - << u256(0) << eth::Instruction::MLOAD << eth::Instruction::DIV; + bool const leftAligned = firstType->getCategory() == Type::Category::STRING; + CompilerUtils(m_context).loadFromMemory(0, retSize, leftAligned); } break; } @@ -275,7 +267,8 @@ bool ExpressionCompiler::visit(FunctionCall& _functionCall) arguments.front()->accept(*this); appendTypeConversion(*arguments.front()->getType(), *function.getParameterTypes().front(), true); // @todo move this once we actually use memory - m_context << u256(0) << eth::Instruction::MSTORE << u256(32) << u256(0) << eth::Instruction::SHA3; + CompilerUtils(m_context).storeInMemory(0); + m_context << u256(32) << u256(0) << eth::Instruction::SHA3; break; case Location::ECRECOVER: case Location::SHA256: @@ -291,13 +284,13 @@ bool ExpressionCompiler::visit(FunctionCall& _functionCall) arguments[i]->accept(*this); appendTypeConversion(*arguments[i]->getType(), *function.getParameterTypes()[i], true); // @todo move this once we actually use memory - m_context << u256(i * 32) << eth::Instruction::MSTORE; + CompilerUtils(m_context).storeInMemory(i * 32); } m_context << u256(32) << u256(0) << u256(arguments.size() * 32) << u256(0) << u256(0) << contractAddress << u256(500) //@todo determine actual gas requirement << eth::Instruction::CALL - << eth::Instruction::POP - << u256(0) << eth::Instruction::MLOAD; + << eth::Instruction::POP; + CompilerUtils(m_context).loadFromMemory(0); break; } default: @@ -381,7 +374,8 @@ bool ExpressionCompiler::visit(IndexAccess& _indexAccess) *dynamic_cast(*_indexAccess.getBaseExpression().getType()).getKeyType(), true); // @todo move this once we actually use memory - m_context << u256(32) << eth::Instruction::MSTORE << u256(0) << eth::Instruction::MSTORE; + CompilerUtils(m_context).storeInMemory(0); + CompilerUtils(m_context).storeInMemory(32); m_context << u256(64) << u256(0) << eth::Instruction::SHA3; m_currentLValue = LValue(m_context, LValue::STORAGE, *_indexAccess.getType()); From b1dc0d5e6a1093bcb8849d932df7c7a91578f1d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Wed, 10 Dec 2014 17:41:53 +0100 Subject: [PATCH 12/40] VMFactory - a new way of creating VMs --- libethereum/Executive.cpp | 5 +++-- libethereum/State.cpp | 14 ++++++------ libevm/ExtVMFace.h | 2 +- libevm/VM.h | 8 ++++--- libevm/VMFactory.cpp | 46 +++++++++++++++++++++++++++++++++++++++ libevm/VMFactory.h | 43 ++++++++++++++++++++++++++++++++++++ test/createRandomTest.cpp | 8 +++---- test/vm.cpp | 7 +++--- 8 files changed, 113 insertions(+), 20 deletions(-) create mode 100644 libevm/VMFactory.cpp create mode 100644 libevm/VMFactory.h diff --git a/libethereum/Executive.cpp b/libethereum/Executive.cpp index db77d349a..9e2f5ee07 100644 --- a/libethereum/Executive.cpp +++ b/libethereum/Executive.cpp @@ -21,6 +21,7 @@ #include #include +#include #include #include "Interface.h" #include "Executive.h" @@ -105,7 +106,7 @@ bool Executive::call(Address _receiveAddress, Address _senderAddress, u256 _valu if (m_s.addressHasCode(_receiveAddress)) { - m_vm.reset(new VM(_gas)); + m_vm = VMFactory::create(_gas); bytes const& c = m_s.code(_receiveAddress); m_ext.reset(new ExtVM(m_s, _receiveAddress, _senderAddress, _originAddress, _value, _gasPrice, _data, &c, m_ms)); } @@ -124,7 +125,7 @@ bool Executive::create(Address _sender, u256 _endowment, u256 _gasPrice, u256 _g m_s.m_cache[m_newAddress] = Account(m_s.balance(m_newAddress) + _endowment, Account::ContractConception); // Execute _init. - m_vm.reset(new VM(_gas)); + m_vm = VMFactory::create(_gas); m_ext.reset(new ExtVM(m_s, m_newAddress, _sender, _origin, _endowment, _gasPrice, bytesConstRef(), _init, m_ms)); return _init.empty(); } diff --git a/libethereum/State.cpp b/libethereum/State.cpp index 335b7ff04..feb732882 100644 --- a/libethereum/State.cpp +++ b/libethereum/State.cpp @@ -29,7 +29,7 @@ #include #include #include -#include +#include #include "BlockChain.h" #include "Defaults.h" #include "ExtVM.h" @@ -1214,19 +1214,19 @@ bool State::call(Address _receiveAddress, Address _codeAddress, Address _senderA } else if (addressHasCode(_codeAddress)) { - VM vm(*_gas); + auto vm = VMFactory::create(*_gas); ExtVM evm(*this, _receiveAddress, _senderAddress, _originAddress, _value, _gasPrice, _data, &code(_codeAddress), o_ms, _level); bool revert = false; try { - auto out = vm.go(evm, _onOp); + auto out = vm->go(evm, _onOp); memcpy(_out.data(), out.data(), std::min(out.size(), _out.size())); if (o_sub) *o_sub += evm.sub; if (o_ms) o_ms->output = out.toBytes(); - *_gas = vm.gas(); + *_gas = vm->gas(); } catch (VMException const& _e) { @@ -1273,19 +1273,19 @@ h160 State::create(Address _sender, u256 _endowment, u256 _gasPrice, u256* _gas, m_cache[newAddress] = Account(balance(newAddress) + _endowment, Account::ContractConception); // Execute init code. - VM vm(*_gas); + auto vm = VMFactory::create(*_gas); ExtVM evm(*this, newAddress, _sender, _origin, _endowment, _gasPrice, bytesConstRef(), _code, o_ms, _level); bool revert = false; bytesConstRef out; try { - out = vm.go(evm, _onOp); + out = vm->go(evm, _onOp); if (o_ms) o_ms->output = out.toBytes(); if (o_sub) *o_sub += evm.sub; - *_gas = vm.gas(); + *_gas = vm->gas(); } catch (VMException const& _e) { diff --git a/libevm/ExtVMFace.h b/libevm/ExtVMFace.h index 4a175ff4e..52798cd89 100644 --- a/libevm/ExtVMFace.h +++ b/libevm/ExtVMFace.h @@ -150,7 +150,7 @@ public: BlockInfo previousBlock; ///< The previous block's information. BlockInfo currentBlock; ///< The current block's information. SubState sub; ///< Sub-band VM state (suicides, refund counter, logs). - unsigned depth; ///< Depth of the present call. + unsigned depth = 0; ///< Depth of the present call. }; } diff --git a/libevm/VM.h b/libevm/VM.h index 654e9719f..3b9e32c7b 100644 --- a/libevm/VM.h +++ b/libevm/VM.h @@ -52,9 +52,6 @@ inline u256 fromAddress(Address _a) class VM : public VMFace { public: - /// Construct VM object. - explicit VM(u256 _gas = 0) : VMFace(_gas) {} - virtual void reset(u256 _gas = 0) noexcept override final; virtual bytesConstRef go(ExtVMFace& _ext, OnOpFunc const& _onOp = {}, uint64_t _steps = (uint64_t)-1) override final; @@ -68,6 +65,11 @@ public: u256s const& stack() const { return m_stack; } private: + friend class VMFactory; + + /// Construct VM object. + explicit VM(u256 _gas = 0) : VMFace(_gas) {} + template bytesConstRef goImpl(Ext& _ext, OnOpFunc const& _onOp = OnOpFunc(), uint64_t _steps = (uint64_t)-1); diff --git a/libevm/VMFactory.cpp b/libevm/VMFactory.cpp new file mode 100644 index 000000000..423c6b8ce --- /dev/null +++ b/libevm/VMFactory.cpp @@ -0,0 +1,46 @@ +/* + 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 . +*/ + +#include "VMFactory.h" +#include +#include "VM.h" + +namespace dev +{ +namespace eth +{ +namespace +{ + VMKind g_kind = VMKind::Interpreter; +} + +void VMFactory::setKind(VMKind _kind) +{ + g_kind = _kind; +} + +std::unique_ptr VMFactory::create(u256 _gas) +{ + assert(g_kind == VMKind::Interpreter && "Only interpreter supported for now"); + return std::unique_ptr(new VM(_gas)); +} + +} +} + + + diff --git a/libevm/VMFactory.h b/libevm/VMFactory.h new file mode 100644 index 000000000..155350602 --- /dev/null +++ b/libevm/VMFactory.h @@ -0,0 +1,43 @@ +/* + 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 . +*/ + +#include "VMFace.h" + +namespace dev +{ +namespace eth +{ + +enum class VMKind : bool +{ + Interpreter, + JIT +}; + +class VMFactory +{ +public: + VMFactory() = delete; + + static std::unique_ptr create(u256 _gas); + + static void setKind(VMKind _kind); + +}; + +} +} diff --git a/test/createRandomTest.cpp b/test/createRandomTest.cpp index caeeb6b67..a11688457 100644 --- a/test/createRandomTest.cpp +++ b/test/createRandomTest.cpp @@ -32,7 +32,7 @@ #include #include #include -#include +#include #include "vm.h" using namespace std; @@ -142,14 +142,14 @@ void doMyTests(json_spirit::mValue& v) } bytes output; - eth::VM vm(fev.gas); + auto vm = eth::VMFactory::create(fev.gas); u256 gas; bool vmExceptionOccured = false; try { - output = vm.go(fev, fev.simpleTrace()).toBytes(); - gas = vm.gas(); + output = vm->go(fev, fev.simpleTrace()).toBytes(); + gas = vm->gas(); } catch (eth::VMException const& _e) { diff --git a/test/vm.cpp b/test/vm.cpp index 45034b716..02cee0a05 100644 --- a/test/vm.cpp +++ b/test/vm.cpp @@ -22,6 +22,7 @@ #include #include +#include #include "vm.h" using namespace std; @@ -298,14 +299,14 @@ void doVMTests(json_spirit::mValue& v, bool _fillin) } bytes output; - VM vm(fev.gas); + auto vm = eth::VMFactory::create(fev.gas); u256 gas; bool vmExceptionOccured = false; try { - output = vm.go(fev, fev.simpleTrace()).toBytes(); - gas = vm.gas(); + output = vm->go(fev, fev.simpleTrace()).toBytes(); + gas = vm->gas(); } catch (VMException const& _e) { From c7526d0d95a3eff32494f572b2b2a1dee24bdf98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Wed, 10 Dec 2014 20:13:33 +0100 Subject: [PATCH 13/40] Style fixes (mostly) --- libevm/VM.h | 4 ++-- libevm/VMFace.h | 14 +++++++------- libevm/VMFactory.cpp | 3 +-- libevm/VMFactory.h | 3 ++- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/libevm/VM.h b/libevm/VM.h index 3b9e32c7b..d03760a78 100644 --- a/libevm/VM.h +++ b/libevm/VM.h @@ -49,7 +49,7 @@ inline u256 fromAddress(Address _a) /** */ -class VM : public VMFace +class VM: public VMFace { public: virtual void reset(u256 _gas = 0) noexcept override final; @@ -68,7 +68,7 @@ private: friend class VMFactory; /// Construct VM object. - explicit VM(u256 _gas = 0) : VMFace(_gas) {} + explicit VM(u256 _gas = 0): VMFace(_gas) {} template bytesConstRef goImpl(Ext& _ext, OnOpFunc const& _onOp = OnOpFunc(), uint64_t _steps = (uint64_t)-1); diff --git a/libevm/VMFace.h b/libevm/VMFace.h index f6db7d0b5..319da7fc1 100644 --- a/libevm/VMFace.h +++ b/libevm/VMFace.h @@ -26,13 +26,13 @@ namespace dev namespace eth { -struct VMException : virtual Exception {}; -struct StepsDone : virtual VMException {}; -struct BreakPointHit : virtual VMException {}; -struct BadInstruction : virtual VMException {}; -struct BadJumpDestination : virtual VMException {}; -struct OutOfGas : virtual VMException {}; -struct StackTooSmall : virtual public VMException {}; +struct VMException: virtual Exception {}; +struct StepsDone: virtual VMException {}; +struct BreakPointHit: virtual VMException {}; +struct BadInstruction: virtual VMException {}; +struct BadJumpDestination: virtual VMException {}; +struct OutOfGas: virtual VMException {}; +struct StackTooSmall: virtual VMException {}; /** */ diff --git a/libevm/VMFactory.cpp b/libevm/VMFactory.cpp index 423c6b8ce..90e17c553 100644 --- a/libevm/VMFactory.cpp +++ b/libevm/VMFactory.cpp @@ -16,7 +16,6 @@ */ #include "VMFactory.h" -#include #include "VM.h" namespace dev @@ -35,7 +34,7 @@ void VMFactory::setKind(VMKind _kind) std::unique_ptr VMFactory::create(u256 _gas) { - assert(g_kind == VMKind::Interpreter && "Only interpreter supported for now"); + asserts(g_kind == VMKind::Interpreter && "Only interpreter supported for now"); return std::unique_ptr(new VM(_gas)); } diff --git a/libevm/VMFactory.h b/libevm/VMFactory.h index 155350602..c5d9c4f65 100644 --- a/libevm/VMFactory.h +++ b/libevm/VMFactory.h @@ -14,6 +14,7 @@ You should have received a copy of the GNU General Public License along with cpp-ethereum. If not, see . */ +#pragma once #include "VMFace.h" @@ -22,7 +23,7 @@ namespace dev namespace eth { -enum class VMKind : bool +enum class VMKind: bool { Interpreter, JIT From 15b2fcd5432492ec4d4a6e36f492db3d824ed1fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Wed, 10 Dec 2014 21:55:29 +0100 Subject: [PATCH 14/40] Catch exception by reference --- libethereum/State.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libethereum/State.cpp b/libethereum/State.cpp index feb732882..539cb55f0 100644 --- a/libethereum/State.cpp +++ b/libethereum/State.cpp @@ -1102,7 +1102,7 @@ bool State::isTrieGood(bool _enforceRefs, bool _requireNoLeftOvers) const return false; } } - catch (InvalidTrie) + catch (InvalidTrie const&) { cwarn << "BAD TRIE" << (e ? "[enforced" : "[unenforced") << "refs]"; cnote << m_db.keys(); From 37a15d96ea38401cdeddefdbfe4cfb39e7b83682 Mon Sep 17 00:00:00 2001 From: Christian Date: Thu, 11 Dec 2014 14:19:11 +0100 Subject: [PATCH 15/40] Support empty strings. --- libsolidity/AST.cpp | 2 +- libsolidity/Compiler.cpp | 4 ++-- libsolidity/CompilerUtils.cpp | 18 ++++++++++++++---- libsolidity/Token.h | 1 + libsolidity/Types.cpp | 8 ++++---- test/solidityEndToEndTest.cpp | 14 ++++++++++++++ test/solidityNameAndTypeResolution.cpp | 8 -------- 7 files changed, 36 insertions(+), 19 deletions(-) diff --git a/libsolidity/AST.cpp b/libsolidity/AST.cpp index 593e0960c..8174d138a 100644 --- a/libsolidity/AST.cpp +++ b/libsolidity/AST.cpp @@ -339,7 +339,7 @@ void Literal::checkTypeRequirements() { m_type = Type::forLiteral(*this); if (!m_type) - BOOST_THROW_EXCEPTION(createTypeError("Literal value too large or too small.")); + BOOST_THROW_EXCEPTION(createTypeError("Literal value too large.")); } } diff --git a/libsolidity/Compiler.cpp b/libsolidity/Compiler.cpp index 940a5e700..92d574fb1 100644 --- a/libsolidity/Compiler.cpp +++ b/libsolidity/Compiler.cpp @@ -135,7 +135,7 @@ unsigned Compiler::appendCalldataUnpacker(FunctionDefinition const& _function, b for (ASTPointer const& var: _function.getParameters()) { unsigned const numBytes = var->getType()->getCalldataEncodedSize(); - if (numBytes == 0 || numBytes > 32) + if (numBytes > 32) BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(var->getLocation()) << errinfo_comment("Type " + var->getType()->toString() + " not yet supported.")); @@ -156,7 +156,7 @@ void Compiler::appendReturnValuePacker(FunctionDefinition const& _function) { Type const& paramType = *parameters[i]->getType(); unsigned numBytes = paramType.getCalldataEncodedSize(); - if (numBytes == 0 || numBytes > 32) + if (numBytes > 32) BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(parameters[i]->getLocation()) << errinfo_comment("Type " + paramType.toString() + " not yet supported.")); diff --git a/libsolidity/CompilerUtils.cpp b/libsolidity/CompilerUtils.cpp index 0ee4a53cb..9f474896e 100644 --- a/libsolidity/CompilerUtils.cpp +++ b/libsolidity/CompilerUtils.cpp @@ -33,9 +33,14 @@ namespace solidity void CompilerUtils::loadFromMemory(unsigned _offset, unsigned _bytes, bool _leftAligned, bool _fromCalldata) { + if (_bytes == 0) + { + m_context << u256(0); + return; + } eth::Instruction load = _fromCalldata ? eth::Instruction::CALLDATALOAD : eth::Instruction::MLOAD; - if (asserts(0 < _bytes && _bytes <= 32)) - BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Memory load of 0 or more than 32 bytes requested.")); + if (asserts(_bytes <= 32)) + BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Memory load of more than 32 bytes requested.")); if (_bytes == 32) m_context << u256(_offset) << load; else @@ -53,8 +58,13 @@ void CompilerUtils::loadFromMemory(unsigned _offset, unsigned _bytes, bool _left void CompilerUtils::storeInMemory(unsigned _offset, unsigned _bytes, bool _leftAligned) { - if (asserts(0 < _bytes && _bytes <= 32)) - BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Memory store of 0 or more than 32 bytes requested.")); + if (_bytes == 0) + { + m_context << eth::Instruction::POP; + return; + } + if (asserts(_bytes <= 32)) + BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Memory store of more than 32 bytes requested.")); if (_bytes != 32 && !_leftAligned) // shift the value accordingly before storing m_context << (u256(1) << ((32 - _bytes) * 8)) << eth::Instruction::MUL; diff --git a/libsolidity/Token.h b/libsolidity/Token.h index f9ded3114..21b74eceb 100644 --- a/libsolidity/Token.h +++ b/libsolidity/Token.h @@ -269,6 +269,7 @@ namespace solidity K(ADDRESS, "address", 0) \ K(BOOL, "bool", 0) \ K(STRING_TYPE, "string", 0) \ + K(STRING0, "string0", 0) \ K(STRING1, "string1", 0) \ K(STRING2, "string2", 0) \ K(STRING3, "string3", 0) \ diff --git a/libsolidity/Types.cpp b/libsolidity/Types.cpp index 543c27c2d..00e530c3f 100644 --- a/libsolidity/Types.cpp +++ b/libsolidity/Types.cpp @@ -53,8 +53,8 @@ shared_ptr Type::fromElementaryTypeName(Token::Value _typeToken) return make_shared(0, IntegerType::Modifier::ADDRESS); else if (_typeToken == Token::BOOL) return make_shared(); - else if (Token::STRING1 <= _typeToken && _typeToken <= Token::STRING32) - return make_shared(int(_typeToken) - int(Token::STRING1) + 1); + else if (Token::STRING0 <= _typeToken && _typeToken <= Token::STRING32) + return make_shared(int(_typeToken) - int(Token::STRING0)); else BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unable to convert elementary typename " + std::string(Token::toString(_typeToken)) + " to type.")); @@ -199,14 +199,14 @@ const MemberList IntegerType::AddressMemberList = shared_ptr StaticStringType::smallestTypeForLiteral(string const& _literal) { - if (0 < _literal.length() && _literal.length() <= 32) + if (_literal.length() <= 32) return make_shared(_literal.length()); return shared_ptr(); } StaticStringType::StaticStringType(int _bytes): m_bytes(_bytes) { - if (asserts(m_bytes > 0 && m_bytes <= 32)) + if (asserts(m_bytes >= 0 && m_bytes <= 32)) BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Invalid byte number for static string type: " + dev::toString(m_bytes))); } diff --git a/test/solidityEndToEndTest.cpp b/test/solidityEndToEndTest.cpp index 940ccac63..5523ded39 100644 --- a/test/solidityEndToEndTest.cpp +++ b/test/solidityEndToEndTest.cpp @@ -510,6 +510,20 @@ BOOST_AUTO_TEST_CASE(strings) BOOST_CHECK(callContractFunction(1, bytes({0x00, 0x02, 0x01})) == expectation); } +BOOST_AUTO_TEST_CASE(empty_string_on_stack) +{ + char const* sourceCode = "contract test {\n" + " function run(string0 empty, uint8 inp) returns(uint16 a, string0 b, string4 c) {\n" + " var x = \"abc\";\n" + " var y = \"\";\n" + " var z = inp;\n" + " a = z; b = y; c = x;" + " }\n" + "}\n"; + compileAndRun(sourceCode); + BOOST_CHECK(callContractFunction(0, bytes({0x02})) == bytes({0x00, 0x02, 'a', 'b', 'c', 0x00})); +} + BOOST_AUTO_TEST_CASE(state_smoke_test) { char const* sourceCode = "contract test {\n" diff --git a/test/solidityNameAndTypeResolution.cpp b/test/solidityNameAndTypeResolution.cpp index 7357471cc..03eaebb3a 100644 --- a/test/solidityNameAndTypeResolution.cpp +++ b/test/solidityNameAndTypeResolution.cpp @@ -226,14 +226,6 @@ BOOST_AUTO_TEST_CASE(type_inference_explicit_conversion) BOOST_CHECK_NO_THROW(parseTextAndResolveNames(text)); } -BOOST_AUTO_TEST_CASE(empty_string_literal) -{ - char const* text = "contract test {\n" - " function f() { var x = \"\"; }" - "}\n"; - BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError); -} - BOOST_AUTO_TEST_CASE(large_string_literal) { char const* text = "contract test {\n" From 7e2e58aaa5793620a7f2e0860e811c86861bb1f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Thu, 11 Dec 2014 15:41:19 +0100 Subject: [PATCH 16/40] VM::go detemplatized. Cleanups. --- libevm/VM.cpp | 8 -------- libevm/VM.h | 13 +++++-------- libevm/VMFace.h | 12 +++--------- libevm/VMFactory.cpp | 3 --- libevm/VMFactory.h | 2 -- 5 files changed, 8 insertions(+), 30 deletions(-) diff --git a/libevm/VM.cpp b/libevm/VM.cpp index 44620a3d5..b8452e4f5 100644 --- a/libevm/VM.cpp +++ b/libevm/VM.cpp @@ -31,11 +31,3 @@ void VM::reset(u256 _gas) noexcept m_curPC = 0; m_jumpDests.clear(); } - -bytesConstRef VM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _steps) -{ - if (auto defaultExt = dynamic_cast(&_ext)) - return goImpl(*defaultExt, _onOp, _steps); - else - return goImpl(_ext, _onOp, _steps); -} diff --git a/libevm/VM.h b/libevm/VM.h index d03760a78..c427802f6 100644 --- a/libevm/VM.h +++ b/libevm/VM.h @@ -68,10 +68,7 @@ private: friend class VMFactory; /// Construct VM object. - explicit VM(u256 _gas = 0): VMFace(_gas) {} - - template - bytesConstRef goImpl(Ext& _ext, OnOpFunc const& _onOp = OnOpFunc(), uint64_t _steps = (uint64_t)-1); + explicit VM(u256 _gas): VMFace(_gas) {} u256 m_curPC = 0; bytes m_temp; @@ -80,10 +77,8 @@ private: std::function m_onFail; }; -} - -// INLINE: -template dev::bytesConstRef dev::eth::VM::goImpl(Ext& _ext, OnOpFunc const& _onOp, uint64_t _steps) +// TODO: Move it to cpp file. Not done to make review easier. +inline bytesConstRef VM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _steps) { auto memNeed = [](dev::u256 _offset, dev::u256 _size) { return _size ? (bigint)_offset + _size : (bigint)0; }; @@ -864,4 +859,6 @@ template dev::bytesConstRef dev::eth::VM::goImpl(Ext& _ext, OnOpFunc BOOST_THROW_EXCEPTION(StepsDone()); return bytesConstRef(); } + +} } diff --git a/libevm/VMFace.h b/libevm/VMFace.h index 319da7fc1..44ae03868 100644 --- a/libevm/VMFace.h +++ b/libevm/VMFace.h @@ -14,7 +14,6 @@ You should have received a copy of the GNU General Public License along with cpp-ethereum. If not, see . */ - #pragma once #include @@ -34,25 +33,20 @@ struct BadJumpDestination: virtual VMException {}; struct OutOfGas: virtual VMException {}; struct StackTooSmall: virtual VMException {}; -/** - */ +/// EVM Virtual Machine interface class VMFace { public: - /// Construct VM object. - explicit VMFace(u256 _gas = 0): m_gas(_gas) {} - + explicit VMFace(u256 _gas): m_gas(_gas) {} virtual ~VMFace() = default; - VMFace(VMFace const&) = delete; void operator=(VMFace const&) = delete; virtual void reset(u256 _gas = 0) noexcept { m_gas = _gas; } + u256 gas() const noexcept { return m_gas; } virtual bytesConstRef go(ExtVMFace& _ext, OnOpFunc const& _onOp = {}, uint64_t _steps = (uint64_t)-1) = 0; - u256 gas() const { return m_gas; } - protected: u256 m_gas = 0; }; diff --git a/libevm/VMFactory.cpp b/libevm/VMFactory.cpp index 90e17c553..af37ec710 100644 --- a/libevm/VMFactory.cpp +++ b/libevm/VMFactory.cpp @@ -40,6 +40,3 @@ std::unique_ptr VMFactory::create(u256 _gas) } } - - - diff --git a/libevm/VMFactory.h b/libevm/VMFactory.h index c5d9c4f65..d0d02e0c4 100644 --- a/libevm/VMFactory.h +++ b/libevm/VMFactory.h @@ -35,9 +35,7 @@ public: VMFactory() = delete; static std::unique_ptr create(u256 _gas); - static void setKind(VMKind _kind); - }; } From 615438a890427f576bab5b7fcc5b3a8ec49098a1 Mon Sep 17 00:00:00 2001 From: Christian Date: Thu, 11 Dec 2014 17:35:23 +0100 Subject: [PATCH 17/40] Swap literals to the end if optimizing. --- libsolidity/Compiler.cpp | 15 ++++++++++----- libsolidity/Compiler.h | 6 ++++-- libsolidity/CompilerStack.cpp | 4 ++-- libsolidity/ExpressionCompiler.cpp | 26 ++++++++++++++++++++------ libsolidity/ExpressionCompiler.h | 7 ++++--- libsolidity/Token.h | 2 ++ test/solidityOptimizerTest.cpp | 19 +++++++++++++++++-- 7 files changed, 59 insertions(+), 20 deletions(-) diff --git a/libsolidity/Compiler.cpp b/libsolidity/Compiler.cpp index b094af194..d41bcffad 100644 --- a/libsolidity/Compiler.cpp +++ b/libsolidity/Compiler.cpp @@ -246,7 +246,7 @@ bool Compiler::visit(FunctionDefinition const& _function) bool Compiler::visit(IfStatement const& _ifStatement) { - ExpressionCompiler::compileExpression(m_context, _ifStatement.getCondition()); + compileExpression(_ifStatement.getCondition()); eth::AssemblyItem trueTag = m_context.appendConditionalJump(); if (_ifStatement.getFalseStatement()) _ifStatement.getFalseStatement()->accept(*this); @@ -265,7 +265,7 @@ bool Compiler::visit(WhileStatement const& _whileStatement) m_breakTags.push_back(loopEnd); m_context << loopStart; - ExpressionCompiler::compileExpression(m_context, _whileStatement.getCondition()); + compileExpression(_whileStatement.getCondition()); m_context << eth::Instruction::ISZERO; m_context.appendConditionalJumpTo(loopEnd); @@ -298,7 +298,7 @@ bool Compiler::visit(Return const& _return) //@todo modifications are needed to make this work with functions returning multiple values if (Expression const* expression = _return.getExpression()) { - ExpressionCompiler::compileExpression(m_context, *expression); + compileExpression(*expression); VariableDeclaration const& firstVariable = *_return.getFunctionReturnParameters().getParameters().front(); ExpressionCompiler::appendTypeConversion(m_context, *expression->getType(), *firstVariable.getType()); @@ -312,7 +312,7 @@ bool Compiler::visit(VariableDefinition const& _variableDefinition) { if (Expression const* expression = _variableDefinition.getExpression()) { - ExpressionCompiler::compileExpression(m_context, *expression); + compileExpression(*expression); ExpressionCompiler::appendTypeConversion(m_context, *expression->getType(), *_variableDefinition.getDeclaration().getType()); @@ -324,10 +324,15 @@ bool Compiler::visit(VariableDefinition const& _variableDefinition) bool Compiler::visit(ExpressionStatement const& _expressionStatement) { Expression const& expression = _expressionStatement.getExpression(); - ExpressionCompiler::compileExpression(m_context, expression); + compileExpression(expression); CompilerUtils(m_context).popStackElement(*expression.getType()); return false; } +void Compiler::compileExpression(Expression const& _expression) +{ + ExpressionCompiler::compileExpression(m_context, _expression, m_optimize); +} + } } diff --git a/libsolidity/Compiler.h b/libsolidity/Compiler.h index 4b8f02c5d..639e98410 100644 --- a/libsolidity/Compiler.h +++ b/libsolidity/Compiler.h @@ -30,10 +30,10 @@ namespace solidity { class Compiler: private ASTConstVisitor { public: - Compiler(): m_returnTag(m_context.newTag()) {} + explicit Compiler(bool _optimize = false): m_optimize(_optimize), m_returnTag(m_context.newTag()) {} void compileContract(ContractDefinition const& _contract, std::vector const& _magicGlobals); - bytes getAssembledBytecode(bool _optimize = false) { return m_context.getAssembledBytecode(_optimize); } + bytes getAssembledBytecode() { return m_context.getAssembledBytecode(m_optimize); } void streamAssembly(std::ostream& _stream) const { m_context.streamAssembly(_stream); } private: @@ -57,7 +57,9 @@ private: virtual bool visit(VariableDefinition const& _variableDefinition) override; virtual bool visit(ExpressionStatement const& _expressionStatement) override; + void compileExpression(Expression const& _expression); + bool const m_optimize; CompilerContext m_context; std::vector m_breakTags; ///< tag to jump to for a "break" statement std::vector m_continueTags; ///< tag to jump to for a "continue" statement diff --git a/libsolidity/CompilerStack.cpp b/libsolidity/CompilerStack.cpp index d46754856..7aaa79b1c 100644 --- a/libsolidity/CompilerStack.cpp +++ b/libsolidity/CompilerStack.cpp @@ -101,10 +101,10 @@ void CompilerStack::compile(bool _optimize) if (ContractDefinition* contract = dynamic_cast(node.get())) { m_globalContext->setCurrentContract(*contract); - shared_ptr compiler = make_shared(); + shared_ptr compiler = make_shared(_optimize); compiler->compileContract(*contract, m_globalContext->getMagicVariables()); Contract& compiledContract = m_contracts[contract->getName()]; - compiledContract.bytecode = compiler->getAssembledBytecode(_optimize); + compiledContract.bytecode = compiler->getAssembledBytecode(); compiledContract.compiler = move(compiler); } } diff --git a/libsolidity/ExpressionCompiler.cpp b/libsolidity/ExpressionCompiler.cpp index a0b0a54a8..a19c9cfd8 100644 --- a/libsolidity/ExpressionCompiler.cpp +++ b/libsolidity/ExpressionCompiler.cpp @@ -33,9 +33,9 @@ using namespace std; namespace dev { namespace solidity { -void ExpressionCompiler::compileExpression(CompilerContext& _context, Expression const& _expression) +void ExpressionCompiler::compileExpression(CompilerContext& _context, Expression const& _expression, bool _optimize) { - ExpressionCompiler compiler(_context); + ExpressionCompiler compiler(_context, _optimize); _expression.accept(compiler); } @@ -145,10 +145,24 @@ bool ExpressionCompiler::visit(BinaryOperation const& _binaryOperation) if (Token::isCompareOp(op) || op == Token::DIV || op == Token::MOD) cleanupNeeded = true; - rightExpression.accept(*this); - appendTypeConversion(*rightExpression.getType(), commonType, cleanupNeeded); - leftExpression.accept(*this); - appendTypeConversion(*leftExpression.getType(), commonType, cleanupNeeded); + // for commutative operators, push the literal as late as possible to allow improved optimization + //@todo this has to be extended for literal expressions + bool swap = (m_optimize && Token::isCommutativeOp(op) && dynamic_cast(&rightExpression) + && !dynamic_cast(&leftExpression)); + if (swap) + { + leftExpression.accept(*this); + appendTypeConversion(*leftExpression.getType(), commonType, cleanupNeeded); + rightExpression.accept(*this); + appendTypeConversion(*rightExpression.getType(), commonType, cleanupNeeded); + } + else + { + rightExpression.accept(*this); + appendTypeConversion(*rightExpression.getType(), commonType, cleanupNeeded); + leftExpression.accept(*this); + appendTypeConversion(*leftExpression.getType(), commonType, cleanupNeeded); + } if (Token::isCompareOp(op)) appendCompareOperatorCode(op, commonType); else diff --git a/libsolidity/ExpressionCompiler.h b/libsolidity/ExpressionCompiler.h index 41cb66763..fb4577c8c 100644 --- a/libsolidity/ExpressionCompiler.h +++ b/libsolidity/ExpressionCompiler.h @@ -45,14 +45,14 @@ class ExpressionCompiler: private ASTConstVisitor { public: /// Compile the given @a _expression into the @a _context. - static void compileExpression(CompilerContext& _context, Expression const& _expression); + static void compileExpression(CompilerContext& _context, Expression const& _expression, bool _optimize = false); /// Appends code to remove dirty higher order bits in case of an implicit promotion to a wider type. static void appendTypeConversion(CompilerContext& _context, Type const& _typeOnStack, Type const& _targetType); private: - ExpressionCompiler(CompilerContext& _compilerContext): - m_context(_compilerContext), m_currentLValue(m_context) {} + explicit ExpressionCompiler(CompilerContext& _compilerContext, bool _optimize = false): + m_optimize(_optimize), m_context(_compilerContext), m_currentLValue(m_context) {} virtual bool visit(Assignment const& _assignment) override; virtual void endVisit(UnaryOperation const& _unaryOperation) override; @@ -132,6 +132,7 @@ private: unsigned m_stackSize; }; + bool m_optimize; CompilerContext& m_context; LValue m_currentLValue; }; diff --git a/libsolidity/Token.h b/libsolidity/Token.h index f1a94af35..c748d9892 100644 --- a/libsolidity/Token.h +++ b/libsolidity/Token.h @@ -317,6 +317,8 @@ public: static bool isElementaryTypeName(Value tok) { return INT <= tok && tok < TYPES_END; } static bool isAssignmentOp(Value tok) { return ASSIGN <= tok && tok <= ASSIGN_MOD; } static bool isBinaryOp(Value op) { return COMMA <= op && op <= MOD; } + static bool isCommutativeOp(Value op) { return op == BIT_OR || op == BIT_XOR || op == BIT_AND || + op == ADD || op == MUL || op == EQ || op == NE; } static bool isArithmeticOp(Value op) { return ADD <= op && op <= MOD; } static bool isCompareOp(Value op) { return EQ <= op && op <= IN; } diff --git a/test/solidityOptimizerTest.cpp b/test/solidityOptimizerTest.cpp index f8d3b552c..69000a57f 100644 --- a/test/solidityOptimizerTest.cpp +++ b/test/solidityOptimizerTest.cpp @@ -48,7 +48,7 @@ public: m_optimize = true; bytes optimizedBytecode = compileAndRun(_sourceCode, _value, _contractName); int sizeDiff = nonOptimizedBytecode.size() - optimizedBytecode.size(); - BOOST_CHECK_MESSAGE(sizeDiff >= _expectedSizeDecrease, "Bytecode did only shrink by " + BOOST_CHECK_MESSAGE(sizeDiff == _expectedSizeDecrease, "Bytecode did only shrink by " + boost::lexical_cast(sizeDiff) + " bytes, expected: " + boost::lexical_cast(_expectedSizeDecrease)); m_optimizedContract = m_contractAddress; @@ -106,7 +106,7 @@ BOOST_AUTO_TEST_CASE(invariants) return (((a + (1 - 1)) ^ 0) | 0) & (uint(0) - 1); } })"; - compileBothVersions(19, sourceCode); + compileBothVersions(28, sourceCode); compareVersions(0, u256(0x12334664)); } @@ -124,6 +124,21 @@ BOOST_AUTO_TEST_CASE(unused_expressions) compareVersions(0); } +BOOST_AUTO_TEST_CASE(constant_folding_both_sides) +{ + // if constants involving the same associative and commutative operator are applied from both + // sides, the operator should be applied only once, because the expression compiler + // (even in non-optimized mode) pushes literals as late as possible + char const* sourceCode = R"( + contract test { + function f(uint x) returns (uint y) { + return 98 ^ (7 * ((1 | (x | 1000)) * 40) ^ 102); + } + })"; + compileBothVersions(31, sourceCode); + compareVersions(0); +} + BOOST_AUTO_TEST_SUITE_END() } From 22fa12debf95a5ec0481c437ebd27c2d7f4324c0 Mon Sep 17 00:00:00 2001 From: Christian Date: Thu, 11 Dec 2014 17:34:47 +0100 Subject: [PATCH 18/40] Do not add at the end of the function selector "loop". --- libsolidity/Compiler.cpp | 4 ++-- test/solidityCompiler.cpp | 14 +++++++------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/libsolidity/Compiler.cpp b/libsolidity/Compiler.cpp index d41bcffad..974313418 100644 --- a/libsolidity/Compiler.cpp +++ b/libsolidity/Compiler.cpp @@ -109,8 +109,8 @@ void Compiler::appendFunctionSelector(ContractDefinition const& _contract) callDataUnpackerEntryPoints.push_back(m_context.newTag()); m_context << eth::dupInstruction(2) << eth::dupInstruction(2) << eth::Instruction::EQ; m_context.appendConditionalJumpTo(callDataUnpackerEntryPoints.back()); - m_context << eth::dupInstruction(4) << eth::Instruction::ADD; - //@todo avoid the last ADD (or remove it in the optimizer) + if (funid < interfaceFunctions.size() - 1) + m_context << eth::dupInstruction(4) << eth::Instruction::ADD; } m_context << eth::Instruction::STOP; // function not found diff --git a/test/solidityCompiler.cpp b/test/solidityCompiler.cpp index 004740b5e..eae8f3142 100644 --- a/test/solidityCompiler.cpp +++ b/test/solidityCompiler.cpp @@ -87,7 +87,7 @@ BOOST_AUTO_TEST_CASE(smoke_test) "}\n"; bytes code = compileContract(sourceCode); - unsigned boilerplateSize = 42; + unsigned boilerplateSize = 40; bytes expectation({byte(Instruction::JUMPDEST), byte(Instruction::PUSH1), 0x0, // initialize local variable x byte(Instruction::PUSH1), 0x2, @@ -107,8 +107,8 @@ BOOST_AUTO_TEST_CASE(different_argument_numbers) "}\n"; bytes code = compileContract(sourceCode); - unsigned shift = 70; - unsigned boilerplateSize = 83; + unsigned shift = 68; + unsigned boilerplateSize = 81; bytes expectation({byte(Instruction::JUMPDEST), byte(Instruction::PUSH1), 0x0, // initialize return variable d byte(Instruction::DUP3), @@ -158,8 +158,8 @@ BOOST_AUTO_TEST_CASE(ifStatement) "}\n"; bytes code = compileContract(sourceCode); - unsigned shift = 29; - unsigned boilerplateSize = 42; + unsigned shift = 27; + unsigned boilerplateSize = 40; bytes expectation({byte(Instruction::JUMPDEST), byte(Instruction::PUSH1), 0x0, byte(Instruction::DUP1), @@ -200,8 +200,8 @@ BOOST_AUTO_TEST_CASE(loops) "}\n"; bytes code = compileContract(sourceCode); - unsigned shift = 29; - unsigned boilerplateSize = 42; + unsigned shift = 27; + unsigned boilerplateSize = 40; bytes expectation({byte(Instruction::JUMPDEST), byte(Instruction::JUMPDEST), byte(Instruction::PUSH1), 0x1, From 488db068649a60fe4a4012b0d7b187a2e0062850 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Fri, 14 Nov 2014 11:44:51 +0100 Subject: [PATCH 19/40] Test unexpected storage entries --- test/TestHelper.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/test/TestHelper.cpp b/test/TestHelper.cpp index ea0bf341c..b0935d0b6 100644 --- a/test/TestHelper.cpp +++ b/test/TestHelper.cpp @@ -329,6 +329,12 @@ void checkStorage(map _expectedStore, map _resultStore, BOOST_CHECK_MESSAGE(expectedStoreValue == resultStoreValue, _expectedAddr << ": store[" << expectedStoreKey << "] = " << resultStoreValue << ", expected " << expectedStoreValue); } } + + for (auto&& resultStorePair : _resultStore) + { + if (!_expectedStore.count(resultStorePair.first)) + BOOST_ERROR("unexpected result store key " << resultStorePair.first); + } } void checkLog(LogEntries _resultLogs, LogEntries _expectedLogs) From 79e9becdaa07c5cf6d0da35be6263a8e60d83fd7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Thu, 11 Dec 2014 19:33:49 +0100 Subject: [PATCH 20/40] Report wrong account address in case of unexpected storege key --- test/TestHelper.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/TestHelper.cpp b/test/TestHelper.cpp index b0935d0b6..566c3c53f 100644 --- a/test/TestHelper.cpp +++ b/test/TestHelper.cpp @@ -333,7 +333,7 @@ void checkStorage(map _expectedStore, map _resultStore, for (auto&& resultStorePair : _resultStore) { if (!_expectedStore.count(resultStorePair.first)) - BOOST_ERROR("unexpected result store key " << resultStorePair.first); + BOOST_ERROR(_expectedAddr << ": unexpected store key " << resultStorePair.first); } } From 992abb31edacf5966f3a173134b0418a89331dea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Thu, 11 Dec 2014 20:46:05 +0100 Subject: [PATCH 21/40] Replace spaces with tabs --- test/TestHelper.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/test/TestHelper.cpp b/test/TestHelper.cpp index 566c3c53f..1201cfee2 100644 --- a/test/TestHelper.cpp +++ b/test/TestHelper.cpp @@ -330,11 +330,11 @@ void checkStorage(map _expectedStore, map _resultStore, } } - for (auto&& resultStorePair : _resultStore) - { - if (!_expectedStore.count(resultStorePair.first)) - BOOST_ERROR(_expectedAddr << ": unexpected store key " << resultStorePair.first); - } + for (auto&& resultStorePair : _resultStore) + { + if (!_expectedStore.count(resultStorePair.first)) + BOOST_ERROR(_expectedAddr << ": unexpected store key " << resultStorePair.first); + } } void checkLog(LogEntries _resultLogs, LogEntries _expectedLogs) From e0389c58ba694e82213cc9b99a2e4020cfa0ce03 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Fri, 12 Dec 2014 13:35:05 +0100 Subject: [PATCH 22/40] Reduce verbosity. --- test/whisperTopic.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/whisperTopic.cpp b/test/whisperTopic.cpp index 941c790e5..1c8e74837 100644 --- a/test/whisperTopic.cpp +++ b/test/whisperTopic.cpp @@ -32,7 +32,8 @@ BOOST_AUTO_TEST_SUITE(whisper) BOOST_AUTO_TEST_CASE(topic) { - g_logVerbosity = 20; + cnote << "Testing Whisper..."; + g_logVerbosity = 0; bool started = false; unsigned result = 0; From 25939bc77771ace6776c1d2705d2b264f2998b70 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Fri, 12 Dec 2014 15:31:24 +0100 Subject: [PATCH 23/40] Fix tests. --- test/jsonrpc.cpp | 2 +- test/trie.cpp | 7 +++---- test/whisperTopic.cpp | 4 ++-- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/test/jsonrpc.cpp b/test/jsonrpc.cpp index d17c5a594..20ffc6d54 100644 --- a/test/jsonrpc.cpp +++ b/test/jsonrpc.cpp @@ -19,7 +19,7 @@ * @date 2014 */ -#if ETH_JSONRPC +#if ETH_JSONRPC && 0 #include #include diff --git a/test/trie.cpp b/test/trie.cpp index 67f706917..3f072a6d1 100644 --- a/test/trie.cpp +++ b/test/trie.cpp @@ -59,8 +59,8 @@ BOOST_AUTO_TEST_CASE(trie_tests) cnote << "Testing Trie..."; js::mValue v; - string s = asString(contents(testPath + "/trietest.json")); - BOOST_REQUIRE_MESSAGE(s.length() > 0, "Contents of 'trietest.json' is empty. Have you cloned the 'tests' repo branch develop?"); + string s = asString(contents(testPath + "/trieanyorder.json")); + BOOST_REQUIRE_MESSAGE(s.length() > 0, "Contents of 'trieanyorder.json' is empty. Have you cloned the 'tests' repo branch develop?"); js::read_string(s, v); for (auto& i: v.get_obj()) { @@ -88,12 +88,11 @@ BOOST_AUTO_TEST_CASE(trie_tests) BOOST_REQUIRE(t.check(true)); } BOOST_REQUIRE(!o["root"].is_null()); - BOOST_CHECK_EQUAL(o["root"].get_str(), toHex(t.root().asArray())); + BOOST_CHECK_EQUAL(o["root"].get_str(), "0x" + toHex(t.root().asArray())); } } } - inline h256 stringMapHash256(StringMap const& _s) { return hash256(_s); diff --git a/test/whisperTopic.cpp b/test/whisperTopic.cpp index 1c8e74837..c5e59332d 100644 --- a/test/whisperTopic.cpp +++ b/test/whisperTopic.cpp @@ -33,7 +33,7 @@ BOOST_AUTO_TEST_SUITE(whisper) BOOST_AUTO_TEST_CASE(topic) { cnote << "Testing Whisper..."; - g_logVerbosity = 0; +// g_logVerbosity = 0; bool started = false; unsigned result = 0; @@ -81,7 +81,7 @@ BOOST_AUTO_TEST_CASE(topic) } listener.join(); - g_logVerbosity = 0; +// g_logVerbosity = 0; BOOST_REQUIRE_EQUAL(result, 1 + 9 + 25 + 49 + 81); } From e2cb773dac12ecf058bbe683a3b1b2054f71b0a5 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Fri, 12 Dec 2014 15:44:18 +0100 Subject: [PATCH 24/40] Minor warning fix. --- test/solidityOptimizerTest.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/solidityOptimizerTest.cpp b/test/solidityOptimizerTest.cpp index f8d3b552c..b689fe54e 100644 --- a/test/solidityOptimizerTest.cpp +++ b/test/solidityOptimizerTest.cpp @@ -48,7 +48,7 @@ public: m_optimize = true; bytes optimizedBytecode = compileAndRun(_sourceCode, _value, _contractName); int sizeDiff = nonOptimizedBytecode.size() - optimizedBytecode.size(); - BOOST_CHECK_MESSAGE(sizeDiff >= _expectedSizeDecrease, "Bytecode did only shrink by " + BOOST_CHECK_MESSAGE(sizeDiff >= (int)_expectedSizeDecrease, "Bytecode did only shrink by " + boost::lexical_cast(sizeDiff) + " bytes, expected: " + boost::lexical_cast(_expectedSizeDecrease)); m_optimizedContract = m_contractAddress; From 9e2eb149af7038c3efb1bba1f80cfeb8571b1646 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Fri, 12 Dec 2014 13:33:42 +0100 Subject: [PATCH 25/40] Beginnings of cleaning up the Executive/State code. --- alethzero/MainWin.cpp | 4 +-- eth/main.cpp | 2 +- libdevcore/vector_ref.h | 1 + libethereum/Executive.cpp | 41 +++++++++++++------------ libethereum/Executive.h | 13 +++++--- libethereum/ExtVM.h | 10 +++--- libethereum/State.cpp | 51 ++++++++++++++++++++----------- libethereum/State.h | 4 +-- libevm/ExtVMFace.h | 11 +++++-- libevm/VM.h | 4 +-- test/jsonrpc.cpp | 2 +- test/solidityExecutionFramework.h | 4 +-- test/trie.cpp | 3 +- test/vm.cpp | 8 ++--- test/vm.h | 4 +-- test/whisperTopic.cpp | 5 +-- 16 files changed, 99 insertions(+), 68 deletions(-) diff --git a/alethzero/MainWin.cpp b/alethzero/MainWin.cpp index a240f64e1..0e36c7737 100644 --- a/alethzero/MainWin.cpp +++ b/alethzero/MainWin.cpp @@ -1350,7 +1350,7 @@ void Main::on_debugCurrent_triggered() { unsigned txi = item->data(Qt::UserRole + 1).toInt(); m_executiveState = ethereum()->state(txi + 1, h); - m_currentExecution = unique_ptr(new Executive(m_executiveState)); + m_currentExecution = unique_ptr(new Executive(m_executiveState, 0)); Transaction t = m_executiveState.pending()[txi]; m_executiveState = m_executiveState.fromPending(txi); auto r = t.rlp(); @@ -1855,7 +1855,7 @@ void Main::on_debug_clicked() { Secret s = i.secret(); m_executiveState = ethereum()->postState(); - m_currentExecution = unique_ptr(new Executive(m_executiveState)); + m_currentExecution = unique_ptr(new Executive(m_executiveState, 0)); Transaction t = isCreation() ? Transaction(value(), gasPrice(), ui->gas->value(), m_data, m_executiveState.transactionsFrom(dev::toAddress(s)), s) : Transaction(value(), gasPrice(), ui->gas->value(), fromString(ui->destination->currentText()), m_data, m_executiveState.transactionsFrom(dev::toAddress(s)), s); diff --git a/eth/main.cpp b/eth/main.cpp index 0113fa1a5..49c5fdf56 100644 --- a/eth/main.cpp +++ b/eth/main.cpp @@ -620,7 +620,7 @@ int main(int argc, char** argv) dev::eth::State state =c->state(index + 1,c->blockChain().numberHash(block)); if (index < state.pending().size()) { - Executive e(state); + Executive e(state, 0); Transaction t = state.pending()[index]; state = state.fromPending(index); bytes r = t.rlp(); diff --git a/libdevcore/vector_ref.h b/libdevcore/vector_ref.h index be2fea13c..db46d62f6 100644 --- a/libdevcore/vector_ref.h +++ b/libdevcore/vector_ref.h @@ -40,6 +40,7 @@ public: vector_ref<_T> cropped(size_t _begin, size_t _count = ~size_t(0)) const { if (m_data && _begin + std::max(size_t(0), _count) <= m_count) return vector_ref<_T>(m_data + _begin, _count == ~size_t(0) ? m_count - _begin : _count); else return vector_ref<_T>(); } void retarget(_T const* _d, size_t _s) { m_data = _d; m_count = _s; } void retarget(std::vector<_T> const& _t) { m_data = _t.data(); m_count = _t.size(); } + void copyTo(vector_ref::type> _t) const { memcpy(_t.data(), m_data, std::min(_t.size(), m_count) * sizeof(_T)); } _T* begin() { return m_data; } _T* end() { return m_data + m_count; } diff --git a/libethereum/Executive.cpp b/libethereum/Executive.cpp index 007896d75..c81dd7ed8 100644 --- a/libethereum/Executive.cpp +++ b/libethereum/Executive.cpp @@ -19,12 +19,13 @@ * @date 2014 */ +#include "Executive.h" + #include #include #include #include #include "Interface.h" -#include "Executive.h" #include "State.h" #include "ExtVM.h" using namespace std; @@ -88,11 +89,12 @@ bool Executive::setup(bytesConstRef _rlp) if (m_t.isCreation()) return create(m_sender, m_t.value(), m_t.gasPrice(), m_t.gas() - (u256)gasCost, &m_t.data(), m_sender); else - return call(m_t.receiveAddress(), m_sender, m_t.value(), m_t.gasPrice(), bytesConstRef(&m_t.data()), m_t.gas() - (u256)gasCost, m_sender); + return call(m_t.receiveAddress(), m_t.receiveAddress(), m_sender, m_t.value(), m_t.gasPrice(), bytesConstRef(&m_t.data()), m_t.gas() - (u256)gasCost, m_sender); } -bool Executive::call(Address _receiveAddress, Address _senderAddress, u256 _value, u256 _gasPrice, bytesConstRef _data, u256 _gas, Address _originAddress) +bool Executive::call(Address _receiveAddress, Address _codeAddress, Address _senderAddress, u256 _value, u256 _gasPrice, bytesConstRef _data, u256 _gas, Address _originAddress) { + m_isCreation = false; // cnote << "Transferring" << formatBalance(_value) << "to receiver."; m_s.addBalance(_receiveAddress, _value); @@ -109,11 +111,11 @@ bool Executive::call(Address _receiveAddress, Address _senderAddress, u256 _valu it->second.exec(_data, bytesRef()); return true; } - else if (m_s.addressHasCode(_receiveAddress)) + else if (m_s.addressHasCode(_codeAddress)) { m_vm = VMFactory::create(_gas); bytes const& c = m_s.code(_receiveAddress); - m_ext.reset(new ExtVM(m_s, _receiveAddress, _senderAddress, _originAddress, _value, _gasPrice, _data, &c)); + m_ext = make_shared(m_s, _receiveAddress, _senderAddress, _originAddress, _value, _gasPrice, _data, &c, m_depth); } else m_endGas = _gas; @@ -122,6 +124,8 @@ bool Executive::call(Address _receiveAddress, Address _senderAddress, u256 _valu bool Executive::create(Address _sender, u256 _endowment, u256 _gasPrice, u256 _gas, bytesConstRef _init, Address _origin) { + m_isCreation = true; + // We can allow for the reverted state (i.e. that with which m_ext is constructed) to contain the m_newAddress, since // we delete it explicitly if we decide we need to revert. m_newAddress = right160(sha3(rlpList(_sender, m_s.transactionsFrom(_sender) - 1))); @@ -131,7 +135,7 @@ bool Executive::create(Address _sender, u256 _endowment, u256 _gasPrice, u256 _g // Execute _init. m_vm = VMFactory::create(_gas); - m_ext.reset(new ExtVM(m_s, m_newAddress, _sender, _origin, _endowment, _gasPrice, bytesConstRef(), _init)); + m_ext = make_shared(m_s, m_newAddress, _sender, _origin, _endowment, _gasPrice, bytesConstRef(), _init, m_depth); return _init.empty(); } @@ -163,14 +167,18 @@ bool Executive::go(OnOpFunc const& _onOp) auto sgas = m_vm->gas(); try { - m_out = m_vm->go(*m_ext, _onOp); + m_out = m_vm->go(*m_ext, _onOp, 0); m_endGas = m_vm->gas(); m_endGas += min((m_t.gas() - m_endGas) / 2, m_ext->sub.refunds); m_logs = m_ext->sub.logs; - if (m_out.size() * c_createDataGas <= m_endGas) - m_endGas -= m_out.size() * c_createDataGas; - else - m_out.reset(); + + if (m_isCreation) + { + if (m_out.size() * c_createDataGas <= m_endGas) + m_endGas -= m_out.size() * c_createDataGas; + else + m_out.reset(); + } } catch (StepsDone const&) { @@ -184,12 +192,7 @@ bool Executive::go(OnOpFunc const& _onOp) // Write state out only in the case of a non-excepted transaction. m_ext->revert(); - // Explicitly delete a newly created address - this will still be in the reverted state. -/* if (m_newAddress) - { - m_s.m_cache.erase(m_newAddress); - m_newAddress = Address(); - }*/ + m_excepted = true; } catch (Exception const& _e) { @@ -214,12 +217,10 @@ u256 Executive::gas() const void Executive::finalize(OnOpFunc const&) { if (m_t.isCreation() && !m_ext->sub.suicides.count(m_newAddress)) - { // creation - put code in place. m_s.m_cache[m_newAddress].setCode(m_out); - } -// cnote << "Refunding" << formatBalance(m_endGas * m_ext->gasPrice) << "to origin (=" << m_endGas << "*" << formatBalance(m_ext->gasPrice) << ")"; + // cnote << "Refunding" << formatBalance(m_endGas * m_ext->gasPrice) << "to origin (=" << m_endGas << "*" << formatBalance(m_ext->gasPrice) << ")"; m_s.addBalance(m_sender, m_endGas * m_t.gasPrice()); u256 feesEarned = (m_t.gas() - m_endGas) * m_t.gasPrice(); diff --git a/libethereum/Executive.h b/libethereum/Executive.h index 9e47bbfbf..fa438f025 100644 --- a/libethereum/Executive.h +++ b/libethereum/Executive.h @@ -27,7 +27,6 @@ #include #include #include "Transaction.h" -#include "ExtVM.h" namespace dev { @@ -35,21 +34,23 @@ namespace eth { class State; +class ExtVM; struct Manifest; struct VMTraceChannel: public LogChannel { static const char* name() { return "EVM"; } static const int verbosity = 11; }; + class Executive { public: - Executive(State& _s): m_s(_s) {} + Executive(State& _s, unsigned _level): m_s(_s), m_depth(_level) {} ~Executive() = default; Executive(Executive const&) = delete; void operator=(Executive) = delete; bool setup(bytesConstRef _transaction); bool create(Address _txSender, u256 _endowment, u256 _gasPrice, u256 _gas, bytesConstRef _code, Address _originAddress); - bool call(Address _myAddress, Address _txSender, u256 _txValue, u256 _gasPrice, bytesConstRef _txData, u256 _gas, Address _originAddress); + bool call(Address _myAddress, Address _codeAddress, Address _txSender, u256 _txValue, u256 _gasPrice, bytesConstRef _txData, u256 _gas, Address _originAddress); bool go(OnOpFunc const& _onOp = OnOpFunc()); void finalize(OnOpFunc const& _onOp = OnOpFunc()); u256 gasUsed() const; @@ -63,6 +64,7 @@ public: bytesConstRef out() const { return m_out; } h160 newAddress() const { return m_newAddress; } LogEntries const& logs() const { return m_logs; } + bool excepted() const { return m_excepted; } VMFace const& vm() const { return *m_vm; } State const& state() const { return m_s; } @@ -70,12 +72,15 @@ public: private: State& m_s; - std::unique_ptr m_ext; + std::shared_ptr m_ext; std::unique_ptr m_vm; bytesConstRef m_out; Address m_newAddress; Transaction m_t; + bool m_isCreation; + bool m_excepted = false; + unsigned m_depth = 0; Address m_sender; u256 m_endGas; diff --git a/libethereum/ExtVM.h b/libethereum/ExtVM.h index ad1045d3f..d4937bcd5 100644 --- a/libethereum/ExtVM.h +++ b/libethereum/ExtVM.h @@ -55,17 +55,17 @@ public: virtual bytes const& codeAt(Address _a) override final { return m_s.code(_a); } /// Create a new contract. - virtual h160 create(u256 _endowment, u256* _gas, bytesConstRef _code, OnOpFunc const& _onOp = OnOpFunc()) override final + virtual h160 create(u256 _endowment, u256& io_gas, bytesConstRef _code, OnOpFunc const& _onOp = OnOpFunc()) override final { // Increment associated nonce for sender. m_s.noteSending(myAddress); - return m_s.create(myAddress, _endowment, gasPrice, _gas, _code, origin, &sub, _onOp, depth + 1); + return m_s.create(myAddress, _endowment, gasPrice, io_gas, _code, origin, sub, _onOp, depth + 1); } /// Create a new message call. Leave _myAddressOverride as the default to use the present address as caller. - virtual bool call(Address _receiveAddress, u256 _txValue, bytesConstRef _txData, u256* _gas, bytesRef _out, OnOpFunc const& _onOp = {}, Address _myAddressOverride = {}, Address _codeAddressOverride = {}) override final + virtual bool call(Address _receiveAddress, u256 _txValue, bytesConstRef _txData, u256& io_gas, bytesRef _out, OnOpFunc const& _onOp = {}, Address _myAddressOverride = {}, Address _codeAddressOverride = {}) override final { - return m_s.call(_receiveAddress, _codeAddressOverride ? _codeAddressOverride : _receiveAddress, _myAddressOverride ? _myAddressOverride : myAddress, _txValue, gasPrice, _txData, _gas, _out, origin, &sub, _onOp, depth + 1); + return m_s.call(_receiveAddress, _codeAddressOverride ? _codeAddressOverride : _receiveAddress, _myAddressOverride ? _myAddressOverride : myAddress, _txValue, gasPrice, _txData, io_gas, _out, origin, sub, _onOp, depth + 1); } /// Read address's balance. @@ -86,7 +86,7 @@ public: /// Revert any changes made (by any of the other calls). /// @TODO check call site for the parent manifest being discarded. - virtual void revert() override final { m_s.m_cache = m_origCache; } + virtual void revert() override final { m_s.m_cache = m_origCache; sub.clear(); } State& state() const { return m_s; } diff --git a/libethereum/State.cpp b/libethereum/State.cpp index 1d6ad3480..f2b5d7283 100644 --- a/libethereum/State.cpp +++ b/libethereum/State.cpp @@ -1119,7 +1119,7 @@ u256 State::execute(bytesConstRef _rlp, bytes* o_output, bool _commit) auto h = rootHash(); #endif - Executive e(*this); + Executive e(*this, 0); e.setup(_rlp); u256 startGasUsed = gasUsed(); @@ -1174,8 +1174,24 @@ u256 State::execute(bytesConstRef _rlp, bytes* o_output, bool _commit) return e.gasUsed(); } -bool State::call(Address _receiveAddress, Address _codeAddress, Address _senderAddress, u256 _value, u256 _gasPrice, bytesConstRef _data, u256* _gas, bytesRef _out, Address _originAddress, SubState* o_sub, OnOpFunc const& _onOp, unsigned _level) +bool State::call(Address _receiveAddress, Address _codeAddress, Address _senderAddress, u256 _value, u256 _gasPrice, bytesConstRef _data, u256& io_gas, bytesRef _out, Address _originAddress, SubState& io_sub, OnOpFunc const& _onOp, unsigned _level) { +#if 0 + // TODO: TEST TEST TEST!!! + Executive e(*this, _level); + + if (!e.call(_receiveAddress, _codeAddress, _senderAddress, _value, _gasPrice, _data, io_gas, _originAddress)) + { + e.go(_onOp); + io_sub += e.ext().sub; + } + + e.out().copyTo(_out); + io_gas = e.gas(); + + return !e.excepted(); + +#else if (!_originAddress) _originAddress = _senderAddress; @@ -1186,26 +1202,25 @@ bool State::call(Address _receiveAddress, Address _codeAddress, Address _senderA if (it != c_precompiled.end()) { bigint g = it->second.gas(_data); - if (*_gas < g) + if (io_gas < g) { - *_gas = 0; + io_gas = 0; return false; } - *_gas -= (u256)g; + io_gas -= (u256)g; it->second.exec(_data, _out); } else if (addressHasCode(_codeAddress)) { - auto vm = VMFactory::create(*_gas); + auto vm = VMFactory::create(io_gas); ExtVM evm(*this, _receiveAddress, _senderAddress, _originAddress, _value, _gasPrice, _data, &code(_codeAddress), _level); try { auto out = vm->go(evm, _onOp); memcpy(_out.data(), out.data(), std::min(out.size(), _out.size())); - if (o_sub) - *o_sub += evm.sub; - *_gas = vm->gas(); + io_sub += evm.sub; + io_gas = vm->gas(); // Write state out only in the case of a non-excepted transaction. return true; } @@ -1213,7 +1228,7 @@ bool State::call(Address _receiveAddress, Address _codeAddress, Address _senderA { clog(StateChat) << "Safe VM Exception: " << diagnostic_information(_e); evm.revert(); - *_gas = 0; + io_gas = 0; return false; } catch (Exception const& _e) @@ -1232,9 +1247,10 @@ bool State::call(Address _receiveAddress, Address _codeAddress, Address _senderA } } return true; +#endif } -h160 State::create(Address _sender, u256 _endowment, u256 _gasPrice, u256* _gas, bytesConstRef _code, Address _origin, SubState* o_sub, OnOpFunc const& _onOp, unsigned _level) +h160 State::create(Address _sender, u256 _endowment, u256 _gasPrice, u256& io_gas, bytesConstRef _code, Address _origin, SubState& io_sub, OnOpFunc const& _onOp, unsigned _level) { if (!_origin) _origin = _sender; @@ -1245,19 +1261,18 @@ h160 State::create(Address _sender, u256 _endowment, u256 _gasPrice, u256* _gas, m_cache[newAddress] = Account(balance(newAddress) + _endowment, Account::ContractConception); // Execute init code. - auto vm = VMFactory::create(*_gas); + auto vm = VMFactory::create(io_gas); ExtVM evm(*this, newAddress, _sender, _origin, _endowment, _gasPrice, bytesConstRef(), _code, _level); bytesConstRef out; try { out = vm->go(evm, _onOp); - if (o_sub) - *o_sub += evm.sub; - *_gas = vm->gas(); + io_sub += evm.sub; + io_gas = vm->gas(); - if (out.size() * c_createDataGas <= *_gas) - *_gas -= out.size() * c_createDataGas; + if (out.size() * c_createDataGas <= io_gas) + io_gas -= out.size() * c_createDataGas; else out.reset(); @@ -1269,7 +1284,7 @@ h160 State::create(Address _sender, u256 _endowment, u256 _gasPrice, u256* _gas, { clog(StateChat) << "Safe VM Exception: " << diagnostic_information(_e); evm.revert(); - *_gas = 0; + io_gas = 0; } catch (Exception const& _e) { diff --git a/libethereum/State.h b/libethereum/State.h index 10d73f671..dd83a83ee 100644 --- a/libethereum/State.h +++ b/libethereum/State.h @@ -276,12 +276,12 @@ private: // We assume all instrinsic fees are paid up before this point. /// Execute a contract-creation transaction. - h160 create(Address _txSender, u256 _endowment, u256 _gasPrice, u256* _gas, bytesConstRef _code, Address _originAddress = Address(), SubState* o_sub = nullptr, OnOpFunc const& _onOp = OnOpFunc(), unsigned _level = 0); + h160 create(Address _txSender, u256 _endowment, u256 _gasPrice, u256& io_gas, bytesConstRef _code, Address _originAddress, SubState& io_sub, OnOpFunc const& _onOp, unsigned _level); /// Execute a call. /// @a _gas points to the amount of gas to use for the call, and will lower it accordingly. /// @returns false if the call ran out of gas before completion. true otherwise. - bool call(Address _myAddress, Address _codeAddress, Address _txSender, u256 _txValue, u256 _gasPrice, bytesConstRef _txData, u256* _gas, bytesRef _out, Address _originAddress = Address(), SubState* o_sub = nullptr, OnOpFunc const& _onOp = OnOpFunc(), unsigned _level = 0); + bool call(Address _myAddress, Address _codeAddress, Address _txSender, u256 _txValue, u256 _gasPrice, bytesConstRef _txData, u256& io_gas, bytesRef _out, Address _originAddress, SubState& io_sub, OnOpFunc const& _onOp, unsigned _level); /// Sets m_currentBlock to a clean state, (i.e. no change from m_previousBlock). void resetCurrent(); diff --git a/libevm/ExtVMFace.h b/libevm/ExtVMFace.h index 24c7dfcda..b8c4184ab 100644 --- a/libevm/ExtVMFace.h +++ b/libevm/ExtVMFace.h @@ -83,6 +83,13 @@ struct SubState logs += _s.logs; return *this; } + + void clear() + { + suicides.clear(); + logs.clear(); + refunds = 0; + } }; class ExtVMFace; @@ -129,10 +136,10 @@ public: virtual void suicide(Address) { sub.suicides.insert(myAddress); } /// Create a new (contract) account. - virtual h160 create(u256, u256*, bytesConstRef, OnOpFunc const&) { return h160(); } + virtual h160 create(u256, u256&, bytesConstRef, OnOpFunc const&) { return h160(); } /// Make a new message call. - virtual bool call(Address, u256, bytesConstRef, u256*, bytesRef, OnOpFunc const&, Address, Address) { return false; } + virtual bool call(Address, u256, bytesConstRef, u256&, bytesRef, OnOpFunc const&, Address, Address) { return false; } /// Revert any changes made (by any of the other calls). virtual void log(h256s&& _topics, bytesConstRef _data) { sub.logs.push_back(LogEntry(myAddress, std::move(_topics), _data.toBytes())); } diff --git a/libevm/VM.h b/libevm/VM.h index 6f3229920..09a1f6d26 100644 --- a/libevm/VM.h +++ b/libevm/VM.h @@ -798,7 +798,7 @@ inline bytesConstRef VM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _st if (_ext.depth == 1024) BOOST_THROW_EXCEPTION(OutOfGas()); _ext.subBalance(endowment); - m_stack.push_back((u160)_ext.create(endowment, &m_gas, bytesConstRef(m_temp.data() + initOff, initSize), _onOp)); + m_stack.push_back((u160)_ext.create(endowment, m_gas, bytesConstRef(m_temp.data() + initOff, initSize), _onOp)); } else m_stack.push_back(0); @@ -828,7 +828,7 @@ inline bytesConstRef VM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _st if (_ext.depth == 1024) BOOST_THROW_EXCEPTION(OutOfGas()); _ext.subBalance(value); - m_stack.push_back(_ext.call(inst == Instruction::CALL ? receiveAddress : _ext.myAddress, value, bytesConstRef(m_temp.data() + inOff, inSize), &gas, bytesRef(m_temp.data() + outOff, outSize), _onOp, Address(), receiveAddress)); + m_stack.push_back(_ext.call(inst == Instruction::CALL ? receiveAddress : _ext.myAddress, value, bytesConstRef(m_temp.data() + inOff, inSize), gas, bytesRef(m_temp.data() + outOff, outSize), _onOp, Address(), receiveAddress)); } else m_stack.push_back(0); diff --git a/test/jsonrpc.cpp b/test/jsonrpc.cpp index 20ffc6d54..970957b52 100644 --- a/test/jsonrpc.cpp +++ b/test/jsonrpc.cpp @@ -43,7 +43,7 @@ using namespace dev; using namespace dev::eth; namespace js = json_spirit; -WebThreeDirect *web3; +WebThreeDirect* web3; unique_ptr jsonrpcServer; unique_ptr jsonrpcClient; diff --git a/test/solidityExecutionFramework.h b/test/solidityExecutionFramework.h index 9d40e8a46..91ee7ad6a 100644 --- a/test/solidityExecutionFramework.h +++ b/test/solidityExecutionFramework.h @@ -117,7 +117,7 @@ private: void sendMessage(bytes const& _data, bool _isCreation, u256 const& _value = 0) { m_state.addBalance(m_sender, _value); // just in case - eth::Executive executive(m_state); + eth::Executive executive(m_state, 0); eth::Transaction t = _isCreation ? eth::Transaction(_value, m_gasPrice, m_gas, _data, 0, KeyPair::create().sec()) : eth::Transaction(_value, m_gasPrice, m_gas, m_contractAddress, _data, 0, KeyPair::create().sec()); bytes transactionRLP = t.rlp(); @@ -137,7 +137,7 @@ private: else { BOOST_REQUIRE(m_state.addressHasCode(m_contractAddress)); - BOOST_REQUIRE(!executive.call(m_contractAddress, m_sender, _value, m_gasPrice, &_data, m_gas, m_sender)); + BOOST_REQUIRE(!executive.call(m_contractAddress, m_contractAddress, m_sender, _value, m_gasPrice, &_data, m_gas, m_sender)); } BOOST_REQUIRE(executive.go()); m_state.noteSending(m_sender); diff --git a/test/trie.cpp b/test/trie.cpp index 3f072a6d1..0cf87c212 100644 --- a/test/trie.cpp +++ b/test/trie.cpp @@ -54,7 +54,6 @@ BOOST_AUTO_TEST_CASE(trie_tests) { string testPath = test::getTestPath(); - testPath += "/TrieTests"; cnote << "Testing Trie..."; @@ -245,6 +244,7 @@ BOOST_AUTO_TEST_CASE(moreTrieTests) BOOST_AUTO_TEST_CASE(trieLowerBound) { cnote << "Stress-testing Trie.lower_bound..."; + if (0) { MemoryDB dm; EnforceRefs e(dm, true); @@ -290,6 +290,7 @@ BOOST_AUTO_TEST_CASE(trieLowerBound) BOOST_AUTO_TEST_CASE(trieStess) { cnote << "Stress-testing Trie..."; + if (0) { MemoryDB m; MemoryDB dm; diff --git a/test/vm.cpp b/test/vm.cpp index d8e85383c..f05981d9e 100644 --- a/test/vm.cpp +++ b/test/vm.cpp @@ -34,18 +34,18 @@ using namespace dev::test; FakeExtVM::FakeExtVM(eth::BlockInfo const& _previousBlock, eth::BlockInfo const& _currentBlock, unsigned _depth): /// TODO: XXX: remove the default argument & fix. ExtVMFace(Address(), Address(), Address(), 0, 1, bytesConstRef(), bytes(), _previousBlock, _currentBlock, _depth) {} -h160 FakeExtVM::create(u256 _endowment, u256* _gas, bytesConstRef _init, OnOpFunc const&) +h160 FakeExtVM::create(u256 _endowment, u256& io_gas, bytesConstRef _init, OnOpFunc const&) { Address na = right160(sha3(rlpList(myAddress, get<1>(addresses[myAddress])))); - Transaction t(_endowment, gasPrice, *_gas, _init.toBytes()); + Transaction t(_endowment, gasPrice, io_gas, _init.toBytes()); callcreates.push_back(t); return na; } -bool FakeExtVM::call(Address _receiveAddress, u256 _value, bytesConstRef _data, u256* _gas, bytesRef _out, OnOpFunc const&, Address _myAddressOverride, Address _codeAddressOverride) +bool FakeExtVM::call(Address _receiveAddress, u256 _value, bytesConstRef _data, u256& io_gas, bytesRef _out, OnOpFunc const&, Address _myAddressOverride, Address _codeAddressOverride) { - Transaction t(_value, gasPrice, *_gas, _receiveAddress, _data.toVector()); + Transaction t(_value, gasPrice, io_gas, _receiveAddress, _data.toVector()); callcreates.push_back(t); (void)_out; (void)_myAddressOverride; diff --git a/test/vm.h b/test/vm.h index 3d4b88d54..ff948cbf8 100644 --- a/test/vm.h +++ b/test/vm.h @@ -55,8 +55,8 @@ public: virtual u256 txCount(Address _a) override { return std::get<1>(addresses[_a]); } virtual void suicide(Address _a) override { std::get<0>(addresses[_a]) += std::get<0>(addresses[myAddress]); addresses.erase(myAddress); } virtual bytes const& codeAt(Address _a) override { return std::get<3>(addresses[_a]); } - virtual h160 create(u256 _endowment, u256* _gas, bytesConstRef _init, eth::OnOpFunc const&) override; - virtual bool call(Address _receiveAddress, u256 _value, bytesConstRef _data, u256* _gas, bytesRef _out, eth::OnOpFunc const&, Address, Address) override; + virtual h160 create(u256 _endowment, u256& io_gas, bytesConstRef _init, eth::OnOpFunc const&) override; + virtual bool call(Address _receiveAddress, u256 _value, bytesConstRef _data, u256& io_gas, bytesRef _out, eth::OnOpFunc const&, Address, Address) override; void setTransaction(Address _caller, u256 _value, u256 _gasPrice, bytes const& _data); void setContract(Address _myAddress, u256 _myBalance, u256 _myNonce, std::map const& _storage, bytes const& _code); void set(Address _a, u256 _myBalance, u256 _myNonce, std::map const& _storage, bytes const& _code); diff --git a/test/whisperTopic.cpp b/test/whisperTopic.cpp index c5e59332d..79adf3d6a 100644 --- a/test/whisperTopic.cpp +++ b/test/whisperTopic.cpp @@ -33,7 +33,8 @@ BOOST_AUTO_TEST_SUITE(whisper) BOOST_AUTO_TEST_CASE(topic) { cnote << "Testing Whisper..."; -// g_logVerbosity = 0; + auto oldLogVerbosity = g_logVerbosity; + g_logVerbosity = 0; bool started = false; unsigned result = 0; @@ -81,7 +82,7 @@ BOOST_AUTO_TEST_CASE(topic) } listener.join(); -// g_logVerbosity = 0; + g_logVerbosity = oldLogVerbosity; BOOST_REQUIRE_EQUAL(result, 1 + 9 + 25 + 49 + 81); } From ac5a0ff1fb6dce456b2a3484c322cc498dfdb51c Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Fri, 12 Dec 2014 16:04:34 +0100 Subject: [PATCH 26/40] Minor fix for execution. --- libethereum/Executive.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libethereum/Executive.cpp b/libethereum/Executive.cpp index c81dd7ed8..9d4e9c1e6 100644 --- a/libethereum/Executive.cpp +++ b/libethereum/Executive.cpp @@ -167,7 +167,7 @@ bool Executive::go(OnOpFunc const& _onOp) auto sgas = m_vm->gas(); try { - m_out = m_vm->go(*m_ext, _onOp, 0); + m_out = m_vm->go(*m_ext, _onOp); m_endGas = m_vm->gas(); m_endGas += min((m_t.gas() - m_endGas) / 2, m_ext->sub.refunds); m_logs = m_ext->sub.logs; From 5cc2152dd02909084a632544e2aa70bb23d31704 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Fri, 12 Dec 2014 17:22:32 +0100 Subject: [PATCH 27/40] Cleanups. --- libethereum/Executive.cpp | 25 +++++++++++++++---------- libethereum/Executive.h | 2 +- libethereum/ExtVM.h | 2 +- libethereum/State.cpp | 3 +-- libevm/VM.h | 2 +- test/state.cpp | 2 +- 6 files changed, 20 insertions(+), 16 deletions(-) diff --git a/libethereum/Executive.cpp b/libethereum/Executive.cpp index 9d4e9c1e6..e5a8e6b11 100644 --- a/libethereum/Executive.cpp +++ b/libethereum/Executive.cpp @@ -105,11 +105,13 @@ bool Executive::call(Address _receiveAddress, Address _codeAddress, Address _sen if (_gas < g) { m_endGas = 0; - return false; + m_excepted = true; + } + else + { + m_endGas = (u256)(_gas - g); + it->second.exec(_data, bytesRef()); } - m_endGas = (u256)(_gas - g); - it->second.exec(_data, bytesRef()); - return true; } else if (m_s.addressHasCode(_codeAddress)) { @@ -169,8 +171,6 @@ bool Executive::go(OnOpFunc const& _onOp) { m_out = m_vm->go(*m_ext, _onOp); m_endGas = m_vm->gas(); - m_endGas += min((m_t.gas() - m_endGas) / 2, m_ext->sub.refunds); - m_logs = m_ext->sub.logs; if (m_isCreation) { @@ -188,11 +188,10 @@ bool Executive::go(OnOpFunc const& _onOp) { clog(StateChat) << "Safe VM Exception: " << diagnostic_information(_e); m_endGas = 0;//m_vm->gas(); + m_excepted = true; // Write state out only in the case of a non-excepted transaction. m_ext->revert(); - - m_excepted = true; } catch (Exception const& _e) { @@ -209,10 +208,10 @@ bool Executive::go(OnOpFunc const& _onOp) return true; } -u256 Executive::gas() const +/*u256 Executive::gas() const { return m_vm ? m_vm->gas() : m_endGas; -} +}*/ void Executive::finalize(OnOpFunc const&) { @@ -220,6 +219,9 @@ void Executive::finalize(OnOpFunc const&) // creation - put code in place. m_s.m_cache[m_newAddress].setCode(m_out); + // SSTORE refunds. + m_endGas += min((m_t.gas() - m_endGas) / 2, m_ext->sub.refunds); + // cnote << "Refunding" << formatBalance(m_endGas * m_ext->gasPrice) << "to origin (=" << m_endGas << "*" << formatBalance(m_ext->gasPrice) << ")"; m_s.addBalance(m_sender, m_endGas * m_t.gasPrice()); @@ -231,4 +233,7 @@ void Executive::finalize(OnOpFunc const&) if (m_ext) for (auto a: m_ext->sub.suicides) m_s.m_cache[a].kill(); + + // Logs + m_logs = m_ext->sub.logs; } diff --git a/libethereum/Executive.h b/libethereum/Executive.h index fa438f025..068439d87 100644 --- a/libethereum/Executive.h +++ b/libethereum/Executive.h @@ -59,7 +59,7 @@ public: Transaction const& t() const { return m_t; } - u256 gas() const; + u256 endGas() const { return m_endGas; } bytesConstRef out() const { return m_out; } h160 newAddress() const { return m_newAddress; } diff --git a/libethereum/ExtVM.h b/libethereum/ExtVM.h index d4937bcd5..8c04ff113 100644 --- a/libethereum/ExtVM.h +++ b/libethereum/ExtVM.h @@ -55,7 +55,7 @@ public: virtual bytes const& codeAt(Address _a) override final { return m_s.code(_a); } /// Create a new contract. - virtual h160 create(u256 _endowment, u256& io_gas, bytesConstRef _code, OnOpFunc const& _onOp = OnOpFunc()) override final + virtual h160 create(u256 _endowment, u256& io_gas, bytesConstRef _code, OnOpFunc const& _onOp = {}) override final { // Increment associated nonce for sender. m_s.noteSending(myAddress); diff --git a/libethereum/State.cpp b/libethereum/State.cpp index f2b5d7283..07dcccf2a 100644 --- a/libethereum/State.cpp +++ b/libethereum/State.cpp @@ -1187,10 +1187,9 @@ bool State::call(Address _receiveAddress, Address _codeAddress, Address _senderA } e.out().copyTo(_out); - io_gas = e.gas(); + io_gas = e.endGas(); return !e.excepted(); - #else if (!_originAddress) _originAddress = _senderAddress; diff --git a/libevm/VM.h b/libevm/VM.h index 09a1f6d26..3eb330fcd 100644 --- a/libevm/VM.h +++ b/libevm/VM.h @@ -828,7 +828,7 @@ inline bytesConstRef VM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _st if (_ext.depth == 1024) BOOST_THROW_EXCEPTION(OutOfGas()); _ext.subBalance(value); - m_stack.push_back(_ext.call(inst == Instruction::CALL ? receiveAddress : _ext.myAddress, value, bytesConstRef(m_temp.data() + inOff, inSize), gas, bytesRef(m_temp.data() + outOff, outSize), _onOp, Address(), receiveAddress)); + m_stack.push_back(_ext.call(inst == Instruction::CALL ? receiveAddress : _ext.myAddress, value, bytesConstRef(m_temp.data() + inOff, inSize), gas, bytesRef(m_temp.data() + outOff, outSize), _onOp, {}, receiveAddress)); } else m_stack.push_back(0); diff --git a/test/state.cpp b/test/state.cpp index 07c8373e2..3fe0fe30b 100644 --- a/test/state.cpp +++ b/test/state.cpp @@ -45,7 +45,7 @@ void doStateTests(json_spirit::mValue& v, bool _fillin) { for (auto& i: v.get_obj()) { - cnote << i.first; + cerr << i.first << endl; mObject& o = i.second.get_obj(); BOOST_REQUIRE(o.count("env") > 0); From daa6b8f84951978d08458306cafa12a1d0f2ec72 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Fri, 12 Dec 2014 19:44:09 +0100 Subject: [PATCH 28/40] Refactor state to use executive for calls. --- libethereum/Executive.cpp | 11 ++++++----- libethereum/Executive.h | 3 ++- libethereum/State.cpp | 32 ++++++++++++-------------------- libethereum/State.h | 2 +- 4 files changed, 21 insertions(+), 27 deletions(-) diff --git a/libethereum/Executive.cpp b/libethereum/Executive.cpp index e5a8e6b11..d568996c3 100644 --- a/libethereum/Executive.cpp +++ b/libethereum/Executive.cpp @@ -98,7 +98,7 @@ bool Executive::call(Address _receiveAddress, Address _codeAddress, Address _sen // cnote << "Transferring" << formatBalance(_value) << "to receiver."; m_s.addBalance(_receiveAddress, _value); - auto it = !(_receiveAddress & ~h160(0xffffffff)) ? State::precompiled().find((unsigned)(u160)_receiveAddress) : State::precompiled().end(); + auto it = !(_codeAddress & ~h160(0xffffffff)) ? State::precompiled().find((unsigned)(u160)_codeAddress) : State::precompiled().end(); if (it != State::precompiled().end()) { bigint g = it->second.gas(_data); @@ -110,13 +110,14 @@ bool Executive::call(Address _receiveAddress, Address _codeAddress, Address _sen else { m_endGas = (u256)(_gas - g); - it->second.exec(_data, bytesRef()); + m_precompiledOut = it->second.exec(_data); + m_out = &m_precompiledOut; } } else if (m_s.addressHasCode(_codeAddress)) { m_vm = VMFactory::create(_gas); - bytes const& c = m_s.code(_receiveAddress); + bytes const& c = m_s.code(_codeAddress); m_ext = make_shared(m_s, _receiveAddress, _senderAddress, _originAddress, _value, _gasPrice, _data, &c, m_depth); } else @@ -166,7 +167,7 @@ bool Executive::go(OnOpFunc const& _onOp) if (m_vm) { boost::timer t; - auto sgas = m_vm->gas(); +// auto sgas = m_vm->gas(); try { m_out = m_vm->go(*m_ext, _onOp); @@ -203,7 +204,7 @@ bool Executive::go(OnOpFunc const& _onOp) // TODO: AUDIT: check that this can never reasonably happen. Consider what to do if it does. cwarn << "Unexpected std::exception in VM. This is probably unrecoverable. " << _e.what(); } - cnote << "VM took:" << t.elapsed() << "; gas used: " << (sgas - m_endGas); +// cnote << "VM took:" << t.elapsed() << "; gas used: " << (sgas - m_endGas); } return true; } diff --git a/libethereum/Executive.h b/libethereum/Executive.h index 068439d87..d6de1491e 100644 --- a/libethereum/Executive.h +++ b/libethereum/Executive.h @@ -74,7 +74,8 @@ private: State& m_s; std::shared_ptr m_ext; std::unique_ptr m_vm; - bytesConstRef m_out; + bytes m_precompiledOut; ///< Used for the output when there is no VM for a contract (i.e. precompiled). + bytesConstRef m_out; ///< Holds the copyable output. Address m_newAddress; Transaction m_t; diff --git a/libethereum/State.cpp b/libethereum/State.cpp index 07dcccf2a..65f3cdeca 100644 --- a/libethereum/State.cpp +++ b/libethereum/State.cpp @@ -42,7 +42,7 @@ using namespace dev::eth; static const u256 c_blockReward = 1500 * finney; -void ecrecoverCode(bytesConstRef _in, bytesRef _out) +bytes ecrecoverCode(bytesConstRef _in) { struct inType { @@ -54,38 +54,35 @@ void ecrecoverCode(bytesConstRef _in, bytesRef _out) memcpy(&in, _in.data(), min(_in.size(), sizeof(in))); - memset(_out.data(), 0, _out.size()); + h256 ret; + if ((u256)in.v > 28) - return; + return ret.asBytes(); SignatureStruct sig{in.r, in.s, (byte)((int)(u256)in.v - 27)}; if (!sig.isValid()) - return; + return ret.asBytes(); - h256 ret; byte pubkey[65]; int pubkeylen = 65; secp256k1_start(); if (secp256k1_ecdsa_recover_compact(in.hash.data(), 32, in.r.data(), pubkey, &pubkeylen, 0, (int)(u256)in.v - 27)) ret = dev::sha3(bytesConstRef(&(pubkey[1]), 64)); - memset(ret.data(), 0, 12); - memcpy(_out.data(), &ret, min(_out.size(), sizeof(ret))); + return ret.asBytes(); } -void sha256Code(bytesConstRef _in, bytesRef _out) +bytes sha256Code(bytesConstRef _in) { h256 ret; sha256(_in, bytesRef(ret.data(), 32)); - memcpy(_out.data(), &ret, min(_out.size(), sizeof(ret))); + return ret.asBytes(); } -void ripemd160Code(bytesConstRef _in, bytesRef _out) +bytes ripemd160Code(bytesConstRef _in) { h256 ret; ripemd160(_in, bytesRef(ret.data(), 32)); - memset(_out.data(), 0, std::min(12, _out.size())); - if (_out.size() > 12) - memcpy(_out.data() + 12, &ret, min(_out.size() - 12, sizeof(ret))); + return ret.asBytes(); } const std::map State::c_precompiled = @@ -1176,24 +1173,19 @@ u256 State::execute(bytesConstRef _rlp, bytes* o_output, bool _commit) bool State::call(Address _receiveAddress, Address _codeAddress, Address _senderAddress, u256 _value, u256 _gasPrice, bytesConstRef _data, u256& io_gas, bytesRef _out, Address _originAddress, SubState& io_sub, OnOpFunc const& _onOp, unsigned _level) { -#if 0 +#if 1 // TODO: TEST TEST TEST!!! Executive e(*this, _level); - if (!e.call(_receiveAddress, _codeAddress, _senderAddress, _value, _gasPrice, _data, io_gas, _originAddress)) { e.go(_onOp); io_sub += e.ext().sub; } - - e.out().copyTo(_out); io_gas = e.endGas(); + e.out().copyTo(_out); return !e.excepted(); #else - if (!_originAddress) - _originAddress = _senderAddress; - // cnote << "Transferring" << formatBalance(_value) << "to receiver."; addBalance(_receiveAddress, _value); diff --git a/libethereum/State.h b/libethereum/State.h index dd83a83ee..744f82e10 100644 --- a/libethereum/State.h +++ b/libethereum/State.h @@ -55,7 +55,7 @@ struct StateDetail: public LogChannel { static const char* name() { return "/S/" struct PrecompiledAddress { std::function gas; - std::function exec; + std::function exec; }; /** From 5412dbb9e8ba7ad13f21bebe0cb088322b07a6e2 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Fri, 12 Dec 2014 20:38:49 +0100 Subject: [PATCH 29/40] Remove old code, refactor State::call, State::create to use Executive. --- libethereum/Executive.cpp | 5 +- libethereum/Executive.h | 2 +- libethereum/State.cpp | 120 ++++---------------------------------- 3 files changed, 14 insertions(+), 113 deletions(-) diff --git a/libethereum/Executive.cpp b/libethereum/Executive.cpp index d568996c3..deb3da33e 100644 --- a/libethereum/Executive.cpp +++ b/libethereum/Executive.cpp @@ -179,6 +179,7 @@ bool Executive::go(OnOpFunc const& _onOp) m_endGas -= m_out.size() * c_createDataGas; else m_out.reset(); + m_s.m_cache[m_newAddress].setCode(m_out); } } catch (StepsDone const&) @@ -216,10 +217,6 @@ bool Executive::go(OnOpFunc const& _onOp) void Executive::finalize(OnOpFunc const&) { - if (m_t.isCreation() && !m_ext->sub.suicides.count(m_newAddress)) - // creation - put code in place. - m_s.m_cache[m_newAddress].setCode(m_out); - // SSTORE refunds. m_endGas += min((m_t.gas() - m_endGas) / 2, m_ext->sub.refunds); diff --git a/libethereum/Executive.h b/libethereum/Executive.h index d6de1491e..c5baada1d 100644 --- a/libethereum/Executive.h +++ b/libethereum/Executive.h @@ -67,8 +67,8 @@ public: bool excepted() const { return m_excepted; } VMFace const& vm() const { return *m_vm; } - State const& state() const { return m_s; } ExtVM const& ext() const { return *m_ext; } + State const& state() const { return m_s; } private: State& m_s; diff --git a/libethereum/State.cpp b/libethereum/State.cpp index 65f3cdeca..bad41d0a5 100644 --- a/libethereum/State.cpp +++ b/libethereum/State.cpp @@ -73,16 +73,16 @@ bytes ecrecoverCode(bytesConstRef _in) bytes sha256Code(bytesConstRef _in) { - h256 ret; - sha256(_in, bytesRef(ret.data(), 32)); - return ret.asBytes(); + bytes ret(32); + sha256(_in, &ret); + return ret; } bytes ripemd160Code(bytesConstRef _in) { - h256 ret; - ripemd160(_in, bytesRef(ret.data(), 32)); - return ret.asBytes(); + bytes ret(32); + ripemd160(_in, &ret); + return ret; } const std::map State::c_precompiled = @@ -1173,8 +1173,6 @@ u256 State::execute(bytesConstRef _rlp, bytes* o_output, bool _commit) bool State::call(Address _receiveAddress, Address _codeAddress, Address _senderAddress, u256 _value, u256 _gasPrice, bytesConstRef _data, u256& io_gas, bytesRef _out, Address _originAddress, SubState& io_sub, OnOpFunc const& _onOp, unsigned _level) { -#if 1 - // TODO: TEST TEST TEST!!! Executive e(*this, _level); if (!e.call(_receiveAddress, _codeAddress, _senderAddress, _value, _gasPrice, _data, io_gas, _originAddress)) { @@ -1185,112 +1183,18 @@ bool State::call(Address _receiveAddress, Address _codeAddress, Address _senderA e.out().copyTo(_out); return !e.excepted(); -#else -// cnote << "Transferring" << formatBalance(_value) << "to receiver."; - addBalance(_receiveAddress, _value); - - auto it = !(_codeAddress & ~h160(0xffffffff)) ? c_precompiled.find((unsigned)(u160)_codeAddress) : c_precompiled.end(); - if (it != c_precompiled.end()) - { - bigint g = it->second.gas(_data); - if (io_gas < g) - { - io_gas = 0; - return false; - } - - io_gas -= (u256)g; - it->second.exec(_data, _out); - } - else if (addressHasCode(_codeAddress)) - { - auto vm = VMFactory::create(io_gas); - ExtVM evm(*this, _receiveAddress, _senderAddress, _originAddress, _value, _gasPrice, _data, &code(_codeAddress), _level); - try - { - auto out = vm->go(evm, _onOp); - memcpy(_out.data(), out.data(), std::min(out.size(), _out.size())); - io_sub += evm.sub; - io_gas = vm->gas(); - // Write state out only in the case of a non-excepted transaction. - return true; - } - catch (VMException const& _e) - { - clog(StateChat) << "Safe VM Exception: " << diagnostic_information(_e); - evm.revert(); - io_gas = 0; - return false; - } - catch (Exception const& _e) - { - cwarn << "Unexpected exception in VM: " << diagnostic_information(_e) << ". This is exceptionally bad."; - // TODO: use fallback known-safe VM. - // AUDIT: THIS SHOULD NEVER HAPPEN! PROVE IT! - throw; - } - catch (std::exception const& _e) - { - cwarn << "Unexpected exception in VM: " << _e.what() << ". This is exceptionally bad."; - // TODO: use fallback known-safe VM. - // AUDIT: THIS SHOULD NEVER HAPPEN! PROVE IT! - throw; - } - } - return true; -#endif } h160 State::create(Address _sender, u256 _endowment, u256 _gasPrice, u256& io_gas, bytesConstRef _code, Address _origin, SubState& io_sub, OnOpFunc const& _onOp, unsigned _level) { - if (!_origin) - _origin = _sender; - - Address newAddress = right160(sha3(rlpList(_sender, transactionsFrom(_sender) - 1))); - - // Set up new account... - m_cache[newAddress] = Account(balance(newAddress) + _endowment, Account::ContractConception); - - // Execute init code. - auto vm = VMFactory::create(io_gas); - ExtVM evm(*this, newAddress, _sender, _origin, _endowment, _gasPrice, bytesConstRef(), _code, _level); - bytesConstRef out; - - try - { - out = vm->go(evm, _onOp); - io_sub += evm.sub; - io_gas = vm->gas(); - - if (out.size() * c_createDataGas <= io_gas) - io_gas -= out.size() * c_createDataGas; - else - out.reset(); - - // Set code. - if (!evm.sub.suicides.count(newAddress)) - m_cache[newAddress].setCode(out); - } - catch (VMException const& _e) - { - clog(StateChat) << "Safe VM Exception: " << diagnostic_information(_e); - evm.revert(); - io_gas = 0; - } - catch (Exception const& _e) - { - // TODO: AUDIT: check that this can never reasonably happen. Consider what to do if it does. - cwarn << "Unexpected exception in VM. There may be a bug in this implementation. " << diagnostic_information(_e); - throw; - } - catch (std::exception const& _e) + Executive e(*this, _level); + if (!e.create(_sender, _endowment, _gasPrice, io_gas, _code, _origin)) { - // TODO: AUDIT: check that this can never reasonably happen. Consider what to do if it does. - cwarn << "Unexpected std::exception in VM. This is probably unrecoverable. " << _e.what(); - throw; + e.go(_onOp); + io_sub += e.ext().sub; } - - return newAddress; + io_gas = e.endGas(); + return e.newAddress(); } State State::fromPending(unsigned _i) const From aa25784764e2bb91626bd4d8fc758072b207c5dc Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Fri, 12 Dec 2014 20:44:36 +0100 Subject: [PATCH 30/40] Fix alignment of RIPEMD160. --- libethereum/State.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libethereum/State.cpp b/libethereum/State.cpp index bad41d0a5..7ca973e05 100644 --- a/libethereum/State.cpp +++ b/libethereum/State.cpp @@ -82,6 +82,9 @@ bytes ripemd160Code(bytesConstRef _in) { bytes ret(32); ripemd160(_in, &ret); + // leaves the 20-byte hash left-aligned. we want it right-aligned: + memmove(ret.data() + 12, ret.data(), 20); + memset(ret.data(), 0, 12); return ret; } From ede878cefde5b6b77bf11d39203e5092238a9832 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Fri, 12 Dec 2014 20:49:28 +0100 Subject: [PATCH 31/40] Style fix. --- test/TestHelper.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/test/TestHelper.cpp b/test/TestHelper.cpp index 1201cfee2..ff8a0d40c 100644 --- a/test/TestHelper.cpp +++ b/test/TestHelper.cpp @@ -331,10 +331,8 @@ void checkStorage(map _expectedStore, map _resultStore, } for (auto&& resultStorePair : _resultStore) - { if (!_expectedStore.count(resultStorePair.first)) BOOST_ERROR(_expectedAddr << ": unexpected store key " << resultStorePair.first); - } } void checkLog(LogEntries _resultLogs, LogEntries _expectedLogs) From a4bf3c645cba4697bc42df5f573747df911b82b4 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Fri, 12 Dec 2014 21:02:47 +0100 Subject: [PATCH 32/40] Finally! Remove two horrible, nasty, lingering, confusing, hacky calls in State. --- libethereum/ExtVM.cpp | 36 ++++++++++++++++++++++++++++++++++-- libethereum/ExtVM.h | 12 ++---------- libethereum/State.cpp | 26 -------------------------- libethereum/State.h | 11 ----------- 4 files changed, 36 insertions(+), 49 deletions(-) diff --git a/libethereum/ExtVM.cpp b/libethereum/ExtVM.cpp index 4c343c162..fcd703b25 100644 --- a/libethereum/ExtVM.cpp +++ b/libethereum/ExtVM.cpp @@ -21,5 +21,37 @@ #include "ExtVM.h" -#pragma GCC diagnostic ignored "-Wunused-variable" -namespace { char dummy; }; +#include "Executive.h" +using namespace std; +using namespace dev; +using namespace dev::eth; + +bool ExtVM::call(Address _receiveAddress, u256 _txValue, bytesConstRef _txData, u256& io_gas, bytesRef _out, OnOpFunc const& _onOp, Address _myAddressOverride, Address _codeAddressOverride) +{ + Executive e(m_s, depth + 1); + if (!e.call(_receiveAddress, _codeAddressOverride ? _codeAddressOverride : _receiveAddress, _myAddressOverride ? _myAddressOverride : myAddress, _txValue, gasPrice, _txData, io_gas, origin)) + { + e.go(_onOp); + sub += e.ext().sub; + } + io_gas = e.endGas(); + e.out().copyTo(_out); + + return !e.excepted(); +} + +h160 ExtVM::create(u256 _endowment, u256& io_gas, bytesConstRef _code, OnOpFunc const& _onOp) +{ + // Increment associated nonce for sender. + m_s.noteSending(myAddress); + + Executive e(m_s, depth + 1); + if (!e.create(myAddress, _endowment, gasPrice, io_gas, _code, origin)) + { + e.go(_onOp); + sub += e.ext().sub; + } + io_gas = e.endGas(); + return e.newAddress(); +} + diff --git a/libethereum/ExtVM.h b/libethereum/ExtVM.h index 8c04ff113..9699a68ad 100644 --- a/libethereum/ExtVM.h +++ b/libethereum/ExtVM.h @@ -55,18 +55,10 @@ public: virtual bytes const& codeAt(Address _a) override final { return m_s.code(_a); } /// Create a new contract. - virtual h160 create(u256 _endowment, u256& io_gas, bytesConstRef _code, OnOpFunc const& _onOp = {}) override final - { - // Increment associated nonce for sender. - m_s.noteSending(myAddress); - return m_s.create(myAddress, _endowment, gasPrice, io_gas, _code, origin, sub, _onOp, depth + 1); - } + virtual h160 create(u256 _endowment, u256& io_gas, bytesConstRef _code, OnOpFunc const& _onOp = {}) override final; /// Create a new message call. Leave _myAddressOverride as the default to use the present address as caller. - virtual bool call(Address _receiveAddress, u256 _txValue, bytesConstRef _txData, u256& io_gas, bytesRef _out, OnOpFunc const& _onOp = {}, Address _myAddressOverride = {}, Address _codeAddressOverride = {}) override final - { - return m_s.call(_receiveAddress, _codeAddressOverride ? _codeAddressOverride : _receiveAddress, _myAddressOverride ? _myAddressOverride : myAddress, _txValue, gasPrice, _txData, io_gas, _out, origin, sub, _onOp, depth + 1); - } + virtual bool call(Address _receiveAddress, u256 _txValue, bytesConstRef _txData, u256& io_gas, bytesRef _out, OnOpFunc const& _onOp = {}, Address _myAddressOverride = {}, Address _codeAddressOverride = {}) override final; /// Read address's balance. virtual u256 balance(Address _a) override final { return m_s.balance(_a); } diff --git a/libethereum/State.cpp b/libethereum/State.cpp index 7ca973e05..bd5c770cf 100644 --- a/libethereum/State.cpp +++ b/libethereum/State.cpp @@ -1174,32 +1174,6 @@ u256 State::execute(bytesConstRef _rlp, bytes* o_output, bool _commit) return e.gasUsed(); } -bool State::call(Address _receiveAddress, Address _codeAddress, Address _senderAddress, u256 _value, u256 _gasPrice, bytesConstRef _data, u256& io_gas, bytesRef _out, Address _originAddress, SubState& io_sub, OnOpFunc const& _onOp, unsigned _level) -{ - Executive e(*this, _level); - if (!e.call(_receiveAddress, _codeAddress, _senderAddress, _value, _gasPrice, _data, io_gas, _originAddress)) - { - e.go(_onOp); - io_sub += e.ext().sub; - } - io_gas = e.endGas(); - e.out().copyTo(_out); - - return !e.excepted(); -} - -h160 State::create(Address _sender, u256 _endowment, u256 _gasPrice, u256& io_gas, bytesConstRef _code, Address _origin, SubState& io_sub, OnOpFunc const& _onOp, unsigned _level) -{ - Executive e(*this, _level); - if (!e.create(_sender, _endowment, _gasPrice, io_gas, _code, _origin)) - { - e.go(_onOp); - io_sub += e.ext().sub; - } - io_gas = e.endGas(); - return e.newAddress(); -} - State State::fromPending(unsigned _i) const { State ret = *this; diff --git a/libethereum/State.h b/libethereum/State.h index 744f82e10..afa1f1d2c 100644 --- a/libethereum/State.h +++ b/libethereum/State.h @@ -272,17 +272,6 @@ private: /// Throws on failure. u256 enact(bytesConstRef _block, BlockChain const* _bc = nullptr, bool _checkNonce = true); - // Two priviledged entry points for the VM (these don't get added to the Transaction lists): - // We assume all instrinsic fees are paid up before this point. - - /// Execute a contract-creation transaction. - h160 create(Address _txSender, u256 _endowment, u256 _gasPrice, u256& io_gas, bytesConstRef _code, Address _originAddress, SubState& io_sub, OnOpFunc const& _onOp, unsigned _level); - - /// Execute a call. - /// @a _gas points to the amount of gas to use for the call, and will lower it accordingly. - /// @returns false if the call ran out of gas before completion. true otherwise. - bool call(Address _myAddress, Address _codeAddress, Address _txSender, u256 _txValue, u256 _gasPrice, bytesConstRef _txData, u256& io_gas, bytesRef _out, Address _originAddress, SubState& io_sub, OnOpFunc const& _onOp, unsigned _level); - /// Sets m_currentBlock to a clean state, (i.e. no change from m_previousBlock). void resetCurrent(); From d36a2fa5098f512be7cbe3fc3869eb6dd919569f Mon Sep 17 00:00:00 2001 From: CJentzsch Date: Fri, 12 Dec 2014 21:21:18 +0100 Subject: [PATCH 33/40] Fix import state for state tests Conflicts: test/TestHelper.cpp --- test/TestHelper.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/TestHelper.cpp b/test/TestHelper.cpp index ff8a0d40c..e8940cc7a 100644 --- a/test/TestHelper.cpp +++ b/test/TestHelper.cpp @@ -109,9 +109,6 @@ void ImportTest::importState(json_spirit::mObject& _o, State& _state) Address address = Address(i.first); - for (auto const& j: o["storage"].get_obj()) - _state.setStorage(address, toInt(j.first), toInt(j.second)); - bytes code = importCode(o); if (code.size()) @@ -122,6 +119,9 @@ void ImportTest::importState(json_spirit::mObject& _o, State& _state) else _state.m_cache[address] = Account(toInt(o["balance"]), Account::NormalCreation); + for (auto const& j: o["storage"].get_obj()) + _state.setStorage(address, toInt(j.first), toInt(j.second)); + for(int i=0; i _expectedStore, map _resultStore, BOOST_CHECK_MESSAGE(expectedStoreValue == resultStoreValue, _expectedAddr << ": store[" << expectedStoreKey << "] = " << resultStoreValue << ", expected " << expectedStoreValue); } } - + BOOST_CHECK_EQUAL(_resultStore.size(), _expectedStore.size()); for (auto&& resultStorePair : _resultStore) if (!_expectedStore.count(resultStorePair.first)) BOOST_ERROR(_expectedAddr << ": unexpected store key " << resultStorePair.first); From 45807bff5ee93a291222d86d0670d6c4e096f153 Mon Sep 17 00:00:00 2001 From: CJentzsch Date: Fri, 12 Dec 2014 21:45:10 +0100 Subject: [PATCH 34/40] set first 12 bytes to zero in ecrecover --- libethereum/State.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libethereum/State.cpp b/libethereum/State.cpp index bd5c770cf..30290c315 100644 --- a/libethereum/State.cpp +++ b/libethereum/State.cpp @@ -67,7 +67,7 @@ bytes ecrecoverCode(bytesConstRef _in) secp256k1_start(); if (secp256k1_ecdsa_recover_compact(in.hash.data(), 32, in.r.data(), pubkey, &pubkeylen, 0, (int)(u256)in.v - 27)) ret = dev::sha3(bytesConstRef(&(pubkey[1]), 64)); - + memset(ret.data(), 0, 12); return ret.asBytes(); } From 28556f42ecb9142eaa7afce3edee03f5b4e7cf58 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Fri, 12 Dec 2014 21:51:54 +0100 Subject: [PATCH 35/40] Remove unneeded files. --- libethereum/Manifest.cpp | 0 libethereum/Manifest.h | 0 2 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 libethereum/Manifest.cpp delete mode 100644 libethereum/Manifest.h diff --git a/libethereum/Manifest.cpp b/libethereum/Manifest.cpp deleted file mode 100644 index e69de29bb..000000000 diff --git a/libethereum/Manifest.h b/libethereum/Manifest.h deleted file mode 100644 index e69de29bb..000000000 From 82d29fa979dffabd9a76ddb1c7b0ae513404c251 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Fri, 12 Dec 2014 21:54:29 +0100 Subject: [PATCH 36/40] Rename headers. --- alethzero/MainWin.h | 1 - libethereum/BlockDetails.h | 1 - libethereum/Client.h | 4 +- libethereum/Interface.h | 7 +-- libethereum/MessageFilter.cpp | 81 -------------------------------- libethereum/MessageFilter.h | 69 --------------------------- libethereum/TransactionReceipt.h | 1 - 7 files changed, 2 insertions(+), 162 deletions(-) delete mode 100644 libethereum/MessageFilter.cpp delete mode 100644 libethereum/MessageFilter.h diff --git a/alethzero/MainWin.h b/alethzero/MainWin.h index fffc5843f..f2d632cd4 100644 --- a/alethzero/MainWin.h +++ b/alethzero/MainWin.h @@ -44,7 +44,6 @@ class Main; namespace dev { namespace eth { class Client; class State; -class MessageFilter; }} class QQuickView; diff --git a/libethereum/BlockDetails.h b/libethereum/BlockDetails.h index 2fd0d3048..bbfd48e56 100644 --- a/libethereum/BlockDetails.h +++ b/libethereum/BlockDetails.h @@ -28,7 +28,6 @@ #include #include -#include "Manifest.h" #include "TransactionReceipt.h" namespace ldb = leveldb; diff --git a/libethereum/Client.h b/libethereum/Client.h index 283251e24..477ea43e5 100644 --- a/libethereum/Client.h +++ b/libethereum/Client.h @@ -37,7 +37,7 @@ #include "State.h" #include "CommonNet.h" #include "PastMessage.h" -#include "MessageFilter.h" +#include "LogFilter.h" #include "Miner.h" #include "Interface.h" @@ -79,8 +79,6 @@ static const int GenesisBlock = INT_MIN; struct InstalledFilter { -// InstalledFilter(MessageFilter const& _f): filter(_f) {} -// MessageFilter filter; InstalledFilter(LogFilter const& _f): filter(_f) {} LogFilter filter; diff --git a/libethereum/Interface.h b/libethereum/Interface.h index d598e1f9b..28ac26819 100644 --- a/libethereum/Interface.h +++ b/libethereum/Interface.h @@ -26,7 +26,7 @@ #include #include #include -#include "MessageFilter.h" +#include "LogFilter.h" #include "Transaction.h" #include "AccountDiff.h" #include "BlockDetails.h" @@ -84,11 +84,6 @@ public: virtual bytes codeAt(Address _a, int _block) const = 0; virtual std::map storageAt(Address _a, int _block) const = 0; -// // [MESSAGE API] -// -// virtual PastMessages messages(unsigned _watchId) const = 0; -// virtual PastMessages messages(MessageFilter const& _filter) const = 0; - // [LOGS API] virtual LogEntries logs(unsigned _watchId) const = 0; diff --git a/libethereum/MessageFilter.cpp b/libethereum/MessageFilter.cpp deleted file mode 100644 index f44587620..000000000 --- a/libethereum/MessageFilter.cpp +++ /dev/null @@ -1,81 +0,0 @@ -/* - 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 . -*/ -/** @file MessageFilter.cpp - * @author Gav Wood - * @date 2014 - */ - -#include "MessageFilter.h" - -#include -#include "State.h" -using namespace std; -using namespace dev; -using namespace dev::eth; - -void LogFilter::streamRLP(RLPStream& _s) const -{ - _s.appendList(6) << m_addresses << m_topics << m_earliest << m_latest << m_max << m_skip; -} - -h256 LogFilter::sha3() const -{ - RLPStream s; - streamRLP(s); - return dev::sha3(s.out()); -} - -bool LogFilter::matches(LogBloom _bloom) const -{ - if (m_addresses.size()) - { - for (auto i: m_addresses) - if (_bloom.containsBloom<3>(dev::sha3(i))) - goto OK1; - return false; - } - OK1: - if (m_topics.size()) - { - for (auto i: m_topics) - if (_bloom.containsBloom<3>(dev::sha3(i))) - goto OK2; - return false; - } - OK2: - return true; -} - -bool LogFilter::matches(State const& _s, unsigned _i) const -{ - return matches(_s.receipt(_i)).size() > 0; -} - -LogEntries LogFilter::matches(TransactionReceipt const& _m) const -{ - LogEntries ret; - for (LogEntry const& e: _m.log()) - { - if (!m_addresses.empty() && !m_addresses.count(e.address)) - continue; - for (auto const& t: m_topics) - if (!std::count(e.topics.begin(), e.topics.end(), t)) - continue; - ret.push_back(e); - } - return ret; -} diff --git a/libethereum/MessageFilter.h b/libethereum/MessageFilter.h deleted file mode 100644 index e2c26d214..000000000 --- a/libethereum/MessageFilter.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - 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 . -*/ -/** @file MessageFilter.h - * @author Gav Wood - * @date 2014 - */ - -#pragma once - -#include -#include -#include -#include "TransactionReceipt.h" - -namespace dev -{ -namespace eth -{ - -class State; - -class LogFilter -{ -public: - LogFilter(int _earliest = 0, int _latest = -1, unsigned _max = 10, unsigned _skip = 0): m_earliest(_earliest), m_latest(_latest), m_max(_max), m_skip(_skip) {} - - void streamRLP(RLPStream& _s) const; - h256 sha3() const; - - int earliest() const { return m_earliest; } - int latest() const { return m_latest; } - unsigned max() const { return m_max; } - unsigned skip() const { return m_skip; } - bool matches(LogBloom _bloom) const; - bool matches(State const& _s, unsigned _i) const; - LogEntries matches(TransactionReceipt const& _r) const; - - LogFilter address(Address _a) { m_addresses.insert(_a); return *this; } - LogFilter topic(h256 const& _t) { m_topics.insert(_t); return *this; } - LogFilter withMax(unsigned _m) { m_max = _m; return *this; } - LogFilter withSkip(unsigned _m) { m_skip = _m; return *this; } - LogFilter withEarliest(int _e) { m_earliest = _e; return *this; } - LogFilter withLatest(int _e) { m_latest = _e; return *this; } - -private: - AddressSet m_addresses; - h256Set m_topics; - int m_earliest = 0; - int m_latest = -1; - unsigned m_max; - unsigned m_skip; -}; - -} -} diff --git a/libethereum/TransactionReceipt.h b/libethereum/TransactionReceipt.h index b990459d9..feb26dad3 100644 --- a/libethereum/TransactionReceipt.h +++ b/libethereum/TransactionReceipt.h @@ -26,7 +26,6 @@ #include #include #include -#include "Manifest.h" namespace dev { From 94dc92edabb917e2e02dba003678ed95c8ce4663 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Fri, 12 Dec 2014 21:56:26 +0100 Subject: [PATCH 37/40] Remove unneeded files. --- libethereum/Client.h | 1 - libethereum/LogFilter.cpp | 81 +++++++++++++++++++++++++++++++++++++ libethereum/LogFilter.h | 69 +++++++++++++++++++++++++++++++ libethereum/PastMessage.cpp | 0 libethereum/PastMessage.h | 0 5 files changed, 150 insertions(+), 1 deletion(-) create mode 100644 libethereum/LogFilter.cpp create mode 100644 libethereum/LogFilter.h delete mode 100644 libethereum/PastMessage.cpp delete mode 100644 libethereum/PastMessage.h diff --git a/libethereum/Client.h b/libethereum/Client.h index 477ea43e5..e8c460093 100644 --- a/libethereum/Client.h +++ b/libethereum/Client.h @@ -36,7 +36,6 @@ #include "TransactionQueue.h" #include "State.h" #include "CommonNet.h" -#include "PastMessage.h" #include "LogFilter.h" #include "Miner.h" #include "Interface.h" diff --git a/libethereum/LogFilter.cpp b/libethereum/LogFilter.cpp new file mode 100644 index 000000000..81cb439ba --- /dev/null +++ b/libethereum/LogFilter.cpp @@ -0,0 +1,81 @@ +/* + 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 . +*/ +/** @file LogFilter.cpp + * @author Gav Wood + * @date 2014 + */ + +#include "LogFilter.h" + +#include +#include "State.h" +using namespace std; +using namespace dev; +using namespace dev::eth; + +void LogFilter::streamRLP(RLPStream& _s) const +{ + _s.appendList(6) << m_addresses << m_topics << m_earliest << m_latest << m_max << m_skip; +} + +h256 LogFilter::sha3() const +{ + RLPStream s; + streamRLP(s); + return dev::sha3(s.out()); +} + +bool LogFilter::matches(LogBloom _bloom) const +{ + if (m_addresses.size()) + { + for (auto i: m_addresses) + if (_bloom.containsBloom<3>(dev::sha3(i))) + goto OK1; + return false; + } + OK1: + if (m_topics.size()) + { + for (auto i: m_topics) + if (_bloom.containsBloom<3>(dev::sha3(i))) + goto OK2; + return false; + } + OK2: + return true; +} + +bool LogFilter::matches(State const& _s, unsigned _i) const +{ + return matches(_s.receipt(_i)).size() > 0; +} + +LogEntries LogFilter::matches(TransactionReceipt const& _m) const +{ + LogEntries ret; + for (LogEntry const& e: _m.log()) + { + if (!m_addresses.empty() && !m_addresses.count(e.address)) + continue; + for (auto const& t: m_topics) + if (!std::count(e.topics.begin(), e.topics.end(), t)) + continue; + ret.push_back(e); + } + return ret; +} diff --git a/libethereum/LogFilter.h b/libethereum/LogFilter.h new file mode 100644 index 000000000..bda8e46a2 --- /dev/null +++ b/libethereum/LogFilter.h @@ -0,0 +1,69 @@ +/* + 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 . +*/ +/** @file LogFilter.h + * @author Gav Wood + * @date 2014 + */ + +#pragma once + +#include +#include +#include +#include "TransactionReceipt.h" + +namespace dev +{ +namespace eth +{ + +class State; + +class LogFilter +{ +public: + LogFilter(int _earliest = 0, int _latest = -1, unsigned _max = 10, unsigned _skip = 0): m_earliest(_earliest), m_latest(_latest), m_max(_max), m_skip(_skip) {} + + void streamRLP(RLPStream& _s) const; + h256 sha3() const; + + int earliest() const { return m_earliest; } + int latest() const { return m_latest; } + unsigned max() const { return m_max; } + unsigned skip() const { return m_skip; } + bool matches(LogBloom _bloom) const; + bool matches(State const& _s, unsigned _i) const; + LogEntries matches(TransactionReceipt const& _r) const; + + LogFilter address(Address _a) { m_addresses.insert(_a); return *this; } + LogFilter topic(h256 const& _t) { m_topics.insert(_t); return *this; } + LogFilter withMax(unsigned _m) { m_max = _m; return *this; } + LogFilter withSkip(unsigned _m) { m_skip = _m; return *this; } + LogFilter withEarliest(int _e) { m_earliest = _e; return *this; } + LogFilter withLatest(int _e) { m_latest = _e; return *this; } + +private: + AddressSet m_addresses; + h256Set m_topics; + int m_earliest = 0; + int m_latest = -1; + unsigned m_max; + unsigned m_skip; +}; + +} +} diff --git a/libethereum/PastMessage.cpp b/libethereum/PastMessage.cpp deleted file mode 100644 index e69de29bb..000000000 diff --git a/libethereum/PastMessage.h b/libethereum/PastMessage.h deleted file mode 100644 index e69de29bb..000000000 From b364a8110b384d46ea452c33e8141eb15f54cd4e Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Fri, 12 Dec 2014 22:00:20 +0100 Subject: [PATCH 38/40] Repotting. --- libethereum/TransactionReceipt.cpp | 51 ++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 libethereum/TransactionReceipt.cpp diff --git a/libethereum/TransactionReceipt.cpp b/libethereum/TransactionReceipt.cpp new file mode 100644 index 000000000..0fb104490 --- /dev/null +++ b/libethereum/TransactionReceipt.cpp @@ -0,0 +1,51 @@ +/* + 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 . +*/ +/** @file TransactionReceipt.cpp + * @author Gav Wood + * @date 2014 + */ + +#include "TransactionReceipt.h" + +using namespace std; +using namespace dev; +using namespace dev::eth; + +TransactionReceipt::TransactionReceipt(bytesConstRef _rlp) +{ + RLP r(_rlp); + m_stateRoot = (h256)r[0]; + m_gasUsed = (u256)r[1]; + m_bloom = (LogBloom)r[2]; + for (auto const& i: r[3]) + m_log.emplace_back(i); +} + +TransactionReceipt::TransactionReceipt(h256 _root, u256 _gasUsed, LogEntries const& _log): + m_stateRoot(_root), + m_gasUsed(_gasUsed), + m_bloom(eth::bloom(_log)), + m_log(_log) +{} + +void TransactionReceipt::streamRLP(RLPStream& _s) const +{ + _s.appendList(4) << m_stateRoot << m_gasUsed << m_bloom; + _s.appendList(m_log.size()); + for (LogEntry const& l: m_log) + l.streamRLP(_s); +} From b52e9cae5a015e822a3f315cb84af96b0f12ec2c Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Fri, 12 Dec 2014 22:00:40 +0100 Subject: [PATCH 39/40] Add file. --- libethereum/TransactionReceipt.h | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/libethereum/TransactionReceipt.h b/libethereum/TransactionReceipt.h index feb26dad3..23995c75a 100644 --- a/libethereum/TransactionReceipt.h +++ b/libethereum/TransactionReceipt.h @@ -36,21 +36,15 @@ namespace eth class TransactionReceipt { public: - TransactionReceipt(bytesConstRef _rlp) { RLP r(_rlp); m_stateRoot = (h256)r[0]; m_gasUsed = (u256)r[1]; m_bloom = (LogBloom)r[2]; for (auto const& i: r[3]) m_log.emplace_back(i); } - TransactionReceipt(h256 _root, u256 _gasUsed, LogEntries const& _log): m_stateRoot(_root), m_gasUsed(_gasUsed), m_bloom(eth::bloom(_log)), m_log(_log) {} + TransactionReceipt(bytesConstRef _rlp); + TransactionReceipt(h256 _root, u256 _gasUsed, LogEntries const& _log); h256 const& stateRoot() const { return m_stateRoot; } u256 const& gasUsed() const { return m_gasUsed; } LogBloom const& bloom() const { return m_bloom; } LogEntries const& log() const { return m_log; } - void streamRLP(RLPStream& _s) const - { - _s.appendList(4) << m_stateRoot << m_gasUsed << m_bloom; - _s.appendList(m_log.size()); - for (LogEntry const& l: m_log) - l.streamRLP(_s); - } + void streamRLP(RLPStream& _s) const; bytes rlp() const { RLPStream s; streamRLP(s); return s.out(); } From 1b8f9fdc3b44503890ed1bcb5da8bd5cb8dd83a5 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Fri, 12 Dec 2014 22:08:48 +0100 Subject: [PATCH 40/40] Deduplication. --- alethzero/MainWin.cpp | 57 +++---------------------------------------- 1 file changed, 3 insertions(+), 54 deletions(-) diff --git a/alethzero/MainWin.cpp b/alethzero/MainWin.cpp index 0e36c7737..fa489ffeb 100644 --- a/alethzero/MainWin.cpp +++ b/alethzero/MainWin.cpp @@ -43,6 +43,7 @@ #include #include #include +#include #include #include #include @@ -1500,58 +1501,6 @@ void Main::on_destination_currentTextChanged() // updateFee(); } -static bytes dataFromText(QString _s) -{ - bytes ret; - while (_s.size()) - { - QRegExp r("(@|\\$)?\"([^\"]*)\"(\\s.*)?"); - QRegExp d("(@|\\$)?([0-9]+)(\\s*(ether)|(finney)|(szabo))?(\\s.*)?"); - QRegExp h("(@|\\$)?(0x)?(([a-fA-F0-9])+)(\\s.*)?"); - if (r.exactMatch(_s)) - { - for (auto i: r.cap(2)) - ret.push_back((byte)i.toLatin1()); - if (r.cap(1) != "$") - for (int i = r.cap(2).size(); i < 32; ++i) - ret.push_back(0); - else - ret.push_back(0); - _s = r.cap(3); - } - else if (d.exactMatch(_s)) - { - u256 v(d.cap(2).toStdString()); - if (d.cap(6) == "szabo") - v *= dev::eth::szabo; - else if (d.cap(5) == "finney") - v *= dev::eth::finney; - else if (d.cap(4) == "ether") - v *= dev::eth::ether; - bytes bs = dev::toCompactBigEndian(v); - if (d.cap(1) != "$") - for (auto i = bs.size(); i < 32; ++i) - ret.push_back(0); - for (auto b: bs) - ret.push_back(b); - _s = d.cap(7); - } - else if (h.exactMatch(_s)) - { - bytes bs = fromHex((((h.cap(3).size() & 1) ? "0" : "") + h.cap(3)).toStdString()); - if (h.cap(1) != "$") - for (auto i = bs.size(); i < 32; ++i) - ret.push_back(0); - for (auto b: bs) - ret.push_back(b); - _s = h.cap(5); - } - else - _s = _s.mid(1); - } - return ret; -} - static shh::Topic topicFromText(QString _s) { shh::BuildTopic ret; @@ -1679,7 +1628,7 @@ void Main::on_data_textChanged() } else { - m_data = dataFromText(ui->data->toPlainText()); + m_data = parseData(ui->data->toPlainText().toStdString()); ui->code->setHtml(QString::fromStdString(dev::memDump(m_data, 8, true))); if (ethereum()->codeAt(fromString(ui->destination->currentText()), 0).size()) { @@ -2207,7 +2156,7 @@ void Main::on_post_clicked() { shh::Message m; m.setTo(stringToPublic(ui->shhTo->currentText())); - m.setPayload(dataFromText(ui->shhData->toPlainText())); + m.setPayload(parseData(ui->shhData->toPlainText().toStdString())); Public f = stringToPublic(ui->shhFrom->currentText()); Secret from; if (m_server->ids().count(f))