Browse Source

Merge branch 'develop' into p2p

Conflicts:
	eth/main.cpp
	libwebthree/WebThree.cpp
	libwebthree/WebThree.h
	neth/main.cpp
cl-refactor
subtly 10 years ago
parent
commit
2455966c11
  1. 8
      CMakeLists.txt
  2. 2
      CodingStandards.txt
  3. 1
      alethzero/CMakeLists.txt
  4. 34
      alethzero/MainWin.cpp
  5. 2
      alethzero/NatspecHandler.h
  6. 3
      cmake/FindJsoncpp.cmake
  7. 17
      cmake/scripts/jsonrpcstub.cmake
  8. 98
      eth/main.cpp
  9. 6
      libdevcore/CommonData.cpp
  10. 9
      libdevcore/CommonData.h
  11. 7
      libethereum/Client.cpp
  12. 2
      libethereum/Client.h
  13. 3
      libsolidity/AST.cpp
  14. 2
      libsolidity/ASTJsonConverter.h
  15. 7
      libsolidity/Compiler.cpp
  16. 250
      libsolidity/CompilerUtils.cpp
  17. 33
      libsolidity/CompilerUtils.h
  18. 203
      libsolidity/ExpressionCompiler.cpp
  19. 34
      libsolidity/ExpressionCompiler.h
  20. 2
      libsolidity/InterfaceHandler.h
  21. 6
      libsolidity/Token.h
  22. 70
      libsolidity/Types.cpp
  23. 40
      libsolidity/Types.h
  24. 6
      libwebthree/WebThree.cpp
  25. 2
      libwebthree/WebThree.h
  26. 1
      lllc/CMakeLists.txt
  27. 1
      mix/CMakeLists.txt
  28. 81
      neth/main.cpp
  29. 1
      solc/CMakeLists.txt
  30. 2
      test/SolidityABIJSON.cpp
  31. 139
      test/SolidityEndToEndTest.cpp
  32. 2
      test/SolidityNatspecJSON.cpp
  33. 2
      test/solidityExecutionFramework.h

8
CMakeLists.txt

@ -109,6 +109,7 @@ cmake_policy(SET CMP0015 NEW)
createDefaultCacheConfig()
configureProject()
message(STATUS "CMAKE_VERSION: ${CMAKE_VERSION}")
message("-- VMTRACE: ${VMTRACE}; PARANOIA: ${PARANOIA}; HEADLESS: ${HEADLESS}; JSONRPC: ${JSONRPC}; EVMJIT: ${EVMJIT}")
@ -140,15 +141,15 @@ endif()
add_subdirectory(libdevcore)
add_subdirectory(libevmcore)
add_subdirectory(liblll)
if (NOT ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC"))
add_subdirectory(libserpent)
add_subdirectory(sc)
endif ()
add_subdirectory(libsolidity)
add_subdirectory(lllc)
add_subdirectory(solc)
if (NOT ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC"))
add_subdirectory(sc)
endif()
if (JSONRPC)
add_subdirectory(libweb3jsonrpc)
@ -166,6 +167,7 @@ add_subdirectory(libethereum)
add_subdirectory(libwebthree)
add_subdirectory(test)
add_subdirectory(eth)
if("x${CMAKE_BUILD_TYPE}" STREQUAL "xDebug")
add_subdirectory(exp)
endif ()

2
CodingStandards.txt

@ -93,7 +93,7 @@ b. Only one per line.
c. Associate */& with type, not variable (at ends with parser, but more readable, and safe if in conjunction with (b)).
d. Favour declarations close to use; don't habitually declare at top of scope ala C.
e. Always pass non-trivial parameters with a const& suffix.
f. If a func tion returns multiple values, use std::tuple (std::pair acceptable). Prefer not using */& arguments, except where efficiency requires.
f. If a function returns multiple values, use std::tuple (std::pair acceptable). Prefer not using */& arguments, except where efficiency requires.
g. Never use a macro where adequate non-preprocessor C++ can be written.
h. Prefer "using NewType = OldType" to "typedef OldType NewType".

1
alethzero/CMakeLists.txt

@ -11,6 +11,7 @@ set(CMAKE_INCLUDE_CURRENT_DIR ON)
aux_source_directory(. SRC_LIST)
include_directories(${JSON_RPC_CPP_INCLUDE_DIRS})
include_directories(${JSONCPP_INCLUDE_DIRS})
include_directories(..)
qt5_wrap_ui(ui_Main.h Main.ui)

34
alethzero/MainWin.cpp

@ -572,7 +572,23 @@ Address Main::fromString(QString const& _n) const
}*/
if (_n.size() == 40)
return Address(fromHex(_n.toStdString()));
{
try
{
return Address(fromHex(_n.toStdString(), ThrowType::Throw));
}
catch (BadHexCharacter& _e)
{
cwarn << "invalid hex character, address rejected";
cwarn << boost::diagnostic_information(_e);
return Address();
}
catch (...)
{
cwarn << "address rejected";
return Address();
}
}
else
return Address();
}
@ -1325,8 +1341,20 @@ void Main::ourAccountsRowsMoved()
void Main::on_inject_triggered()
{
QString s = QInputDialog::getText(this, "Inject Transaction", "Enter transaction dump in hex");
bytes b = fromHex(s.toStdString());
ethereum()->inject(&b);
try
{
bytes b = fromHex(s.toStdString(), ThrowType::Throw);
ethereum()->inject(&b);
}
catch (BadHexCharacter& _e)
{
cwarn << "invalid hex character, transaction rejected";
cwarn << boost::diagnostic_information(_e);
}
catch (...)
{
cwarn << "transaction rejected";
}
}
void Main::on_blocks_currentItemChanged()

2
alethzero/NatspecHandler.h

@ -26,7 +26,7 @@
#pragma warning(disable: 4100 4267)
#include <leveldb/db.h>
#pragma warning(pop)
#include <jsoncpp/json/json.h>
#include <json/json.h>
#include <libdevcore/FixedHash.h>
namespace ldb = leveldb;

3
cmake/FindJsoncpp.cmake

@ -12,7 +12,8 @@
# only look in default directories
find_path(
JSONCPP_INCLUDE_DIR
NAMES jsoncpp/json/json.h
NAMES json/json.h
PATH_SUFFIXES jsoncpp
DOC "jsoncpp include dir"
)

17
cmake/scripts/jsonrpcstub.cmake

@ -27,10 +27,19 @@ set(CLIENT_OUTFILE "${ETH_CLIENT_DIR}/${ETH_CLIENT_NAME_LOWER}.h")
execute_process(
COMMAND ${ETH_JSON_RPC_STUB} ${ETH_SPEC_PATH}
--cpp-server=${ETH_SERVER_NAME} --cpp-server-file=${SERVER_TMPFILE}
--cpp-client=${ETH_CLIENT_NAME} --cpp-client-file=${CLIENT_TMPFILE}
--cpp-client=${ETH_CLIENT_NAME} --cpp-client-file=${CLIENT_TMPFILE}
OUTPUT_VARIABLE ERR ERROR_QUIET
)
include("${ETH_SOURCE_DIR}/cmake/EthUtils.cmake")
replace_if_different("${SERVER_TMPFILE}" "${SERVER_OUTFILE}")
replace_if_different("${CLIENT_TMPFILE}" "${CLIENT_OUTFILE}")
# don't throw fatal error on jsonrpcstub error, someone might have old version of jsonrpcstub,
# he does not need to upgrade it if he is not working on JSON RPC
# show him warning instead
if (ERR)
message(WARNING "Your version of jsonrcpstub tool is not supported. Please upgrade it.")
message(WARNING "${ERR}")
else()
include("${ETH_SOURCE_DIR}/cmake/EthUtils.cmake")
replace_if_different("${SERVER_TMPFILE}" "${SERVER_OUTFILE}")
replace_if_different("${CLIENT_TMPFILE}" "${CLIENT_OUTFILE}")
endif()

98
eth/main.cpp

@ -119,6 +119,7 @@ void help()
<< " -p,--port <port> Connect to remote port (default: 30303)." << endl
<< " -r,--remote <host> Connect to remote host (default: none)." << endl
<< " -s,--secret <secretkeyhex> Set the secret key for use with send command (default: auto)." << endl
<< " -t,--miners <number> Number of mining threads to start (Default: " << thread::hardware_concurrency() << ")" << endl
<< " -u,--public-ip <ip> Force public ip to given (default; auto)." << endl
<< " -v,--verbosity <0 - 9> Set the log verbosity from 0 to 9 (Default: 8)." << endl
<< " -x,--peers <number> Attempt to connect to given number of peers (Default: 5)." << endl
@ -195,6 +196,7 @@ int main(int argc, char** argv)
unsigned mining = ~(unsigned)0;
NodeMode mode = NodeMode::Full;
unsigned peers = 5;
int miners = -1;
bool interactive = false;
#if ETH_JSONRPC
int jsonrpc = -1;
@ -258,7 +260,23 @@ int main(int argc, char** argv)
else if ((arg == "-c" || arg == "--client-name") && i + 1 < argc)
clientName = argv[++i];
else if ((arg == "-a" || arg == "--address" || arg == "--coinbase-address") && i + 1 < argc)
coinbase = h160(fromHex(argv[++i]));
{
try
{
coinbase = h160(fromHex(argv[++i], ThrowType::Throw));
}
catch (BadHexCharacter& _e)
{
cwarn << "invalid hex character, coinbase rejected";
cwarn << boost::diagnostic_information(_e);
break;
}
catch (...)
{
cwarn << "coinbase rejected";
break;
}
}
else if ((arg == "-s" || arg == "--secret") && i + 1 < argc)
us = KeyPair(h256(fromHex(argv[++i])));
else if ((arg == "-d" || arg == "--path" || arg == "--db-path") && i + 1 < argc)
@ -295,6 +313,8 @@ int main(int argc, char** argv)
g_logVerbosity = atoi(argv[++i]);
else if ((arg == "-x" || arg == "--peers") && i + 1 < argc)
peers = atoi(argv[++i]);
else if ((arg == "-t" || arg == "--miners") && i + 1 < argc)
miners = atoi(argv[++i]);
else if ((arg == "-o" || arg == "--mode") && i + 1 < argc)
{
string m = argv[++i];
@ -339,7 +359,9 @@ int main(int argc, char** argv)
false,
mode == NodeMode::Full ? set<string>{"eth", "shh"} : set<string>(),
netPrefs,
&nodesState);
&nodesState,
miners
);
web3.setIdealPeerCount(peers);
eth::Client* c = mode == NodeMode::Full ? web3.ethereum() : nullptr;
@ -530,9 +552,21 @@ int main(int argc, char** argv)
}
else
{
Secret secret = h256(fromHex(sechex));
Address dest = h160(fromHex(hexAddr));
c->transact(secret, amount, dest, data, gas, gasPrice);
try
{
Secret secret = h256(fromHex(sechex));
Address dest = h160(fromHex(hexAddr));
c->transact(secret, amount, dest, data, gas, gasPrice);
}
catch (BadHexCharacter& _e)
{
cwarn << "invalid hex character, transaction rejected";
cwarn << boost::diagnostic_information(_e);
}
catch (...)
{
cwarn << "transaction rejected";
}
}
}
else
@ -581,8 +615,20 @@ int main(int argc, char** argv)
auto blockData = bc.block(h);
BlockInfo info(blockData);
u256 minGas = (u256)Client::txGas(bytes(), 0);
Address dest = h160(fromHex(hexAddr));
c->transact(us.secret(), amount, dest, bytes(), minGas);
try
{
Address dest = h160(fromHex(hexAddr, ThrowType::Throw));
c->transact(us.secret(), amount, dest, bytes(), minGas);
}
catch (BadHexCharacter& _e)
{
cwarn << "invalid hex character, transaction rejected";
cwarn << boost::diagnostic_information(_e);
}
catch (...)
{
cwarn << "transaction rejected";
}
}
}
else
@ -613,14 +659,30 @@ int main(int argc, char** argv)
{
cnote << "Assembled:";
stringstream ssc;
init = fromHex(sinit);
try
{
init = fromHex(sinit, ThrowType::Throw);
}
catch (BadHexCharacter& _e)
{
cwarn << "invalid hex character, code rejected";
cwarn << boost::diagnostic_information(_e);
init = bytes();
}
catch (...)
{
cwarn << "code rejected";
init = bytes();
}
ssc.str(string());
ssc << disassemble(init);
cnote << "Init:";
cnote << ssc.str();
}
u256 minGas = (u256)Client::txGas(init, 0);
if (endowment < 0)
if (!init.size())
cwarn << "Contract creation aborted, no init code.";
else if (endowment < 0)
cwarn << "Invalid endowment";
else if (gas < minGas)
cwarn << "Minimum gas amount is" << minGas;
@ -757,8 +819,22 @@ int main(int argc, char** argv)
if (hexAddr.length() != 40)
cwarn << "Invalid address length: " << hexAddr.length();
else
coinbase = h160(fromHex(hexAddr));
}
{
try
{
coinbase = h160(fromHex(hexAddr, ThrowType::Throw));
}
catch (BadHexCharacter& _e)
{
cwarn << "invalid hex character, coinbase rejected";
cwarn << boost::diagnostic_information(_e);
}
catch (...)
{
cwarn << "coinbase rejected";
}
}
}
else
cwarn << "Require parameter: setAddress HEXADDRESS";
}

6
libdevcore/CommonData.cpp

@ -78,7 +78,7 @@ int dev::fromHex(char _i)
BOOST_THROW_EXCEPTION(BadHexCharacter() << errinfo_invalidSymbol(_i));
}
bytes dev::fromHex(std::string const& _s)
bytes dev::fromHex(std::string const& _s, ThrowType _throw)
{
unsigned s = (_s[0] == '0' && _s[1] == 'x') ? 2 : 0;
std::vector<uint8_t> ret;
@ -96,6 +96,8 @@ bytes dev::fromHex(std::string const& _s)
#ifndef BOOST_NO_EXCEPTIONS
cwarn << boost::current_exception_diagnostic_information();
#endif
if (_throw == ThrowType::Throw)
throw;
}
for (unsigned i = s; i < _s.size(); i += 2)
try
@ -107,6 +109,8 @@ bytes dev::fromHex(std::string const& _s)
#ifndef BOOST_NO_EXCEPTIONS
cwarn << boost::current_exception_diagnostic_information();
#endif
if (_throw == ThrowType::Throw)
throw;
}
return ret;
}

9
libdevcore/CommonData.h

@ -35,6 +35,12 @@ namespace dev
// String conversion functions, mainly to/from hex/nibble/byte representations.
enum class ThrowType
{
NoThrow = 0,
Throw = 1,
};
/// Convert a series of bytes to the corresponding string of hex duplets.
/// @param _w specifies the width of each of the elements. Defaults to two - enough to represent a byte.
/// @example toHex("A\x69") == "4169"
@ -53,7 +59,8 @@ int fromHex(char _i);
/// Converts a (printable) ASCII hex string into the corresponding byte stream.
/// @example fromHex("41626261") == asBytes("Abba")
bytes fromHex(std::string const& _s);
/// If _throw = ThrowType::NoThrow, it replaces bad hex characters with 0's, otherwise it will throw an exception.
bytes fromHex(std::string const& _s, ThrowType _throw = ThrowType::NoThrow);
#if 0
std::string toBase58(bytesConstRef _data);

7
libethereum/Client.cpp

@ -59,7 +59,7 @@ void VersionChecker::setOk()
}
}
Client::Client(p2p::Host* _extNet, std::string const& _dbPath, bool _forceClean, u256 _networkId):
Client::Client(p2p::Host* _extNet, std::string const& _dbPath, bool _forceClean, u256 _networkId, int miners):
Worker("eth"),
m_vc(_dbPath),
m_bc(_dbPath, !m_vc.ok() || _forceClean),
@ -69,7 +69,10 @@ Client::Client(p2p::Host* _extNet, std::string const& _dbPath, bool _forceClean,
{
m_host = _extNet->registerCapability(new EthereumHost(m_bc, m_tq, m_bq, _networkId));
setMiningThreads();
if (miners > -1)
setMiningThreads(miners);
else
setMiningThreads();
if (_dbPath.size())
Defaults::setDBPath(_dbPath);
m_vc.setOk();

2
libethereum/Client.h

@ -166,7 +166,7 @@ class Client: public MinerHost, public Interface, Worker
public:
/// New-style Constructor.
explicit Client(p2p::Host* _host, std::string const& _dbPath = std::string(), bool _forceClean = false, u256 _networkId = 0);
explicit Client(p2p::Host* _host, std::string const& _dbPath = std::string(), bool _forceClean = false, u256 _networkId = 0, int miners = -1);
/// Destructor.
virtual ~Client();

3
libsolidity/AST.cpp

@ -403,7 +403,8 @@ void Assignment::checkTypeRequirements()
m_leftHandSide->checkTypeRequirements();
m_leftHandSide->requireLValue();
//@todo later, assignments to structs might be possible, but not to mappings
if (!m_leftHandSide->getType()->isValueType() && !m_leftHandSide->isLocalLValue())
if (m_leftHandSide->getType()->getCategory() != Type::Category::ByteArray &&
!m_leftHandSide->getType()->isValueType() && !m_leftHandSide->isLocalLValue())
BOOST_THROW_EXCEPTION(createTypeError("Assignment to non-local non-value lvalue."));
m_type = m_leftHandSide->getType();
if (m_assigmentOperator == Token::Assign)

2
libsolidity/ASTJsonConverter.h

@ -27,7 +27,7 @@
#include <libsolidity/ASTVisitor.h>
#include <libsolidity/Exceptions.h>
#include <libsolidity/Utils.h>
#include <jsoncpp/json/json.h>
#include <json/json.h>
namespace dev
{

7
libsolidity/Compiler.cpp

@ -207,15 +207,10 @@ void Compiler::appendReturnValuePacker(TypePointers const& _typeParameters)
for (TypePointer const& type: _typeParameters)
{
unsigned numBytes = type->getCalldataEncodedSize();
if (numBytes > 32)
BOOST_THROW_EXCEPTION(CompilerError()
<< errinfo_comment("Type " + type->toString() + " not yet supported."));
CompilerUtils(m_context).copyToStackTop(stackDepth, *type);
ExpressionCompiler::appendTypeConversion(m_context, *type, *type, true);
bool const c_leftAligned = type->getCategory() == Type::Category::String;
bool const c_padToWords = true;
dataOffset += CompilerUtils(m_context).storeInMemory(dataOffset, numBytes, c_leftAligned, c_padToWords);
dataOffset += CompilerUtils(m_context).storeInMemory(dataOffset, *type, c_padToWords);
stackDepth -= type->getSizeOnStack();
}
// note that the stack is not cleaned up here

250
libsolidity/CompilerUtils.cpp

@ -62,20 +62,59 @@ unsigned CompilerUtils::loadFromMemory(unsigned _offset, unsigned _bytes, bool _
}
}
unsigned CompilerUtils::storeInMemory(unsigned _offset, unsigned _bytes, bool _leftAligned,
bool _padToWordBoundaries)
unsigned CompilerUtils::storeInMemory(unsigned _offset, Type const& _type, bool _padToWordBoundaries)
{
if (_bytes == 0)
solAssert(_type.getCategory() != Type::Category::ByteArray, "Unable to statically store dynamic type.");
unsigned numBytes = prepareMemoryStore(_type, _padToWordBoundaries);
if (numBytes > 0)
m_context << u256(_offset) << eth::Instruction::MSTORE;
return numBytes;
}
void CompilerUtils::storeInMemoryDynamic(Type const& _type, bool _padToWordBoundaries)
{
if (_type.getCategory() == Type::Category::ByteArray)
{
m_context << eth::Instruction::POP;
return 0;
auto const& type = dynamic_cast<ByteArrayType const&>(_type);
solAssert(type.getLocation() == ByteArrayType::Location::Storage, "Non-storage byte arrays not yet implemented.");
m_context << eth::Instruction::DUP1 << eth::Instruction::SLOAD;
// stack here: memory_offset storage_offset length_bytes
// jump to end if length is zero
m_context << eth::Instruction::DUP1 << eth::Instruction::ISZERO;
eth::AssemblyItem loopEnd = m_context.newTag();
m_context.appendConditionalJumpTo(loopEnd);
// compute memory end offset
m_context << eth::Instruction::DUP3 << eth::Instruction::ADD << eth::Instruction::SWAP2;
// actual array data is stored at SHA3(storage_offset)
m_context << eth::Instruction::SWAP1;
CompilerUtils(m_context).computeHashStatic();
m_context << eth::Instruction::SWAP1;
// stack here: memory_end_offset storage_data_offset memory_offset
eth::AssemblyItem loopStart = m_context.newTag();
m_context << loopStart
// load and store
<< eth::Instruction::DUP2 << eth::Instruction::SLOAD
<< eth::Instruction::DUP2 << eth::Instruction::MSTORE
// increment storage_data_offset by 1
<< eth::Instruction::SWAP1 << u256(1) << eth::Instruction::ADD
// increment memory offset by 32
<< eth::Instruction::SWAP1 << u256(32) << eth::Instruction::ADD
// check for loop condition
<< eth::Instruction::DUP1 << eth::Instruction::DUP4 << eth::Instruction::GT;
m_context.appendConditionalJumpTo(loopStart);
m_context << loopEnd << eth::Instruction::POP << eth::Instruction::POP;
}
else
{
unsigned numBytes = prepareMemoryStore(_type, _padToWordBoundaries);
if (numBytes > 0)
{
m_context << eth::Instruction::DUP2 << eth::Instruction::MSTORE;
m_context << u256(numBytes) << eth::Instruction::ADD;
}
}
solAssert(_bytes <= 32, "Memory store of more than 32 bytes requested.");
if (_bytes != 32 && !_leftAligned && !_padToWordBoundaries)
// shift the value accordingly before storing
m_context << (u256(1) << ((32 - _bytes) * 8)) << eth::Instruction::MUL;
m_context << u256(_offset) << eth::Instruction::MSTORE;
return _padToWordBoundaries ? 32 : _bytes;
}
void CompilerUtils::moveToStackVariable(VariableDeclaration const& _variable)
@ -114,5 +153,194 @@ unsigned CompilerUtils::getSizeOnStack(vector<shared_ptr<Type const>> const& _va
return size;
}
void CompilerUtils::computeHashStatic(Type const& _type, bool _padToWordBoundaries)
{
unsigned length = storeInMemory(0, _type, _padToWordBoundaries);
m_context << u256(length) << u256(0) << eth::Instruction::SHA3;
}
void CompilerUtils::copyByteArrayToStorage(ByteArrayType const& _targetType,
ByteArrayType const& _sourceType) const
{
// stack layout: [source_ref] target_ref (top)
// need to leave target_ref on the stack at the end
solAssert(_targetType.getLocation() == ByteArrayType::Location::Storage, "");
switch (_sourceType.getLocation())
{
case ByteArrayType::Location::CallData:
{
// @todo this does not take length into account. It also assumes that after "CALLDATALENGTH" we only have zeros.
// fetch old length and convert to words
m_context << eth::Instruction::DUP1 << eth::Instruction::SLOAD;
m_context << u256(31) << eth::Instruction::ADD
<< u256(32) << eth::Instruction::SWAP1 << eth::Instruction::DIV;
// stack here: target_ref target_length_words
// actual array data is stored at SHA3(storage_offset)
m_context << eth::Instruction::DUP2;
CompilerUtils(m_context).computeHashStatic();
// compute target_data_end
m_context << eth::Instruction::DUP1 << eth::Instruction::SWAP2 << eth::Instruction::ADD
<< eth::Instruction::SWAP1;
// stack here: target_ref target_data_end target_data_ref
// store length (in bytes)
if (_sourceType.getOffset() == 0)
m_context << eth::Instruction::CALLDATASIZE;
else
m_context << _sourceType.getOffset() << eth::Instruction::CALLDATASIZE << eth::Instruction::SUB;
m_context << eth::Instruction::DUP1 << eth::Instruction::DUP5 << eth::Instruction::SSTORE;
// jump to end if length is zero
m_context << eth::Instruction::ISZERO;
eth::AssemblyItem copyLoopEnd = m_context.newTag();
m_context.appendConditionalJumpTo(copyLoopEnd);
m_context << _sourceType.getOffset();
// stack now: target_ref target_data_end target_data_ref calldata_offset
eth::AssemblyItem copyLoopStart = m_context.newTag();
m_context << copyLoopStart
// copy from calldata and store
<< eth::Instruction::DUP1 << eth::Instruction::CALLDATALOAD
<< eth::Instruction::DUP3 << eth::Instruction::SSTORE
// increment target_data_ref by 1
<< eth::Instruction::SWAP1 << u256(1) << eth::Instruction::ADD
// increment calldata_offset by 32
<< eth::Instruction::SWAP1 << u256(32) << eth::Instruction::ADD
// check for loop condition
<< eth::Instruction::DUP1 << eth::Instruction::CALLDATASIZE << eth::Instruction::GT;
m_context.appendConditionalJumpTo(copyLoopStart);
m_context << eth::Instruction::POP;
m_context << copyLoopEnd;
// now clear leftover bytes of the old value
// stack now: target_ref target_data_end target_data_ref
clearStorageLoop();
m_context << eth::Instruction::POP;
break;
}
case ByteArrayType::Location::Storage:
{
// this copies source to target and also clears target if it was larger
// stack: source_ref target_ref
// store target_ref
m_context << eth::Instruction::SWAP1 << eth::Instruction::DUP2;
// fetch lengthes
m_context << eth::Instruction::DUP1 << eth::Instruction::SLOAD << eth::Instruction::SWAP2
<< eth::Instruction::DUP1 << eth::Instruction::SLOAD;
// stack: target_ref target_len_bytes target_ref source_ref source_len_bytes
// store new target length
m_context << eth::Instruction::DUP1 << eth::Instruction::DUP4 << eth::Instruction::SSTORE;
// compute hashes (data positions)
m_context << eth::Instruction::SWAP2;
CompilerUtils(m_context).computeHashStatic();
m_context << eth::Instruction::SWAP1;
CompilerUtils(m_context).computeHashStatic();
// stack: target_ref target_len_bytes source_len_bytes target_data_pos source_data_pos
// convert lengthes from bytes to storage slots
m_context << u256(31) << u256(32) << eth::Instruction::DUP1 << eth::Instruction::DUP3
<< eth::Instruction::DUP8 << eth::Instruction::ADD << eth::Instruction::DIV
<< eth::Instruction::SWAP2
<< eth::Instruction::DUP6 << eth::Instruction::ADD << eth::Instruction::DIV;
// stack: target_ref target_len_bytes source_len_bytes target_data_pos source_data_pos target_len source_len
// @todo we might be able to go without a third counter
m_context << u256(0);
// stack: target_ref target_len_bytes source_len_bytes target_data_pos source_data_pos target_len source_len counter
eth::AssemblyItem copyLoopStart = m_context.newTag();
m_context << copyLoopStart;
// check for loop condition
m_context << eth::Instruction::DUP1 << eth::Instruction::DUP3
<< eth::Instruction::GT << eth::Instruction::ISZERO;
eth::AssemblyItem copyLoopEnd = m_context.newTag();
m_context.appendConditionalJumpTo(copyLoopEnd);
// copy
m_context << eth::Instruction::DUP4 << eth::Instruction::DUP2 << eth::Instruction::ADD
<< eth::Instruction::SLOAD
<< eth::Instruction::DUP6 << eth::Instruction::DUP3 << eth::Instruction::ADD
<< eth::Instruction::SSTORE;
// increment
m_context << u256(1) << eth::Instruction::ADD;
m_context.appendJumpTo(copyLoopStart);
m_context << copyLoopEnd;
// zero-out leftovers in target
// stack: target_ref target_len_bytes source_len_bytes target_data_pos source_data_pos target_len source_len counter
// add counter to target_data_pos
m_context << eth::Instruction::DUP5 << eth::Instruction::ADD
<< eth::Instruction::SWAP5 << eth::Instruction::POP;
// stack: target_ref target_len_bytes target_data_pos_updated target_data_pos source_data_pos target_len source_len
// add length to target_data_pos to get target_data_end
m_context << eth::Instruction::POP << eth::Instruction::DUP3 << eth::Instruction::ADD
<< eth::Instruction::SWAP4
<< eth::Instruction::POP << eth::Instruction::POP << eth::Instruction::POP;
// stack: target_ref target_data_end target_data_pos_updated
clearStorageLoop();
m_context << eth::Instruction::POP;
break;
}
default:
solAssert(false, "Given byte array location not implemented.");
}
}
void CompilerUtils::clearByteArray(ByteArrayType const& _type) const
{
solAssert(_type.getLocation() == ByteArrayType::Location::Storage, "");
// fetch length
m_context << eth::Instruction::DUP1 << eth::Instruction::SLOAD;
// set length to zero
m_context << u256(0) << eth::Instruction::DUP3 << eth::Instruction::SSTORE;
// convert length from bytes to storage slots
m_context << u256(31) << eth::Instruction::ADD
<< u256(32) << eth::Instruction::SWAP1 << eth::Instruction::DIV;
// compute data positions
m_context << eth::Instruction::SWAP1;
CompilerUtils(m_context).computeHashStatic();
// stack: len data_pos
m_context << eth::Instruction::SWAP1 << eth::Instruction::DUP2 << eth::Instruction::ADD
<< eth::Instruction::SWAP1;
clearStorageLoop();
// cleanup
m_context << eth::Instruction::POP;
}
unsigned CompilerUtils::prepareMemoryStore(Type const& _type, bool _padToWordBoundaries) const
{
unsigned _encodedSize = _type.getCalldataEncodedSize();
unsigned numBytes = _padToWordBoundaries ? getPaddedSize(_encodedSize) : _encodedSize;
bool leftAligned = _type.getCategory() == Type::Category::String;
if (numBytes == 0)
m_context << eth::Instruction::POP;
else
{
solAssert(numBytes <= 32, "Memory store of more than 32 bytes requested.");
if (numBytes != 32 && !leftAligned && !_padToWordBoundaries)
// shift the value accordingly before storing
m_context << (u256(1) << ((32 - numBytes) * 8)) << eth::Instruction::MUL;
}
return numBytes;
}
void CompilerUtils::clearStorageLoop() const
{
// stack: end_pos pos
eth::AssemblyItem loopStart = m_context.newTag();
m_context << loopStart;
// check for loop condition
m_context << eth::Instruction::DUP1 << eth::Instruction::DUP3
<< eth::Instruction::GT << eth::Instruction::ISZERO;
eth::AssemblyItem zeroLoopEnd = m_context.newTag();
m_context.appendConditionalJumpTo(zeroLoopEnd);
// zero out
m_context << u256(0) << eth::Instruction::DUP2 << eth::Instruction::SSTORE;
// increment
m_context << u256(1) << eth::Instruction::ADD;
m_context.appendJumpTo(loopStart);
// cleanup
m_context << zeroLoopEnd;
m_context << eth::Instruction::POP;
}
}
}

33
libsolidity/CompilerUtils.h

@ -47,13 +47,16 @@ public:
bool _fromCalldata = false, bool _padToWordBoundaries = 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)
/// @param _type type of the data on the stack
/// @param _padToWordBoundaries if true, pad the data to word (32 byte) boundaries
/// @returns the number of bytes written to memory (can be different from _bytes if
/// _padToWordBoundaries is true)
unsigned storeInMemory(unsigned _offset, unsigned _bytes = 32, bool _leftAligned = false,
bool _padToWordBoundaries = false);
unsigned storeInMemory(unsigned _offset, Type const& _type = IntegerType(256), bool _padToWordBoundaries = false);
/// Dynamic version of @see storeInMemory, expects the memory offset below the value on the stack
/// and also updates that.
/// Stack pre: memory_offset value...
/// Stack post: (memory_offset+length)
void storeInMemoryDynamic(Type const& _type, bool _padToWordBoundaries = true);
/// @returns _size rounded up to the next multiple of 32 (the number of bytes occupied in the
/// padded calldata)
static unsigned getPaddedSize(unsigned _size) { return ((_size + 31) / 32) * 32; }
@ -69,10 +72,32 @@ public:
static unsigned getSizeOnStack(std::vector<T> const& _variables);
static unsigned getSizeOnStack(std::vector<std::shared_ptr<Type const>> const& _variableTypes);
/// Appends code that computes tha SHA3 hash of the topmost stack element of type @a _type.
/// If @a _pad is set, padds the type to muliples of 32 bytes.
/// @note Only works for types of fixed size.
void computeHashStatic(Type const& _type = IntegerType(256), bool _padToWordBoundaries = false);
/// Copies a byte array to a byte array in storage.
/// Stack pre: [source_reference] target_reference
/// Stack post: target_reference
void copyByteArrayToStorage(ByteArrayType const& _targetType, ByteArrayType const& _sourceType) const;
/// Clears the length and data elements of the byte array referenced on the stack.
/// Stack pre: reference
/// Stack post:
void clearByteArray(ByteArrayType const& _type) const;
/// Bytes we need to the start of call data.
/// - The size in bytes of the function (hash) identifier.
static const unsigned int dataStartOffset;
private:
/// Prepares the given type for storing in memory by shifting it if necessary.
unsigned prepareMemoryStore(Type const& _type, bool _padToWordBoundaries) const;
/// Appends a loop that clears a sequence of storage slots (excluding end).
/// Stack pre: end_ref start_ref
/// Stack post: end_ref
void clearStorageLoop() const;
CompilerContext& m_context;
};

203
libsolidity/ExpressionCompiler.cpp

@ -59,13 +59,15 @@ void ExpressionCompiler::appendStateVariableAccessor(CompilerContext& _context,
bool ExpressionCompiler::visit(Assignment const& _assignment)
{
_assignment.getRightHandSide().accept(*this);
appendTypeConversion(*_assignment.getRightHandSide().getType(), *_assignment.getType());
if (_assignment.getType()->isValueType())
appendTypeConversion(*_assignment.getRightHandSide().getType(), *_assignment.getType());
_assignment.getLeftHandSide().accept(*this);
solAssert(m_currentLValue.isValid(), "LValue not retrieved.");
Token::Value op = _assignment.getAssignmentOperator();
if (op != Token::Assign) // compound assignment
{
solAssert(_assignment.getType()->isValueType(), "Compound operators not implemented for non-value types.");
if (m_currentLValue.storesReferenceOnStack())
m_context << eth::Instruction::SWAP1 << eth::Instruction::DUP2;
m_currentLValue.retrieveValue(_assignment.getType(), _assignment.getLocation(), true);
@ -73,7 +75,7 @@ bool ExpressionCompiler::visit(Assignment const& _assignment)
if (m_currentLValue.storesReferenceOnStack())
m_context << eth::Instruction::SWAP1;
}
m_currentLValue.storeValue(_assignment);
m_currentLValue.storeValue(_assignment, *_assignment.getRightHandSide().getType());
m_currentLValue.reset();
return false;
@ -103,7 +105,7 @@ bool ExpressionCompiler::visit(UnaryOperation const& _unaryOperation)
break;
case Token::Delete: // delete
solAssert(m_currentLValue.isValid(), "LValue not retrieved.");
m_currentLValue.setToZero(_unaryOperation);
m_currentLValue.setToZero(_unaryOperation, *_unaryOperation.getSubExpression().getType());
m_currentLValue.reset();
break;
case Token::Inc: // ++ (pre- or postfix)
@ -126,7 +128,7 @@ bool ExpressionCompiler::visit(UnaryOperation const& _unaryOperation)
// Stack for postfix: *ref [ref] (*ref)+-1
if (m_currentLValue.storesReferenceOnStack())
m_context << eth::Instruction::SWAP1;
m_currentLValue.storeValue(_unaryOperation, !_unaryOperation.isPrefixOperation());
m_currentLValue.storeValue(_unaryOperation, *_unaryOperation.getType(), !_unaryOperation.isPrefixOperation());
m_currentLValue.reset();
break;
case Token::Add: // +
@ -274,10 +276,10 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
//@todo copy to memory position 0, shift as soon as we use memory
m_context << u256(0) << eth::Instruction::CODECOPY;
unsigned length = bytecode.size();
length += appendArgumentsCopyToMemory(arguments, function.getParameterTypes(), length);
m_context << u256(bytecode.size());
appendArgumentsCopyToMemory(arguments, function.getParameterTypes());
// size, offset, endowment
m_context << u256(length) << u256(0);
m_context << u256(0);
if (function.valueSet())
m_context << eth::dupInstruction(3);
else
@ -327,8 +329,9 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
break;
case Location::SHA3:
{
unsigned length = appendArgumentsCopyToMemory(arguments, TypePointers(), 0, function.padArguments());
m_context << u256(length) << u256(0) << eth::Instruction::SHA3;
m_context << u256(0);
appendArgumentsCopyToMemory(arguments, TypePointers(), function.padArguments());
m_context << u256(0) << eth::Instruction::SHA3;
break;
}
case Location::Log0:
@ -343,23 +346,16 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
arguments[arg]->accept(*this);
appendTypeConversion(*arguments[arg]->getType(), *function.getParameterTypes()[arg], true);
}
unsigned length = appendExpressionCopyToMemory(*function.getParameterTypes().front(),
*arguments.front());
solAssert(length == 32, "Log data should be 32 bytes long (for now).");
m_context << u256(length) << u256(0) << eth::logInstruction(logNumber);
m_context << u256(0);
appendExpressionCopyToMemory(*function.getParameterTypes().front(), *arguments.front());
m_context << u256(0) << eth::logInstruction(logNumber);
break;
}
case Location::Event:
{
_functionCall.getExpression().accept(*this);
auto const& event = dynamic_cast<EventDefinition const&>(function.getDeclaration());
// Copy all non-indexed arguments to memory (data)
unsigned numIndexed = 0;
unsigned memLength = 0;
for (unsigned arg = 0; arg < arguments.size(); ++arg)
if (!event.getParameters()[arg]->isIndexed())
memLength += appendExpressionCopyToMemory(*function.getParameterTypes()[arg],
*arguments[arg], memLength);
// All indexed arguments go to the stack
for (unsigned arg = arguments.size(); arg > 0; --arg)
if (event.getParameters()[arg - 1]->isIndexed())
@ -372,7 +368,12 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
m_context << u256(h256::Arith(dev::sha3(function.getCanonicalSignature(event.getName()))));
++numIndexed;
solAssert(numIndexed <= 4, "Too many indexed arguments.");
m_context << u256(memLength) << u256(0) << eth::logInstruction(numIndexed);
// Copy all non-indexed arguments to memory (data)
m_context << u256(0);
for (unsigned arg = 0; arg < arguments.size(); ++arg)
if (!event.getParameters()[arg]->isIndexed())
appendExpressionCopyToMemory(*function.getParameterTypes()[arg], *arguments[arg]);
m_context << u256(0) << eth::logInstruction(numIndexed);
break;
}
case Location::BlockHash:
@ -472,6 +473,10 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess)
m_context << eth::Instruction::GAS;
else if (member == "gasprice")
m_context << eth::Instruction::GASPRICE;
else if (member == "data")
{
// nothing to store on the stack
}
else
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown magic member."));
break;
@ -508,12 +513,16 @@ bool ExpressionCompiler::visit(IndexAccess const& _indexAccess)
{
_indexAccess.getBaseExpression().accept(*this);
TypePointer const& keyType = dynamic_cast<MappingType const&>(*_indexAccess.getBaseExpression().getType()).getKeyType();
unsigned length = appendExpressionCopyToMemory(*keyType, _indexAccess.getIndexExpression());
solAssert(length == 32, "Mapping key has to take 32 bytes in memory (for now).");
// @todo move this once we actually use memory
length += CompilerUtils(m_context).storeInMemory(length);
m_context << u256(length) << u256(0) << eth::Instruction::SHA3;
Type const& baseType = *_indexAccess.getBaseExpression().getType();
solAssert(baseType.getCategory() == Type::Category::Mapping, "");
Type const& keyType = *dynamic_cast<MappingType const&>(baseType).getKeyType();
m_context << u256(0);
appendExpressionCopyToMemory(keyType, _indexAccess.getIndexExpression());
solAssert(baseType.getSizeOnStack() == 1,
"Unexpected: Not exactly one stack slot taken by subscriptable expression.");
m_context << eth::Instruction::SWAP1;
appendTypeMoveToMemory(IntegerType(256));
m_context << u256(0) << eth::Instruction::SHA3;
m_currentLValue = LValue(m_context, LValue::LValueType::Storage, *_indexAccess.getType());
m_currentLValue.retrieveValueIfLValueNotRequested(_indexAccess);
@ -804,26 +813,30 @@ void ExpressionCompiler::appendExternalFunctionCall(FunctionType const& _functio
unsigned gasStackPos = m_context.currentToBaseStackOffset(gasValueSize);
unsigned valueStackPos = m_context.currentToBaseStackOffset(1);
if (!bare)
//@todo only return the first return value for now
Type const* firstType = _functionType.getReturnParameterTypes().empty() ? nullptr :
_functionType.getReturnParameterTypes().front().get();
unsigned retSize = firstType ? CompilerUtils::getPaddedSize(firstType->getCalldataEncodedSize()) : 0;
m_context << u256(retSize) << u256(0);
if (bare)
m_context << u256(0);
else
{
// copy function identifier
m_context << eth::dupInstruction(gasValueSize + 1);
CompilerUtils(m_context).storeInMemory(0, CompilerUtils::dataStartOffset);
m_context << eth::dupInstruction(gasValueSize + 3);
CompilerUtils(m_context).storeInMemory(0, IntegerType(CompilerUtils::dataStartOffset * 8));
m_context << u256(CompilerUtils::dataStartOffset);
}
// reserve space for the function identifier
unsigned dataOffset = bare ? 0 : CompilerUtils::dataStartOffset;
// For bare call, activate "4 byte pad exception": If the first argument has exactly 4 bytes,
// do not pad it to 32 bytes.
dataOffset += appendArgumentsCopyToMemory(_arguments, _functionType.getParameterTypes(), dataOffset,
_functionType.padArguments(), bare);
appendArgumentsCopyToMemory(_arguments, _functionType.getParameterTypes(),
_functionType.padArguments(), bare);
//@todo only return the first return value for now
Type const* firstType = _functionType.getReturnParameterTypes().empty() ? nullptr :
_functionType.getReturnParameterTypes().front().get();
unsigned retSize = firstType ? CompilerUtils::getPaddedSize(firstType->getCalldataEncodedSize()) : 0;
// CALL arguments: outSize, outOff, inSize, inOff, value, addr, gas (stack top)
m_context << u256(retSize) << u256(0) << u256(dataOffset) << u256(0);
// CALL arguments: outSize, outOff, inSize, (already present up to here)
// inOff, value, addr, gas (stack top)
m_context << u256(0);
if (_functionType.valueSet())
m_context << eth::dupInstruction(m_context.baseToCurrentStackOffset(valueStackPos));
else
@ -852,14 +865,12 @@ void ExpressionCompiler::appendExternalFunctionCall(FunctionType const& _functio
}
}
unsigned ExpressionCompiler::appendArgumentsCopyToMemory(vector<ASTPointer<Expression const>> const& _arguments,
TypePointers const& _types,
unsigned _memoryOffset,
bool _padToWordBoundaries,
bool _padExceptionIfFourBytes)
void ExpressionCompiler::appendArgumentsCopyToMemory(vector<ASTPointer<Expression const>> const& _arguments,
TypePointers const& _types,
bool _padToWordBoundaries,
bool _padExceptionIfFourBytes)
{
solAssert(_types.empty() || _types.size() == _arguments.size(), "");
unsigned length = 0;
for (size_t i = 0; i < _arguments.size(); ++i)
{
_arguments[i]->accept(*this);
@ -869,31 +880,20 @@ unsigned ExpressionCompiler::appendArgumentsCopyToMemory(vector<ASTPointer<Expre
// Do not pad if the first argument has exactly four bytes
if (i == 0 && pad && _padExceptionIfFourBytes && expectedType->getCalldataEncodedSize() == 4)
pad = false;
length += appendTypeMoveToMemory(*expectedType, _arguments[i]->getLocation(),
_memoryOffset + length, pad);
appendTypeMoveToMemory(*expectedType, pad);
}
return length;
}
unsigned ExpressionCompiler::appendTypeMoveToMemory(Type const& _type, Location const& _location, unsigned _memoryOffset, bool _padToWordBoundaries)
void ExpressionCompiler::appendTypeMoveToMemory(Type const& _type, bool _padToWordBoundaries)
{
unsigned const c_encodedSize = _type.getCalldataEncodedSize();
unsigned const c_numBytes = _padToWordBoundaries ? CompilerUtils::getPaddedSize(c_encodedSize) : c_encodedSize;
if (c_numBytes == 0 || c_numBytes > 32)
BOOST_THROW_EXCEPTION(CompilerError()
<< errinfo_sourceLocation(_location)
<< errinfo_comment("Type " + _type.toString() + " not yet supported."));
bool const c_leftAligned = _type.getCategory() == Type::Category::String;
return CompilerUtils(m_context).storeInMemory(_memoryOffset, c_numBytes, c_leftAligned, _padToWordBoundaries);
CompilerUtils(m_context).storeInMemoryDynamic(_type, _padToWordBoundaries);
}
unsigned ExpressionCompiler::appendExpressionCopyToMemory(Type const& _expectedType,
Expression const& _expression,
unsigned _memoryOffset)
void ExpressionCompiler::appendExpressionCopyToMemory(Type const& _expectedType, Expression const& _expression)
{
_expression.accept(*this);
appendTypeConversion(*_expression.getType(), _expectedType, true);
return appendTypeMoveToMemory(_expectedType, _expression.getLocation(), _memoryOffset);
appendTypeMoveToMemory(_expectedType);
}
void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const& _varDecl)
@ -904,7 +904,7 @@ void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const&
TypePointers const& paramTypes = accessorType.getParameterTypes();
// move arguments to memory
for (TypePointer const& paramType: boost::adaptors::reverse(paramTypes))
length += appendTypeMoveToMemory(*paramType, Location(), length);
length += CompilerUtils(m_context).storeInMemory(length, *paramType, true);
// retrieve the position of the variable
m_context << m_context.getStorageLocationOfVariable(_varDecl);
@ -1014,7 +1014,7 @@ void ExpressionCompiler::LValue::retrieveValueFromStorage(TypePointer const& _ty
}
}
void ExpressionCompiler::LValue::storeValue(Expression const& _expression, bool _move) const
void ExpressionCompiler::LValue::storeValue(Expression const& _expression, Type const& _sourceType, bool _move) const
{
switch (m_type)
{
@ -1032,28 +1032,46 @@ void ExpressionCompiler::LValue::storeValue(Expression const& _expression, bool
break;
}
case LValueType::Storage:
if (!_expression.getType()->isValueType())
break; // no distinction between value and reference for non-value types
// stack layout: value value ... value ref
if (!_move) // copy values
// stack layout: value value ... value target_ref
if (_expression.getType()->isValueType())
{
if (m_size + 1 > 16)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(_expression.getLocation())
<< errinfo_comment("Stack too deep."));
if (!_move) // copy values
{
if (m_size + 1 > 16)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(_expression.getLocation())
<< errinfo_comment("Stack too deep."));
for (unsigned i = 0; i < m_size; ++i)
*m_context << eth::dupInstruction(m_size + 1) << eth::Instruction::SWAP1;
}
if (m_size > 0) // store high index value first
*m_context << u256(m_size - 1) << eth::Instruction::ADD;
for (unsigned i = 0; i < m_size; ++i)
*m_context << eth::dupInstruction(m_size + 1) << eth::Instruction::SWAP1;
{
if (i + 1 >= m_size)
*m_context << eth::Instruction::SSTORE;
else
// stack here: value value ... value value (target_ref+offset)
*m_context << eth::Instruction::SWAP1 << eth::Instruction::DUP2
<< eth::Instruction::SSTORE
<< u256(1) << eth::Instruction::SWAP1 << eth::Instruction::SUB;
}
}
if (m_size > 0) // store high index value first
*m_context << u256(m_size - 1) << eth::Instruction::ADD;
for (unsigned i = 0; i < m_size; ++i)
else
{
if (i + 1 >= m_size)
*m_context << eth::Instruction::SSTORE;
solAssert(!_move, "Move assign for non-value types not implemented.");
solAssert(_sourceType.getCategory() == _expression.getType()->getCategory(), "");
if (_expression.getType()->getCategory() == Type::Category::ByteArray)
CompilerUtils(*m_context).copyByteArrayToStorage(
dynamic_cast<ByteArrayType const&>(*_expression.getType()),
dynamic_cast<ByteArrayType const&>(_sourceType));
else if (_expression.getType()->getCategory() == Type::Category::Struct)
{
//@todo
solAssert(false, "Struct copy not yet implemented.");
}
else
// v v ... v v r+x
*m_context << eth::Instruction::SWAP1 << eth::Instruction::DUP2
<< eth::Instruction::SSTORE
<< u256(1) << eth::Instruction::SWAP1 << eth::Instruction::SUB;
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_sourceLocation(_expression.getLocation())
<< errinfo_comment("Invalid non-value type for assignment."));
}
break;
case LValueType::Memory:
@ -1069,7 +1087,7 @@ void ExpressionCompiler::LValue::storeValue(Expression const& _expression, bool
}
}
void ExpressionCompiler::LValue::setToZero(Expression const& _expression) const
void ExpressionCompiler::LValue::setToZero(Expression const& _expression, Type const& _type) const
{
switch (m_type)
{
@ -1086,20 +1104,21 @@ void ExpressionCompiler::LValue::setToZero(Expression const& _expression) const
break;
}
case LValueType::Storage:
if (m_size == 0)
*m_context << eth::Instruction::POP;
for (unsigned i = 0; i < m_size; ++i)
if (_type.getCategory() == Type::Category::ByteArray)
CompilerUtils(*m_context).clearByteArray(dynamic_cast<ByteArrayType const&>(_type));
else
{
if (i + 1 >= m_size)
*m_context << u256(0) << eth::Instruction::SWAP1 << eth::Instruction::SSTORE;
else
*m_context << u256(0) << eth::Instruction::DUP2 << eth::Instruction::SSTORE
<< u256(1) << eth::Instruction::ADD;
if (m_size == 0)
*m_context << eth::Instruction::POP;
for (unsigned i = 0; i < m_size; ++i)
if (i + 1 >= m_size)
*m_context << u256(0) << eth::Instruction::SWAP1 << eth::Instruction::SSTORE;
else
*m_context << u256(0) << eth::Instruction::DUP2 << eth::Instruction::SSTORE
<< u256(1) << eth::Instruction::ADD;
}
break;
case LValueType::Memory:
if (!_expression.getType()->isValueType())
break; // no distinction between value and reference for non-value types
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_sourceLocation(_expression.getLocation())
<< errinfo_comment("Location type not yet implemented."));
break;
@ -1108,7 +1127,6 @@ void ExpressionCompiler::LValue::setToZero(Expression const& _expression) const
<< errinfo_comment("Unsupported location type."));
break;
}
}
void ExpressionCompiler::LValue::retrieveValueIfLValueNotRequested(Expression const& _expression)
@ -1145,5 +1163,6 @@ void ExpressionCompiler::LValue::fromIdentifier(Identifier const& _identifier, D
<< errinfo_comment("Identifier type not supported or identifier not found."));
}
}
}

34
libsolidity/ExpressionCompiler.h

@ -92,21 +92,18 @@ private:
/// Appends code to call a function of the given type with the given arguments.
void appendExternalFunctionCall(FunctionType const& _functionType, std::vector<ASTPointer<Expression const>> const& _arguments,
bool bare = false);
/// Appends code that evaluates the given arguments and moves the result to memory (with optional offset).
/// @returns the number of bytes moved to memory
unsigned appendArgumentsCopyToMemory(std::vector<ASTPointer<Expression const>> const& _arguments,
TypePointers const& _types = {},
unsigned _memoryOffset = 0,
bool _padToWordBoundaries = true,
bool _padExceptionIfFourBytes = false);
/// Appends code that moves a stack element of the given type to memory
/// @returns the number of bytes moved to memory
unsigned appendTypeMoveToMemory(Type const& _type, Location const& _location, unsigned _memoryOffset,
bool _padToWordBoundaries = true);
/// Appends code that evaluates a single expression and moves the result to memory (with optional offset).
/// @returns the number of bytes moved to memory
unsigned appendExpressionCopyToMemory(Type const& _expectedType, Expression const& _expression,
unsigned _memoryOffset = 0);
/// Appends code that evaluates the given arguments and moves the result to memory. The memory offset is
/// expected to be on the stack and is updated by this call.
void appendArgumentsCopyToMemory(std::vector<ASTPointer<Expression const>> const& _arguments,
TypePointers const& _types = {},
bool _padToWordBoundaries = true,
bool _padExceptionIfFourBytes = false);
/// Appends code that moves a stack element of the given type to memory. The memory offset is
/// expected below the stack element and is updated by this call.
void appendTypeMoveToMemory(Type const& _type, bool _padToWordBoundaries = true);
/// Appends code that evaluates a single expression and moves the result to memory. The memory offset is
/// expected to be on the stack and is updated by this call.
void appendExpressionCopyToMemory(Type const& _expectedType, Expression const& _expression);
/// Appends code for a State Variable accessor function
void appendStateVariableAccessor(VariableDeclaration const& _varDecl);
@ -148,10 +145,11 @@ private:
/// be on the top of the stack, if any) in the lvalue and removes the reference.
/// Also removes the stored value from the stack if @a _move is
/// true. @a _expression is the current expression, used for error reporting.
void storeValue(Expression const& _expression, bool _move = false) const;
/// @a _sourceType is the type of the expression that is assigned.
void storeValue(Expression const& _expression, Type const& _sourceType, bool _move = false) const;
/// Stores zero in the lvalue.
/// @a _expression is the current expression, used for error reporting.
void setToZero(Expression const& _expression) const;
void setToZero(Expression const& _expression, Type const& _type) const;
/// Convenience function to convert the stored reference to a value and reset type to NONE if
/// the reference was not requested by @a _expression.
void retrieveValueIfLValueNotRequested(Expression const& _expression);
@ -159,6 +157,8 @@ private:
private:
/// Convenience function to retrieve Value from Storage. Specific version of @ref retrieveValue
void retrieveValueFromStorage(TypePointer const& _type, bool _remove = false) const;
/// Copies from a byte array to a byte array in storage, both references on the stack.
void copyByteArrayToStorage(ByteArrayType const& _targetType, ByteArrayType const& _sourceType) const;
CompilerContext* m_context;
LValueType m_type = LValueType::None;

2
libsolidity/InterfaceHandler.h

@ -28,7 +28,7 @@
#include <string>
#include <memory>
#include <jsoncpp/json/json.h>
#include <json/json.h>
namespace dev
{

6
libsolidity/Token.h

@ -176,8 +176,7 @@ namespace solidity
K(SubFinney, "finney", 0) \
K(SubEther, "ether", 0) \
/* type keywords, keep them in this order, keep int as first keyword
* the implementation in Types.cpp has to be synced to this here
* TODO more to be added */ \
* the implementation in Types.cpp has to be synced to this here */\
K(Int, "int", 0) \
K(Int8, "int8", 0) \
K(Int16, "int16", 0) \
@ -279,7 +278,8 @@ namespace solidity
K(Hash256, "hash256", 0) \
K(Address, "address", 0) \
K(Bool, "bool", 0) \
K(StringType, "string", 0) \
K(Bytes, "bytes", 0) \
K(StringType, "string", 0) \
K(String0, "string0", 0) \
K(String1, "string1", 0) \
K(String2, "string2", 0) \

70
libsolidity/Types.cpp

@ -35,7 +35,7 @@ namespace dev
namespace solidity
{
shared_ptr<Type const> Type::fromElementaryTypeName(Token::Value _typeToken)
TypePointer Type::fromElementaryTypeName(Token::Value _typeToken)
{
solAssert(Token::isElementaryTypeName(_typeToken), "Elementary type name expected.");
@ -57,12 +57,19 @@ shared_ptr<Type const> Type::fromElementaryTypeName(Token::Value _typeToken)
return make_shared<BoolType>();
else if (Token::String0 <= _typeToken && _typeToken <= Token::String32)
return make_shared<StaticStringType>(int(_typeToken) - int(Token::String0));
else if (_typeToken == Token::Bytes)
return make_shared<ByteArrayType>(ByteArrayType::Location::Storage, 0, 0, true);
else
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unable to convert elementary typename " +
std::string(Token::toString(_typeToken)) + " to type."));
}
shared_ptr<Type const> Type::fromUserDefinedTypeName(UserDefinedTypeName const& _typeName)
TypePointer Type::fromElementaryTypeName(string const& _name)
{
return fromElementaryTypeName(Token::fromIdentifierOrKeyword(_name));
}
TypePointer Type::fromUserDefinedTypeName(UserDefinedTypeName const& _typeName)
{
Declaration const* declaration = _typeName.getReferencedDeclaration();
if (StructDefinition const* structDef = dynamic_cast<StructDefinition const*>(declaration))
@ -71,21 +78,21 @@ shared_ptr<Type const> Type::fromUserDefinedTypeName(UserDefinedTypeName const&
return make_shared<FunctionType>(*function);
else if (ContractDefinition const* contract = dynamic_cast<ContractDefinition const*>(declaration))
return make_shared<ContractType>(*contract);
return shared_ptr<Type const>();
return TypePointer();
}
shared_ptr<Type const> Type::fromMapping(Mapping const& _typeName)
TypePointer Type::fromMapping(Mapping const& _typeName)
{
shared_ptr<Type const> keyType = _typeName.getKeyType().toType();
TypePointer keyType = _typeName.getKeyType().toType();
if (!keyType)
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Error resolving type name."));
shared_ptr<Type const> valueType = _typeName.getValueType().toType();
TypePointer valueType = _typeName.getValueType().toType();
if (!valueType)
BOOST_THROW_EXCEPTION(_typeName.getValueType().createTypeError("Invalid type name"));
return make_shared<MappingType>(keyType, valueType);
}
shared_ptr<Type const> Type::forLiteral(Literal const& _literal)
TypePointer Type::forLiteral(Literal const& _literal)
{
switch (_literal.getToken())
{
@ -506,6 +513,40 @@ TypePointer ContractType::unaryOperatorResult(Token::Value _operator) const
return _operator == Token::Delete ? make_shared<VoidType>() : TypePointer();
}
bool ByteArrayType::isImplicitlyConvertibleTo(const Type& _convertTo) const
{
if (*this == _convertTo)
return true;
if (_convertTo.getCategory() != Category::ByteArray)
return false;
auto const& other = dynamic_cast<ByteArrayType const&>(_convertTo);
return (m_dynamicLength == other.m_dynamicLength || m_length == other.m_length);
}
TypePointer ByteArrayType::unaryOperatorResult(Token::Value _operator) const
{
if (_operator == Token::Delete)
return make_shared<VoidType>();
return TypePointer();
}
bool ByteArrayType::operator==(Type const& _other) const
{
if (_other.getCategory() != getCategory())
return false;
ByteArrayType const& other = dynamic_cast<ByteArrayType const&>(_other);
return other.m_location == m_location && other.m_dynamicLength == m_dynamicLength
&& other.m_length == m_length && other.m_offset == m_offset;
}
unsigned ByteArrayType::getSizeOnStack() const
{
if (m_location == Location::CallData)
return 0;
else
return 1;
}
bool ContractType::operator==(Type const& _other) const
{
if (_other.getCategory() != getCategory())
@ -525,8 +566,8 @@ MemberList const& ContractType::getMembers() const
if (!m_members)
{
// All address members and all interface functions
map<string, shared_ptr<Type const>> members(IntegerType::AddressMemberList.begin(),
IntegerType::AddressMemberList.end());
map<string, TypePointer> members(IntegerType::AddressMemberList.begin(),
IntegerType::AddressMemberList.end());
if (m_super)
{
for (ContractDefinition const* base: m_contract.getLinearizedBaseContracts())
@ -581,14 +622,14 @@ bool StructType::operator==(Type const& _other) const
u256 StructType::getStorageSize() const
{
u256 size = 0;
for (pair<string, shared_ptr<Type const>> const& member: getMembers())
for (pair<string, TypePointer> const& member: getMembers())
size += member.second->getStorageSize();
return max<u256>(1, size);
}
bool StructType::canLiveOutsideStorage() const
{
for (pair<string, shared_ptr<Type const>> const& member: getMembers())
for (pair<string, TypePointer> const& member: getMembers())
if (!member.second->canLiveOutsideStorage())
return false;
return true;
@ -604,7 +645,7 @@ MemberList const& StructType::getMembers() const
// We need to lazy-initialize it because of recursive references.
if (!m_members)
{
map<string, shared_ptr<Type const>> members;
map<string, TypePointer> members;
for (ASTPointer<VariableDeclaration> const& variable: m_struct.getMembers())
members[variable->getName()] = variable->getType();
m_members.reset(new MemberList(members));
@ -811,7 +852,7 @@ TypePointers FunctionType::parseElementaryTypeVector(strings const& _types)
TypePointers pointers;
pointers.reserve(_types.size());
for (string const& type: _types)
pointers.push_back(Type::fromElementaryTypeName(Token::fromIdentifierOrKeyword(type)));
pointers.push_back(Type::fromElementaryTypeName(type));
return pointers;
}
@ -941,7 +982,8 @@ MagicType::MagicType(MagicType::Kind _kind):
case Kind::Message:
m_members = MemberList({{"sender", make_shared<IntegerType>(0, IntegerType::Modifier::Address)},
{"gas", make_shared<IntegerType>(256)},
{"value", make_shared<IntegerType>(256)}});
{"value", make_shared<IntegerType>(256)},
{"data", make_shared<ByteArrayType>(ByteArrayType::Location::CallData)}});
break;
case Kind::Transaction:
m_members = MemberList({{"origin", make_shared<IntegerType>(0, IntegerType::Modifier::Address)},

40
libsolidity/Types.h

@ -76,15 +76,17 @@ class Type: private boost::noncopyable, public std::enable_shared_from_this<Type
public:
enum class Category
{
Integer, IntegerConstant, Bool, Real,
String, Contract, Struct, Function,
Mapping, Void, TypeType, Modifier, Magic
Integer, IntegerConstant, Bool, Real, String,
ByteArray, Mapping,
Contract, Struct, Function,
Void, TypeType, Modifier, Magic
};
///@{
///@name Factory functions
/// Factory functions that convert an AST @ref TypeName to a Type.
static TypePointer fromElementaryTypeName(Token::Value _typeToken);
static TypePointer fromElementaryTypeName(std::string const& _name);
static TypePointer fromUserDefinedTypeName(UserDefinedTypeName const& _typeName);
static TypePointer fromMapping(Mapping const& _typeName);
static TypePointer fromFunction(FunctionDefinition const& _function);
@ -263,7 +265,7 @@ class BoolType: public Type
{
public:
BoolType() {}
virtual Category getCategory() const { return Category::Bool; }
virtual Category getCategory() const override { return Category::Bool; }
virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
virtual TypePointer unaryOperatorResult(Token::Value _operator) const override;
virtual TypePointer binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const override;
@ -275,6 +277,36 @@ public:
virtual u256 literalValue(Literal const* _literal) const override;
};
/**
* The type of a byte array, prototype for a general array.
*/
class ByteArrayType: public Type
{
public:
enum class Location { Storage, CallData, Memory };
virtual Category getCategory() const override { return Category::ByteArray; }
explicit ByteArrayType(Location _location, u256 const& _offset = 0, u256 const& _length = 0,
bool _dynamicLength = false):
m_location(_location), m_offset(_offset), m_length(_length), m_dynamicLength(_dynamicLength) {}
virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override;
virtual TypePointer unaryOperatorResult(Token::Value _operator) const override;
virtual bool operator==(const Type& _other) const override;
virtual unsigned getSizeOnStack() const override;
virtual std::string toString() const override { return "bytes"; }
Location getLocation() const { return m_location; }
u256 const& getOffset() const { return m_offset; }
u256 const& getLength() const { return m_length; }
bool hasDynamicLength() const { return m_dynamicLength; }
private:
Location m_location;
u256 m_offset;
u256 m_length;
bool m_dynamicLength;
};
/**
* The type of a contract instance, there is one distinct type for each contract definition.
*/

6
libwebthree/WebThree.cpp

@ -35,15 +35,15 @@ using namespace dev::p2p;
using namespace dev::eth;
using namespace dev::shh;
WebThreeDirect::WebThreeDirect(std::string const& _clientVersion, std::string const& _dbPath, bool _forceClean, std::set<std::string> const& _interfaces, NetworkPreferences const& _n, bytesConstRef _network):
WebThreeDirect::WebThreeDirect(std::string const& _clientVersion, std::string const& _dbPath, bool _forceClean, std::set<std::string> const& _interfaces, NetworkPreferences const& _n, bytesConstRef _network, int miners):
m_clientVersion(_clientVersion),
m_net(_clientVersion, _n, _network)
{
if (_dbPath.size())
Defaults::setDBPath(_dbPath);
if (_interfaces.count("eth"))
m_ethereum.reset(new eth::Client(&m_net, _dbPath, _forceClean));
m_ethereum.reset(new eth::Client(&m_net, _dbPath, _forceClean, 0, miners));
if (_interfaces.count("shh"))
m_whisper = m_net.registerCapability<WhisperHost>(new WhisperHost);

2
libwebthree/WebThree.h

@ -103,7 +103,7 @@ class WebThreeDirect : public WebThreeNetworkFace
public:
/// Constructor for private instance. If there is already another process on the machine using @a _dbPath, then this will throw an exception.
/// ethereum() may be safely static_cast()ed to a eth::Client*.
WebThreeDirect(std::string const& _clientVersion, std::string const& _dbPath, bool _forceClean = false, std::set<std::string> const& _interfaces = {"eth", "shh"}, p2p::NetworkPreferences const& _n = p2p::NetworkPreferences(), bytesConstRef _network = bytesConstRef());
WebThreeDirect(std::string const& _clientVersion, std::string const& _dbPath, bool _forceClean = false, std::set<std::string> const& _interfaces = {"eth", "shh"}, p2p::NetworkPreferences const& _n = p2p::NetworkPreferences(), bytesConstRef _network = bytesConstRef(), int miners = -1);
/// Destructor.
~WebThreeDirect();

1
lllc/CMakeLists.txt

@ -4,6 +4,7 @@ set(CMAKE_AUTOMOC OFF)
aux_source_directory(. SRC_LIST)
include_directories(${Boost_INCLUDE_DIRS})
include_directories(${JSONCPP_INCLUDE_DIRS})
include_directories(..)
set(EXECUTABLE lllc)

1
mix/CMakeLists.txt

@ -10,6 +10,7 @@ endif()
set(CMAKE_INCLUDE_CURRENT_DIR ON)
aux_source_directory(. SRC_LIST)
include_directories(${JSONCPP_INCLUDE_DIRS})
include_directories(..)
find_package (Qt5WebEngine QUIET)

81
neth/main.cpp

@ -81,6 +81,7 @@ void help()
<< " -p,--port <port> Connect to remote port (default: 30303)." << endl
<< " -r,--remote <host> Connect to remote host (default: none)." << endl
<< " -s,--secret <secretkeyhex> Set the secret key for use with send command (default: auto)." << endl
<< " -t,--miners <number> Number of mining threads to start (Default: " << thread::hardware_concurrency() << ")" << endl
<< " -u,--public-ip <ip> Force public ip to given (default; auto)." << endl
<< " -v,--verbosity <0..9> Set the log verbosity from 0 to 9 (tmp forced to 1)." << endl
<< " -x,--peers <number> Attempt to connect to given number of peers (default: 5)." << endl
@ -307,6 +308,7 @@ int main(int argc, char** argv)
unsigned mining = ~(unsigned)0;
NodeMode mode = NodeMode::Full;
unsigned peers = 5;
int miners = -1;
#if ETH_JSONRPC
int jsonrpc = 8080;
#endif
@ -366,7 +368,23 @@ int main(int argc, char** argv)
else if ((arg == "-c" || arg == "--client-name") && i + 1 < argc)
clientName = argv[++i];
else if ((arg == "-a" || arg == "--address" || arg == "--coinbase-address") && i + 1 < argc)
coinbase = h160(fromHex(argv[++i]));
{
try
{
coinbase = h160(fromHex(argv[++i], ThrowType::Throw));
}
catch (BadHexCharacter& _e)
{
cwarn << "invalid hex character, coinbase rejected";
cwarn << boost::diagnostic_information(_e);
break;
}
catch (...)
{
cwarn << "coinbase rejected";
break;
}
}
else if ((arg == "-s" || arg == "--secret") && i + 1 < argc)
us = KeyPair(h256(fromHex(argv[++i])));
else if ((arg == "-d" || arg == "--path" || arg == "--db-path") && i + 1 < argc)
@ -401,6 +419,8 @@ int main(int argc, char** argv)
g_logVerbosity = atoi(argv[++i]);
else if ((arg == "-x" || arg == "--peers") && i + 1 < argc)
peers = atoi(argv[++i]);
else if ((arg == "-t" || arg == "--miners") && i + 1 < argc)
miners = atoi(argv[++i]);
else if (arg == "-h" || arg == "--help")
help();
else if (arg == "-V" || arg == "--version")
@ -422,7 +442,9 @@ int main(int argc, char** argv)
false,
mode == NodeMode::Full ? set<string>{"eth", "shh"} : set<string>(),
netPrefs,
&nodesState);
&nodesState,
miners
);
web3.setIdealPeerCount(peers);
eth::Client* c = mode == NodeMode::Full ? web3.ethereum() : nullptr;
@ -708,9 +730,21 @@ int main(int argc, char** argv)
}
else
{
Secret secret = h256(fromHex(sechex));
Address dest = h160(fromHex(fields[0]));
c->transact(secret, amount, dest, data, gas, gasPrice);
try
{
Secret secret = h256(fromHex(sechex, ThrowType::Throw));
Address dest = h160(fromHex(fields[0], ThrowType::Throw));
c->transact(secret, amount, dest, data, gas, gasPrice);
}
catch (BadHexCharacter& _e)
{
cwarn << "invalid hex character, transaction rejected";
cwarn << boost::diagnostic_information(_e);
}
catch (...)
{
cwarn << "transaction rejected";
}
}
}
}
@ -748,8 +782,20 @@ int main(int argc, char** argv)
auto blockData = bc.block(h);
BlockInfo info(blockData);
u256 minGas = (u256)Client::txGas(bytes(), 0);
Address dest = h160(fromHex(fields[0]));
c->transact(us.secret(), amount, dest, bytes(), minGas);
try
{
Address dest = h160(fromHex(fields[0], ThrowType::Throw));
c->transact(us.secret(), amount, dest, bytes(), minGas);
}
catch (BadHexCharacter& _e)
{
cwarn << "invalid hex character, transaction rejected";
cwarn << boost::diagnostic_information(_e);
}
catch (...)
{
cwarn << "transaction rejected";
}
}
}
}
@ -802,14 +848,31 @@ int main(int argc, char** argv)
{
cnote << "Assembled:";
stringstream ssc;
init = fromHex(sinit);
try
{
init = fromHex(sinit, ThrowType::Throw);
}
catch (BadHexCharacter& _e)
{
cwarn << "invalid hex character, code rejected";
cwarn << boost::diagnostic_information(_e);
init = bytes();
}
catch (...)
{
cwarn << "code rejected";
init = bytes();
}
ssc.str(string());
ssc << disassemble(init);
cnote << "Init:";
cnote << ssc.str();
}
u256 minGas = (u256)Client::txGas(init, 0);
if (endowment < 0)
if (!init.size())
cwarn << "Contract creation aborted, no init code.";
else if (endowment < 0)
cwarn << "Invalid endowment";
else if (gas < minGas)
cwarn << "Minimum gas amount is" << minGas;

1
solc/CMakeLists.txt

@ -4,6 +4,7 @@ set(CMAKE_AUTOMOC OFF)
aux_source_directory(. SRC_LIST)
include_directories(${Boost_INCLUDE_DIRS})
include_directories(${JSONCPP_INCLUDE_DIRS})
include_directories(..)
set(EXECUTABLE solc)

2
test/SolidityABIJSON.cpp

@ -22,7 +22,7 @@
#include <boost/test/unit_test.hpp>
#include <libsolidity/CompilerStack.h>
#include <jsoncpp/json/json.h>
#include <json/json.h>
#include <libdevcore/Exceptions.h>
namespace dev

139
test/SolidityEndToEndTest.cpp

@ -668,7 +668,7 @@ BOOST_AUTO_TEST_CASE(mapping_state)
testSolidityAgainstCpp("getVoteCount(address)", getVoteCount, u160(0));
testSolidityAgainstCpp("getVoteCount(address)", getVoteCount, u160(1));
testSolidityAgainstCpp("getVoteCount(address)", getVoteCount, u160(2));
// voting without vote right shourd be rejected
// voting without vote right should be rejected
testSolidityAgainstCpp("vote(address,address)", vote, u160(0), u160(2));
testSolidityAgainstCpp("getVoteCount(address)", getVoteCount, u160(0));
testSolidityAgainstCpp("getVoteCount(address)", getVoteCount, u160(1));
@ -963,7 +963,7 @@ BOOST_AUTO_TEST_CASE(multiple_elementary_accessors)
compileAndRun(sourceCode);
BOOST_CHECK(callContractFunction("data()") == encodeArgs(8));
BOOST_CHECK(callContractFunction("name()") == encodeArgs("Celina"));
BOOST_CHECK(callContractFunction("a_hash()") == encodeArgs(dev::sha3(bytes{0x7b})));
BOOST_CHECK(callContractFunction("a_hash()") == encodeArgs(dev::sha3(bytes(1, 0x7b))));
BOOST_CHECK(callContractFunction("an_address()") == encodeArgs(toBigEndian(u160(0x1337))));
BOOST_CHECK(callContractFunction("super_secret_data()") == bytes());
}
@ -2203,7 +2203,7 @@ BOOST_AUTO_TEST_CASE(sha3_multiple_arguments_with_numeric_literals)
dev::sha3(
toBigEndian(u256(10)) +
bytes{0x0, 0xc} +
bytes{0x91})));
bytes(1, 0x91))));
}
BOOST_AUTO_TEST_CASE(sha3_multiple_arguments_with_string_literals)
@ -2227,7 +2227,7 @@ BOOST_AUTO_TEST_CASE(sha3_multiple_arguments_with_string_literals)
dev::sha3(
toBigEndian(u256(10)) +
bytes{0x0, 0xc} +
bytes{0x91} +
bytes(1, 0x91) +
bytes{0x66, 0x6f, 0x6f})));
}
@ -2254,6 +2254,137 @@ BOOST_AUTO_TEST_CASE(generic_call)
BOOST_CHECK_EQUAL(m_state.balance(m_contractAddress), 50 - 2);
}
BOOST_AUTO_TEST_CASE(store_bytes)
{
// this test just checks that the copy loop does not mess up the stack
char const* sourceCode = R"(
contract C {
function save() returns (uint r) {
r = 23;
savedData = msg.data;
r = 24;
}
bytes savedData;
}
)";
compileAndRun(sourceCode);
// empty copy loop
BOOST_CHECK(callContractFunction("save()") == encodeArgs(24));
BOOST_CHECK(callContractFunction("save()", "abcdefg") == encodeArgs(24));
}
BOOST_AUTO_TEST_CASE(call_forward_bytes)
{
char const* sourceCode = R"(
contract receiver {
uint public received;
function receive(uint x) { received += x + 1; }
function() { received = 0x80; }
}
contract sender {
function sender() { rec = new receiver(); }
function() { savedData = msg.data; }
function forward() returns (bool) { rec.call(savedData); return true; }
function clear() returns (bool) { delete savedData; return true; }
function val() returns (uint) { return rec.received(); }
receiver rec;
bytes savedData;
}
)";
compileAndRun(sourceCode, 0, "sender");
BOOST_CHECK(callContractFunction("receive(uint256)", 7) == bytes());
BOOST_CHECK(callContractFunction("val()") == encodeArgs(0));
BOOST_CHECK(callContractFunction("forward()") == encodeArgs(true));
BOOST_CHECK(callContractFunction("val()") == encodeArgs(8));
BOOST_CHECK(callContractFunction("clear()") == encodeArgs(true));
BOOST_CHECK(callContractFunction("val()") == encodeArgs(8));
BOOST_CHECK(callContractFunction("forward()") == encodeArgs(true));
BOOST_CHECK(callContractFunction("val()") == encodeArgs(0x80));
}
BOOST_AUTO_TEST_CASE(copying_bytes_multiassign)
{
char const* sourceCode = R"(
contract receiver {
uint public received;
function receive(uint x) { received += x + 1; }
function() { received = 0x80; }
}
contract sender {
function sender() { rec = new receiver(); }
function() { savedData1 = savedData2 = msg.data; }
function forward(bool selector) returns (bool) {
if (selector) { rec.call(savedData1); delete savedData1; }
else { rec.call(savedData2); delete savedData2; }
return true;
}
function val() returns (uint) { return rec.received(); }
receiver rec;
bytes savedData1;
bytes savedData2;
}
)";
compileAndRun(sourceCode, 0, "sender");
BOOST_CHECK(callContractFunction("receive(uint256)", 7) == bytes());
BOOST_CHECK(callContractFunction("val()") == encodeArgs(0));
BOOST_CHECK(callContractFunction("forward(bool)", true) == encodeArgs(true));
BOOST_CHECK(callContractFunction("val()") == encodeArgs(8));
BOOST_CHECK(callContractFunction("forward(bool)", false) == encodeArgs(true));
BOOST_CHECK(callContractFunction("val()") == encodeArgs(16));
BOOST_CHECK(callContractFunction("forward(bool)", true) == encodeArgs(true));
BOOST_CHECK(callContractFunction("val()") == encodeArgs(0x80));
}
BOOST_AUTO_TEST_CASE(delete_removes_bytes_data)
{
char const* sourceCode = R"(
contract c {
function() { data = msg.data; }
function del() returns (bool) { delete data; return true; }
bytes data;
}
)";
compileAndRun(sourceCode);
BOOST_CHECK(callContractFunction("---", 7) == bytes());
BOOST_CHECK(!m_state.storage(m_contractAddress).empty());
BOOST_CHECK(callContractFunction("del()", 7) == encodeArgs(true));
BOOST_CHECK(m_state.storage(m_contractAddress).empty());
}
BOOST_AUTO_TEST_CASE(copy_from_calldata_removes_bytes_data)
{
char const* sourceCode = R"(
contract c {
function set() returns (bool) { data = msg.data; return true; }
function() { data = msg.data; }
bytes data;
}
)";
compileAndRun(sourceCode);
BOOST_CHECK(callContractFunction("set()", 1, 2, 3, 4, 5) == encodeArgs(true));
BOOST_CHECK(!m_state.storage(m_contractAddress).empty());
sendMessage(bytes(), false);
BOOST_CHECK(m_output == bytes());
BOOST_CHECK(m_state.storage(m_contractAddress).empty());
}
BOOST_AUTO_TEST_CASE(copy_removes_bytes_data)
{
char const* sourceCode = R"(
contract c {
function set() returns (bool) { data1 = msg.data; return true; }
function reset() returns (bool) { data1 = data2; return true; }
bytes data1;
bytes data2;
}
)";
compileAndRun(sourceCode);
BOOST_CHECK(callContractFunction("set()", 1, 2, 3, 4, 5) == encodeArgs(true));
BOOST_CHECK(!m_state.storage(m_contractAddress).empty());
BOOST_CHECK(callContractFunction("reset()") == encodeArgs(true));
BOOST_CHECK(m_state.storage(m_contractAddress).empty());
}
BOOST_AUTO_TEST_SUITE_END()
}

2
test/SolidityNatspecJSON.cpp

@ -21,7 +21,7 @@
*/
#include <boost/test/unit_test.hpp>
#include <jsoncpp/json/json.h>
#include <json/json.h>
#include <libsolidity/CompilerStack.h>
#include <libsolidity/Exceptions.h>
#include <libdevcore/Exceptions.h>

2
test/solidityExecutionFramework.h

@ -139,6 +139,7 @@ private:
return encode(_cppFunction(_arguments...));
}
protected:
void sendMessage(bytes const& _data, bool _isCreation, u256 const& _value = 0)
{
m_state.addBalance(m_sender, _value); // just in case
@ -171,7 +172,6 @@ private:
m_logs = executive.logs();
}
protected:
bool m_optimize = false;
bool m_addStandardSources = false;
Address m_sender;

Loading…
Cancel
Save