Browse Source

Merge branch 'develop' of https://github.com/ethereum/cpp-ethereum into bc_rf

cl-refactor
arkpar 10 years ago
parent
commit
1a3163f670
  1. 2
      alethzero/MainWin.cpp
  2. 21
      libdevcore/CommonIO.cpp
  3. 9
      libdevcore/CommonIO.h
  4. 129
      libdevcrypto/Common.cpp
  5. 27
      libdevcrypto/Common.h
  6. 35
      libdevcrypto/Exceptions.h
  7. 124
      libdevcrypto/SecretStore.cpp
  8. 50
      libdevcrypto/SecretStore.h
  9. 2
      libethereum/BlockChain.cpp
  10. 6
      libethereum/EthereumHost.h
  11. 10
      libsolidity/AST.cpp
  12. 56
      libsolidity/ArrayUtils.cpp
  13. 40
      libsolidity/Compiler.cpp
  14. 64
      libsolidity/CompilerUtils.cpp
  15. 5
      libsolidity/CompilerUtils.h
  16. 55
      libsolidity/ExpressionCompiler.cpp
  17. 8
      libsolidity/NameAndTypeResolver.cpp
  18. 62
      libsolidity/Types.cpp
  19. 32
      libsolidity/Types.h
  20. 2
      libweb3jsonrpc/JsonHelper.h
  21. 1
      mix/MixClient.h
  22. 6
      test/TestHelper.cpp
  23. 2
      test/TestHelper.h
  24. 11
      test/TestUtils.cpp
  25. 9
      test/TestUtils.h
  26. 138
      test/libdevcrypto/SecretStore.cpp
  27. 4
      test/libdevcrypto/crypto.cpp
  28. 6
      test/libethereum/stateOriginal.cpp
  29. 4
      test/libp2p/capability.cpp
  30. 26
      test/libp2p/net.cpp
  31. 17
      test/libp2p/peer.cpp
  32. 88
      test/libsolidity/SolidityEndToEndTest.cpp
  33. 14
      test/libsolidity/SolidityTypes.cpp
  34. 11
      test/libwhisper/whisperTopic.cpp

2
alethzero/MainWin.cpp

@ -1157,7 +1157,7 @@ void Main::refreshNetwork()
auto ns = web3()->nodes(); auto ns = web3()->nodes();
for (p2p::Peer const& i: ns) for (p2p::Peer const& i: ns)
ui->nodes->insertItem(sessions.count(i.id) ? 0 : ui->nodes->count(), QString("[%1 %3] %2 - ( =%5s | /%4s%6 ) - *%7 $%8") ui->nodes->insertItem(sessions.count(i.id) ? 0 : ui->nodes->count(), QString("[%1 %3] %2 - ( %4 ) - *%5")
.arg(QString::fromStdString(i.id.abridged())) .arg(QString::fromStdString(i.id.abridged()))
.arg(QString::fromStdString(i.endpoint.address.to_string())) .arg(QString::fromStdString(i.endpoint.address.to_string()))
.arg(i.id == web3()->id() ? "self" : sessions.count(i.id) ? sessions[i.id] : "disconnected") .arg(i.id == web3()->id() ? "self" : sessions.count(i.id) ? sessions[i.id] : "disconnected")

21
libdevcore/CommonIO.cpp

@ -23,13 +23,14 @@
#include <iostream> #include <iostream>
#include <cstdlib> #include <cstdlib>
#include <fstream> #include <fstream>
#include "Exceptions.h"
#include <stdio.h> #include <stdio.h>
#ifdef _WIN32 #ifdef _WIN32
#include <windows.h> #include <windows.h>
#else #else
#include <termios.h> #include <termios.h>
#endif #endif
#include <boost/filesystem.hpp>
#include "Exceptions.h"
using namespace std; using namespace std;
using namespace dev; using namespace dev;
@ -95,9 +96,23 @@ string dev::contentsString(string const& _file)
return contentsGeneric<string>(_file); return contentsGeneric<string>(_file);
} }
void dev::writeFile(std::string const& _file, bytesConstRef _data) void dev::writeFile(std::string const& _file, bytesConstRef _data, bool _writeDeleteRename)
{ {
ofstream(_file, ios::trunc|ios::binary).write((char const*)_data.data(), _data.size()); if (_writeDeleteRename)
{
namespace fs = boost::filesystem;
fs::path tempPath = fs::unique_path(_file + "-%%%%%%");
writeFile(tempPath.string(), _data, false);
// will delete _file if it exists
fs::rename(tempPath, _file);
}
else
{
ofstream s(_file, ios::trunc | ios::binary);
s.write(reinterpret_cast<char const*>(_data.data()), _data.size());
if (!s)
BOOST_THROW_EXCEPTION(FileError());
}
} }
std::string dev::getPassword(std::string const& _prompt) std::string dev::getPassword(std::string const& _prompt)

9
libdevcore/CommonIO.h

@ -56,10 +56,13 @@ std::string contentsString(std::string const& _file);
bytesRef contentsNew(std::string const& _file, bytesRef _dest = bytesRef()); bytesRef contentsNew(std::string const& _file, bytesRef _dest = bytesRef());
/// Write the given binary data into the given file, replacing the file if it pre-exists. /// Write the given binary data into the given file, replacing the file if it pre-exists.
void writeFile(std::string const& _file, bytesConstRef _data); /// Throws exception on error.
/// @param _writeDeleteRename useful not to lose any data: If set, first writes to another file in
/// the same directory and then moves that file.
void writeFile(std::string const& _file, bytesConstRef _data, bool _writeDeleteRename = false);
/// Write the given binary data into the given file, replacing the file if it pre-exists. /// Write the given binary data into the given file, replacing the file if it pre-exists.
inline void writeFile(std::string const& _file, bytes const& _data) { writeFile(_file, bytesConstRef(&_data)); } inline void writeFile(std::string const& _file, bytes const& _data, bool _writeDeleteRename = false) { writeFile(_file, bytesConstRef(&_data), _writeDeleteRename); }
inline void writeFile(std::string const& _file, std::string const& _data) { writeFile(_file, bytesConstRef(_data)); } inline void writeFile(std::string const& _file, std::string const& _data, bool _writeDeleteRename = false) { writeFile(_file, bytesConstRef(_data), _writeDeleteRename); }
/// Nicely renders the given bytes to a string, optionally as HTML. /// Nicely renders the given bytes to a string, optionally as HTML.
/// @a _bytes: bytes array to be rendered as string. @a _width of a bytes line. /// @a _bytes: bytes array to be rendered as string. @a _width of a bytes line.

129
libdevcrypto/Common.cpp

@ -31,6 +31,7 @@
#include <libdevcore/FileSystem.h> #include <libdevcore/FileSystem.h>
#include "AES.h" #include "AES.h"
#include "CryptoPP.h" #include "CryptoPP.h"
#include "Exceptions.h"
using namespace std; using namespace std;
using namespace dev; using namespace dev;
using namespace dev::crypto; using namespace dev::crypto;
@ -178,15 +179,35 @@ bool dev::verify(Public const& _p, Signature const& _s, h256 const& _hash)
bytes dev::pbkdf2(string const& _pass, bytes const& _salt, unsigned _iterations, unsigned _dkLen) bytes dev::pbkdf2(string const& _pass, bytes const& _salt, unsigned _iterations, unsigned _dkLen)
{ {
bytes ret(_dkLen); bytes ret(_dkLen);
PKCS5_PBKDF2_HMAC<SHA256> pbkdf; if (PKCS5_PBKDF2_HMAC<SHA256>().DeriveKey(
pbkdf.DeriveKey(ret.data(), ret.size(), 0, (byte*)_pass.data(), _pass.size(), _salt.data(), _salt.size(), _iterations); ret.data(),
ret.size(),
0,
reinterpret_cast<byte const*>(_pass.data()),
_pass.size(),
_salt.data(),
_salt.size(),
_iterations
) != _iterations)
BOOST_THROW_EXCEPTION(CryptoException() << errinfo_comment("Key derivation failed."));
return ret; return ret;
} }
bytes dev::scrypt(std::string const& _pass, bytes const& _salt, uint64_t _n, uint32_t _r, uint32_t _p, unsigned _dkLen) bytes dev::scrypt(std::string const& _pass, bytes const& _salt, uint64_t _n, uint32_t _r, uint32_t _p, unsigned _dkLen)
{ {
bytes ret(_dkLen); bytes ret(_dkLen);
libscrypt_scrypt((uint8_t const*)_pass.data(), _pass.size(), _salt.data(), _salt.size(), _n, _r, _p, ret.data(), ret.size()); if (libscrypt_scrypt(
reinterpret_cast<uint8_t const*>(_pass.data()),
_pass.size(),
_salt.data(),
_salt.size(),
_n,
_r,
_p,
ret.data(),
ret.size()
) != 0)
BOOST_THROW_EXCEPTION(CryptoException() << errinfo_comment("Key derivation failed."));
return ret; return ret;
} }
@ -233,42 +254,84 @@ h256 crypto::kdf(Secret const& _priv, h256 const& _hash)
return s; return s;
} }
h256 Nonce::get(bool _commit) mutex Nonce::s_x;
static string s_seedFile;
h256 Nonce::get()
{ {
// todo: atomic efface bit, periodic save, kdf, rr, rng // todo: atomic efface bit, periodic save, kdf, rr, rng
// todo: encrypt // todo: encrypt
static h256 s_seed; Guard l(Nonce::s_x);
static string s_seedFile(getDataDir() + "/seed"); return Nonce::singleton().next();
static mutex s_x; }
Guard l(s_x);
if (!s_seed) void Nonce::reset()
{
Guard l(Nonce::s_x);
Nonce::singleton().resetInternal();
}
void Nonce::setSeedFilePath(string const& _filePath)
{
s_seedFile = _filePath;
}
Nonce::~Nonce()
{
Guard l(Nonce::s_x);
if (m_value)
// this might throw
resetInternal();
}
Nonce& Nonce::singleton()
{
static Nonce s;
return s;
}
void Nonce::initialiseIfNeeded()
{
if (m_value)
return;
bytes b = contents(seedFile());
if (b.size() == 32)
memcpy(m_value.data(), b.data(), 32);
else
{ {
static Nonce s_nonce; // todo: replace w/entropy from user and system
bytes b = contents(s_seedFile); std::mt19937_64 s_eng(time(0) + chrono::high_resolution_clock::now().time_since_epoch().count());
if (b.size() == 32) std::uniform_int_distribution<uint16_t> d(0, 255);
memcpy(s_seed.data(), b.data(), 32); for (unsigned i = 0; i < 32; ++i)
else m_value[i] = byte(d(s_eng));
{
// todo: replace w/entropy from user and system
std::mt19937_64 s_eng(time(0) + chrono::high_resolution_clock::now().time_since_epoch().count());
std::uniform_int_distribution<uint16_t> d(0, 255);
for (unsigned i = 0; i < 32; ++i)
s_seed[i] = (byte)d(s_eng);
}
if (!s_seed)
BOOST_THROW_EXCEPTION(InvalidState());
// prevent seed reuse if process terminates abnormally
writeFile(s_seedFile, bytes());
} }
h256 prev(s_seed); if (!m_value)
sha3(prev.ref(), s_seed.ref()); BOOST_THROW_EXCEPTION(InvalidState());
if (_commit)
writeFile(s_seedFile, s_seed.asBytes()); // prevent seed reuse if process terminates abnormally
return std::move(s_seed); // this might throw
writeFile(seedFile(), bytes());
} }
Nonce::~Nonce() h256 Nonce::next()
{
initialiseIfNeeded();
m_value = sha3(m_value);
return m_value;
}
void Nonce::resetInternal()
{
// this might throw
next();
writeFile(seedFile(), m_value.asBytes());
m_value = h256();
}
string const& Nonce::seedFile()
{ {
Nonce::get(true); if (s_seedFile.empty())
s_seedFile = getDataDir() + "/seed";
return s_seedFile;
} }

27
libdevcrypto/Common.h

@ -24,6 +24,7 @@
#pragma once #pragma once
#include <mutex>
#include <libdevcore/Common.h> #include <libdevcore/Common.h>
#include <libdevcore/FixedHash.h> #include <libdevcore/FixedHash.h>
#include <libdevcore/Exceptions.h> #include <libdevcore/Exceptions.h>
@ -180,14 +181,36 @@ struct InvalidState: public dev::Exception {};
h256 kdf(Secret const& _priv, h256 const& _hash); h256 kdf(Secret const& _priv, h256 const& _hash);
/** /**
* @brief Generator for nonce material * @brief Generator for nonce material.
*/ */
struct Nonce struct Nonce
{ {
static h256 get(bool _commit = false); /// Returns the next nonce (might be read from a file).
static h256 get();
/// Stores the current nonce in a file and resets Nonce to the uninitialised state.
static void reset();
/// Sets the location of the seed file to a non-default place. Used for testing.
static void setSeedFilePath(std::string const& _filePath);
private: private:
Nonce() {} Nonce() {}
~Nonce(); ~Nonce();
/// @returns the singleton instance.
static Nonce& singleton();
/// Reads the last seed from the seed file.
void initialiseIfNeeded();
/// @returns the next nonce.
h256 next();
/// Stores the current seed in the seed file.
void resetInternal();
/// @returns the path of the seed file.
static std::string const& seedFile();
/// Mutex for the singleton object.
/// @note Every access to any private function has to be guarded by this mutex.
static std::mutex s_x;
h256 m_value;
}; };
} }

35
libdevcrypto/Exceptions.h

@ -0,0 +1,35 @@
/*
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 <http://www.gnu.org/licenses/>.
*/
/** @file Exceptions.h
* @author Christian <c@ethdev.com>
* @date 2016
*/
#pragma once
#include <libdevcore/Exceptions.h>
namespace dev
{
namespace crypto
{
/// Rare malfunction of cryptographic functions.
DEV_SIMPLE_EXCEPTION(CryptoException);
}
}

124
libdevcrypto/SecretStore.cpp

@ -29,6 +29,7 @@
#include <libdevcore/SHA3.h> #include <libdevcore/SHA3.h>
#include <libdevcore/FileSystem.h> #include <libdevcore/FileSystem.h>
#include <test/JsonSpiritHeaders.h> #include <test/JsonSpiritHeaders.h>
#include <libdevcrypto/Exceptions.h>
using namespace std; using namespace std;
using namespace dev; using namespace dev;
namespace js = json_spirit; namespace js = json_spirit;
@ -36,7 +37,8 @@ namespace fs = boost::filesystem;
static const int c_keyFileVersion = 3; static const int c_keyFileVersion = 3;
static js::mValue upgraded(std::string const& _s) /// Upgrade the json-format to the current version.
static js::mValue upgraded(string const& _s)
{ {
js::mValue v; js::mValue v;
js::read_string(_s, v); js::read_string(_s, v);
@ -84,35 +86,34 @@ static js::mValue upgraded(std::string const& _s)
return js::mValue(); return js::mValue();
} }
SecretStore::SecretStore(std::string const& _path): m_path(_path) SecretStore::SecretStore(string const& _path): m_path(_path)
{ {
load(); load();
} }
SecretStore::~SecretStore() bytes SecretStore::secret(h128 const& _uuid, function<string()> const& _pass, bool _useCache) const
{ {
}
bytes SecretStore::secret(h128 const& _uuid, function<std::string()> const& _pass, bool _useCache) const
{
(void)_pass;
auto rit = m_cached.find(_uuid); auto rit = m_cached.find(_uuid);
if (_useCache && rit != m_cached.end()) if (_useCache && rit != m_cached.end())
return rit->second; return rit->second;
auto it = m_keys.find(_uuid); auto it = m_keys.find(_uuid);
if (it == m_keys.end()) bytes key;
return bytes(); if (it != m_keys.end())
bytes key = decrypt(it->second.first, _pass()); {
if (!key.empty()) key = decrypt(it->second.encryptedKey, _pass());
m_cached[_uuid] = key; if (!key.empty())
m_cached[_uuid] = key;
}
return key; return key;
} }
h128 SecretStore::importSecret(bytes const& _s, std::string const& _pass) h128 SecretStore::importSecret(bytes const& _s, string const& _pass)
{ {
h128 r = h128::random(); h128 r;
EncryptedKey key{encrypt(_s, _pass), string()};
r = h128::random();
m_cached[r] = _s; m_cached[r] = _s;
m_keys[r] = make_pair(encrypt(_s, _pass), std::string()); m_keys[r] = move(key);
save(); save();
return r; return r;
} }
@ -122,7 +123,7 @@ void SecretStore::kill(h128 const& _uuid)
m_cached.erase(_uuid); m_cached.erase(_uuid);
if (m_keys.count(_uuid)) if (m_keys.count(_uuid))
{ {
boost::filesystem::remove(m_keys[_uuid].second); fs::remove(m_keys[_uuid].filename);
m_keys.erase(_uuid); m_keys.erase(_uuid);
} }
} }
@ -132,50 +133,50 @@ void SecretStore::clearCache() const
m_cached.clear(); m_cached.clear();
} }
void SecretStore::save(std::string const& _keysPath) void SecretStore::save(string const& _keysPath)
{ {
fs::path p(_keysPath); fs::path p(_keysPath);
boost::filesystem::create_directories(p); fs::create_directories(p);
for (auto& k: m_keys) for (auto& k: m_keys)
{ {
std::string uuid = toUUID(k.first); string uuid = toUUID(k.first);
std::string filename = (p / uuid).string() + ".json"; string filename = (p / uuid).string() + ".json";
js::mObject v; js::mObject v;
js::mValue crypto; js::mValue crypto;
js::read_string(k.second.first, crypto); js::read_string(k.second.encryptedKey, crypto);
v["crypto"] = crypto; v["crypto"] = crypto;
v["id"] = uuid; v["id"] = uuid;
v["version"] = c_keyFileVersion; v["version"] = c_keyFileVersion;
writeFile(filename, js::write_string(js::mValue(v), true)); writeFile(filename, js::write_string(js::mValue(v), true));
if (!k.second.second.empty() && k.second.second != filename) swap(k.second.filename, filename);
boost::filesystem::remove(k.second.second); if (!filename.empty() && !fs::equivalent(filename, k.second.filename))
k.second.second = filename; fs::remove(filename);
} }
} }
void SecretStore::load(std::string const& _keysPath) void SecretStore::load(string const& _keysPath)
{ {
fs::path p(_keysPath); fs::path p(_keysPath);
boost::filesystem::create_directories(p); fs::create_directories(p);
for (fs::directory_iterator it(p); it != fs::directory_iterator(); ++it) for (fs::directory_iterator it(p); it != fs::directory_iterator(); ++it)
if (is_regular_file(it->path())) if (fs::is_regular_file(it->path()))
readKey(it->path().string(), true); readKey(it->path().string(), true);
} }
h128 SecretStore::readKey(std::string const& _file, bool _deleteFile) h128 SecretStore::readKey(string const& _file, bool _takeFileOwnership)
{ {
cnote << "Reading" << _file; cnote << "Reading" << _file;
return readKeyContent(contentsString(_file), _deleteFile ? _file : string()); return readKeyContent(contentsString(_file), _takeFileOwnership ? _file : string());
} }
h128 SecretStore::readKeyContent(std::string const& _content, std::string const& _file) h128 SecretStore::readKeyContent(string const& _content, string const& _file)
{ {
js::mValue u = upgraded(_content); js::mValue u = upgraded(_content);
if (u.type() == js::obj_type) if (u.type() == js::obj_type)
{ {
js::mObject& o = u.get_obj(); js::mObject& o = u.get_obj();
auto uuid = fromUUID(o["id"].get_str()); auto uuid = fromUUID(o["id"].get_str());
m_keys[uuid] = make_pair(js::write_string(o["crypto"], false), _file); m_keys[uuid] = EncryptedKey{js::write_string(o["crypto"], false), _file};
return uuid; return uuid;
} }
else else
@ -183,62 +184,63 @@ h128 SecretStore::readKeyContent(std::string const& _content, std::string const&
return h128(); return h128();
} }
bool SecretStore::recode(h128 const& _uuid, string const& _newPass, std::function<std::string()> const& _pass, KDF _kdf) bool SecretStore::recode(h128 const& _uuid, string const& _newPass, function<string()> const& _pass, KDF _kdf)
{ {
// cdebug << "recode:" << toUUID(_uuid);
bytes s = secret(_uuid, _pass, true); bytes s = secret(_uuid, _pass, true);
if (s.empty()) if (s.empty())
return false; return false;
m_keys[_uuid].first = encrypt(s, _newPass, _kdf); m_cached.erase(_uuid);
m_keys[_uuid].encryptedKey = encrypt(s, _newPass, _kdf);
save(); save();
return true; return true;
} }
std::string SecretStore::encrypt(bytes const& _v, std::string const& _pass, KDF _kdf) static bytes deriveNewKey(string const& _pass, KDF _kdf, js::mObject& o_ret)
{ {
js::mObject ret;
// KDF info
unsigned dklen = 32; unsigned dklen = 32;
unsigned iterations = 1 << 18;
bytes salt = h256::random().asBytes(); bytes salt = h256::random().asBytes();
bytes derivedKey;
if (_kdf == KDF::Scrypt) if (_kdf == KDF::Scrypt)
{ {
unsigned iterations = 262144;
unsigned p = 1; unsigned p = 1;
unsigned r = 8; unsigned r = 8;
ret["kdf"] = "scrypt"; o_ret["kdf"] = "scrypt";
{ {
js::mObject params; js::mObject params;
params["n"] = (int64_t)iterations; params["n"] = int64_t(iterations);
params["r"] = (int)r; params["r"] = int(r);
params["p"] = (int)p; params["p"] = int(p);
params["dklen"] = (int)dklen; params["dklen"] = int(dklen);
params["salt"] = toHex(salt); params["salt"] = toHex(salt);
ret["kdfparams"] = params; o_ret["kdfparams"] = params;
} }
derivedKey = scrypt(_pass, salt, iterations, r, p, dklen); return scrypt(_pass, salt, iterations, r, p, dklen);
} }
else else
{ {
unsigned iterations = 262144; o_ret["kdf"] = "pbkdf2";
ret["kdf"] = "pbkdf2";
{ {
js::mObject params; js::mObject params;
params["prf"] = "hmac-sha256"; params["prf"] = "hmac-sha256";
params["c"] = (int)iterations; params["c"] = int(iterations);
params["salt"] = toHex(salt); params["salt"] = toHex(salt);
params["dklen"] = (int)dklen; params["dklen"] = int(dklen);
ret["kdfparams"] = params; o_ret["kdfparams"] = params;
} }
derivedKey = pbkdf2(_pass, salt, iterations, dklen); return pbkdf2(_pass, salt, iterations, dklen);
} }
// cdebug << "derivedKey" << toHex(derivedKey); }
string SecretStore::encrypt(bytes const& _v, string const& _pass, KDF _kdf)
{
js::mObject ret;
bytes derivedKey = deriveNewKey(_pass, _kdf, ret);
if (derivedKey.empty())
BOOST_THROW_EXCEPTION(crypto::CryptoException() << errinfo_comment("Key derivation failed."));
// cipher info
ret["cipher"] = "aes-128-ctr"; ret["cipher"] = "aes-128-ctr";
h128 key(derivedKey, h128::AlignLeft); h128 key(derivedKey, h128::AlignLeft);
// cdebug << "cipherKey" << key.hex();
h128 iv = h128::random(); h128 iv = h128::random();
{ {
js::mObject params; js::mObject params;
@ -248,18 +250,18 @@ std::string SecretStore::encrypt(bytes const& _v, std::string const& _pass, KDF
// cipher text // cipher text
bytes cipherText = encryptSymNoAuth(key, iv, &_v); bytes cipherText = encryptSymNoAuth(key, iv, &_v);
if (cipherText.empty())
BOOST_THROW_EXCEPTION(crypto::CryptoException() << errinfo_comment("Key encryption failed."));
ret["ciphertext"] = toHex(cipherText); ret["ciphertext"] = toHex(cipherText);
// and mac. // and mac.
h256 mac = sha3(ref(derivedKey).cropped(16, 16).toBytes() + cipherText); h256 mac = sha3(ref(derivedKey).cropped(16, 16).toBytes() + cipherText);
// cdebug << "macBody" << toHex(ref(derivedKey).cropped(16, 16).toBytes() + cipherText);
// cdebug << "mac" << mac.hex();
ret["mac"] = toHex(mac.ref()); ret["mac"] = toHex(mac.ref());
return js::write_string((js::mValue)ret, true); return js::write_string(js::mValue(ret), true);
} }
bytes SecretStore::decrypt(std::string const& _v, std::string const& _pass) bytes SecretStore::decrypt(string const& _v, string const& _pass)
{ {
js::mObject o; js::mObject o;
{ {

50
libdevcrypto/SecretStore.h

@ -35,41 +35,81 @@ enum class KDF {
Scrypt, Scrypt,
}; };
/**
* Manages encrypted keys stored in a certain directory on disk. The keys are read into memory
* and changes to the keys are automatically synced to the directory.
* Each file stores exactly one key in a specific JSON format whose file name is derived from the
* UUID of the key.
* @note that most of the functions here affect the filesystem and throw exceptions on failure,
* and they also throw exceptions upon rare malfunction in the cryptographic functions.
*/
class SecretStore class SecretStore
{ {
public: public:
/// Construct a new SecretStore and read all keys in the given directory.
SecretStore(std::string const& _path = defaultPath()); SecretStore(std::string const& _path = defaultPath());
~SecretStore();
/// @returns the secret key stored by the given @a _uuid.
/// @param _pass function that returns the password for the key.
/// @param _useCache if true, allow previously decrypted keys to be returned directly.
bytes secret(h128 const& _uuid, std::function<std::string()> const& _pass, bool _useCache = true) const; bytes secret(h128 const& _uuid, std::function<std::string()> const& _pass, bool _useCache = true) const;
/// Imports the (encrypted) key stored in the file @a _file and copies it to the managed directory.
h128 importKey(std::string const& _file) { auto ret = readKey(_file, false); if (ret) save(); return ret; } h128 importKey(std::string const& _file) { auto ret = readKey(_file, false); if (ret) save(); return ret; }
/// Imports the (encrypted) key contained in the json formatted @a _content and stores it in
/// the managed directory.
h128 importKeyContent(std::string const& _content) { auto ret = readKeyContent(_content, std::string()); if (ret) save(); return ret; } h128 importKeyContent(std::string const& _content) { auto ret = readKeyContent(_content, std::string()); if (ret) save(); return ret; }
/// Imports the decrypted key given by @a _s and stores it, encrypted with
/// (a key derived from) the password @a _pass.
h128 importSecret(bytes const& _s, std::string const& _pass); h128 importSecret(bytes const& _s, std::string const& _pass);
/// Decrypts and re-encrypts the key identified by @a _uuid.
bool recode(h128 const& _uuid, std::string const& _newPass, std::function<std::string()> const& _pass, KDF _kdf = KDF::Scrypt); bool recode(h128 const& _uuid, std::string const& _newPass, std::function<std::string()> const& _pass, KDF _kdf = KDF::Scrypt);
/// Removes the key specified by @a _uuid from both memory and disk.
void kill(h128 const& _uuid); void kill(h128 const& _uuid);
/// Returns the uuids of all stored keys.
std::vector<h128> keys() const { return keysOf(m_keys); } std::vector<h128> keys() const { return keysOf(m_keys); }
// Clear any cached keys. /// Clears all cached decrypted keys. The passwords have to be supplied in order to retrieve
/// secrets again after calling this function.
void clearCache() const; void clearCache() const;
// Doesn't save(). /// Import the key from the file @a _file, but do not copy it to the managed directory yet.
h128 readKey(std::string const& _file, bool _deleteFile); /// @param _takeFileOwnership if true, deletes the file if it is not the canonical file for the
/// key (derived from its uuid).
h128 readKey(std::string const& _file, bool _takeFileOwnership);
/// Import the key contained in the json-encoded @a _content, but do not store it in the
/// managed directory.
/// @param _file if given, assume this file contains @a _content and delete it later, if it is
/// not the canonical file for the key (derived from the uuid).
h128 readKeyContent(std::string const& _content, std::string const& _file = std::string()); h128 readKeyContent(std::string const& _content, std::string const& _file = std::string());
/// Store all keys in the directory @a _keysPath.
void save(std::string const& _keysPath); void save(std::string const& _keysPath);
/// Store all keys in the managed directory.
void save() { save(m_path); } void save() { save(m_path); }
/// @returns the default path for the managed directory.
static std::string defaultPath() { return getDataDir("web3") + "/keys"; } static std::string defaultPath() { return getDataDir("web3") + "/keys"; }
private: private:
struct EncryptedKey
{
std::string encryptedKey;
std::string filename;
};
/// Loads all keys in the given directory.
void load(std::string const& _keysPath); void load(std::string const& _keysPath);
void load() { load(m_path); } void load() { load(m_path); }
/// Encrypts @a _v with a key derived from @a _pass or the empty string on error.
static std::string encrypt(bytes const& _v, std::string const& _pass, KDF _kdf = KDF::Scrypt); static std::string encrypt(bytes const& _v, std::string const& _pass, KDF _kdf = KDF::Scrypt);
/// Decrypts @a _v with a key derived from @a _pass or the empty byte array on error.
static bytes decrypt(std::string const& _v, std::string const& _pass); static bytes decrypt(std::string const& _v, std::string const& _pass);
/// Stores decrypted keys by uuid.
mutable std::unordered_map<h128, bytes> m_cached; mutable std::unordered_map<h128, bytes> m_cached;
std::unordered_map<h128, std::pair<std::string, std::string>> m_keys; /// Stores encrypted keys together with the file they were loaded from by uuid.
std::unordered_map<h128, EncryptedKey> m_keys;
std::string m_path; std::string m_path;
}; };

2
libethereum/BlockChain.cpp

@ -1122,6 +1122,6 @@ VerifiedBlockRef BlockChain::verifyBlock(bytes const& _block, function<void(Exce
++i; ++i;
} }
res.block = bytesConstRef(&_block); res.block = bytesConstRef(&_block);
return move(res); return res;
} }

6
libethereum/EthereumHost.h

@ -103,7 +103,7 @@ private:
std::tuple<std::vector<std::shared_ptr<EthereumPeer>>, std::vector<std::shared_ptr<EthereumPeer>>, std::vector<std::shared_ptr<p2p::Session>>> randomSelection(unsigned _percent = 25, std::function<bool(EthereumPeer*)> const& _allow = [](EthereumPeer const*){ return true; }); std::tuple<std::vector<std::shared_ptr<EthereumPeer>>, std::vector<std::shared_ptr<EthereumPeer>>, std::vector<std::shared_ptr<p2p::Session>>> randomSelection(unsigned _percent = 25, std::function<bool(EthereumPeer*)> const& _allow = [](EthereumPeer const*){ return true; });
/// Sync with the BlockChain. It might contain one of our mined blocks, we might have new candidates from the network. /// Sync with the BlockChain. It might contain one of our mined blocks, we might have new candidates from the network.
void doWork(); virtual void doWork() override;
void maintainTransactions(); void maintainTransactions();
void maintainBlocks(h256 const& _currentBlock); void maintainBlocks(h256 const& _currentBlock);
@ -114,8 +114,8 @@ private:
/// Initialises the network peer-state, doing the stuff that needs to be once-only. @returns true if it really was first. /// Initialises the network peer-state, doing the stuff that needs to be once-only. @returns true if it really was first.
bool ensureInitialised(); bool ensureInitialised();
virtual void onStarting() { startWorking(); } virtual void onStarting() override { startWorking(); }
virtual void onStopping() { stopWorking(); } virtual void onStopping() override { stopWorking(); }
BlockChainSync& sync(); BlockChainSync& sync();

10
libsolidity/AST.cpp

@ -919,7 +919,7 @@ void MemberAccess::checkTypeRequirements(TypePointers const* _argumentTypes)
{ {
auto const& arrayType(dynamic_cast<ArrayType const&>(type)); auto const& arrayType(dynamic_cast<ArrayType const&>(type));
m_isLValue = (*m_memberName == "length" && m_isLValue = (*m_memberName == "length" &&
arrayType.location() != ReferenceType::Location::CallData && arrayType.isDynamicallySized()); arrayType.location() != DataLocation::CallData && arrayType.isDynamicallySized());
} }
else else
m_isLValue = false; m_isLValue = false;
@ -942,7 +942,7 @@ void IndexAccess::checkTypeRequirements(TypePointers const*)
m_type = make_shared<FixedBytesType>(1); m_type = make_shared<FixedBytesType>(1);
else else
m_type = type.getBaseType(); m_type = type.getBaseType();
m_isLValue = type.location() != ReferenceType::Location::CallData; m_isLValue = type.location() != DataLocation::CallData;
break; break;
} }
case Type::Category::Mapping: case Type::Category::Mapping:
@ -959,7 +959,7 @@ void IndexAccess::checkTypeRequirements(TypePointers const*)
{ {
TypeType const& type = dynamic_cast<TypeType const&>(*m_base->getType()); TypeType const& type = dynamic_cast<TypeType const&>(*m_base->getType());
if (!m_index) if (!m_index)
m_type = make_shared<TypeType>(make_shared<ArrayType>(ReferenceType::Location::Memory, type.getActualType())); m_type = make_shared<TypeType>(make_shared<ArrayType>(DataLocation::Memory, type.getActualType()));
else else
{ {
m_index->checkTypeRequirements(nullptr); m_index->checkTypeRequirements(nullptr);
@ -967,7 +967,9 @@ void IndexAccess::checkTypeRequirements(TypePointers const*)
if (!length) if (!length)
BOOST_THROW_EXCEPTION(m_index->createTypeError("Integer constant expected.")); BOOST_THROW_EXCEPTION(m_index->createTypeError("Integer constant expected."));
m_type = make_shared<TypeType>(make_shared<ArrayType>( m_type = make_shared<TypeType>(make_shared<ArrayType>(
ReferenceType::Location::Memory, type.getActualType(), length->literalValue(nullptr))); DataLocation::Memory, type.getActualType(),
length->literalValue(nullptr)
));
} }
break; break;
} }

56
libsolidity/ArrayUtils.cpp

@ -38,10 +38,10 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons
// need to leave "target_ref target_byte_off" on the stack at the end // need to leave "target_ref target_byte_off" on the stack at the end
// stack layout: [source_ref] [source_byte_off] [source length] target_ref target_byte_off (top) // stack layout: [source_ref] [source_byte_off] [source length] target_ref target_byte_off (top)
solAssert(_targetType.location() == ReferenceType::Location::Storage, ""); solAssert(_targetType.location() == DataLocation::Storage, "");
solAssert( solAssert(
_sourceType.location() == ReferenceType::Location::CallData || _sourceType.location() == DataLocation::CallData ||
_sourceType.location() == ReferenceType::Location::Storage, _sourceType.location() == DataLocation::Storage,
"Given array location not implemented." "Given array location not implemented."
); );
@ -51,7 +51,7 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons
// TODO unroll loop for small sizes // TODO unroll loop for small sizes
bool sourceIsStorage = _sourceType.location() == ReferenceType::Location::Storage; bool sourceIsStorage = _sourceType.location() == DataLocation::Storage;
bool directCopy = sourceIsStorage && sourceBaseType->isValueType() && *sourceBaseType == *targetBaseType; bool directCopy = sourceIsStorage && sourceBaseType->isValueType() && *sourceBaseType == *targetBaseType;
bool haveByteOffsetSource = !directCopy && sourceIsStorage && sourceBaseType->getStorageBytes() <= 16; bool haveByteOffsetSource = !directCopy && sourceIsStorage && sourceBaseType->getStorageBytes() <= 16;
bool haveByteOffsetTarget = !directCopy && targetBaseType->getStorageBytes() <= 16; bool haveByteOffsetTarget = !directCopy && targetBaseType->getStorageBytes() <= 16;
@ -69,7 +69,7 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons
m_context << eth::Instruction::POP; m_context << eth::Instruction::POP;
// stack: target_ref source_ref [source_length] // stack: target_ref source_ref [source_length]
// retrieve source length // retrieve source length
if (_sourceType.location() != ReferenceType::Location::CallData || !_sourceType.isDynamicallySized()) if (_sourceType.location() != DataLocation::CallData || !_sourceType.isDynamicallySized())
retrieveLength(_sourceType); // otherwise, length is already there retrieveLength(_sourceType); // otherwise, length is already there
// stack: target_ref source_ref source_length // stack: target_ref source_ref source_length
m_context << eth::Instruction::DUP3; m_context << eth::Instruction::DUP3;
@ -82,7 +82,7 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons
if (sourceBaseType->getCategory() == Type::Category::Mapping) if (sourceBaseType->getCategory() == Type::Category::Mapping)
{ {
solAssert(targetBaseType->getCategory() == Type::Category::Mapping, ""); solAssert(targetBaseType->getCategory() == Type::Category::Mapping, "");
solAssert(_sourceType.location() == ReferenceType::Location::Storage, ""); solAssert(_sourceType.location() == DataLocation::Storage, "");
// nothing to copy // nothing to copy
m_context m_context
<< eth::Instruction::POP << eth::Instruction::POP << eth::Instruction::POP << eth::Instruction::POP
@ -106,7 +106,7 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons
eth::AssemblyItem copyLoopEndWithoutByteOffset = m_context.newTag(); eth::AssemblyItem copyLoopEndWithoutByteOffset = m_context.newTag();
m_context.appendConditionalJumpTo(copyLoopEndWithoutByteOffset); m_context.appendConditionalJumpTo(copyLoopEndWithoutByteOffset);
if (_sourceType.location() == ReferenceType::Location::Storage && _sourceType.isDynamicallySized()) if (_sourceType.location() == DataLocation::Storage && _sourceType.isDynamicallySized())
CompilerUtils(m_context).computeHashStatic(); CompilerUtils(m_context).computeHashStatic();
// stack: target_ref target_data_end source_length target_data_pos source_data_pos // stack: target_ref target_data_end source_length target_data_pos source_data_pos
m_context << eth::Instruction::SWAP2; m_context << eth::Instruction::SWAP2;
@ -155,7 +155,7 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons
// checking is easier. // checking is easier.
// stack: target_ref target_data_end source_data_pos target_data_pos source_data_end [target_byte_offset] [source_byte_offset] // stack: target_ref target_data_end source_data_pos target_data_pos source_data_end [target_byte_offset] [source_byte_offset]
m_context << eth::dupInstruction(3 + byteOffsetSize); m_context << eth::dupInstruction(3 + byteOffsetSize);
if (_sourceType.location() == ReferenceType::Location::Storage) if (_sourceType.location() == DataLocation::Storage)
{ {
if (haveByteOffsetSource) if (haveByteOffsetSource)
m_context << eth::Instruction::DUP2; m_context << eth::Instruction::DUP2;
@ -231,7 +231,7 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons
void ArrayUtils::clearArray(ArrayType const& _type) const void ArrayUtils::clearArray(ArrayType const& _type) const
{ {
unsigned stackHeightStart = m_context.getStackHeight(); unsigned stackHeightStart = m_context.getStackHeight();
solAssert(_type.location() == ReferenceType::Location::Storage, ""); solAssert(_type.location() == DataLocation::Storage, "");
if (_type.getBaseType()->getStorageBytes() < 32) if (_type.getBaseType()->getStorageBytes() < 32)
{ {
solAssert(_type.getBaseType()->isValueType(), "Invalid storage size for non-value type."); solAssert(_type.getBaseType()->isValueType(), "Invalid storage size for non-value type.");
@ -286,7 +286,7 @@ void ArrayUtils::clearArray(ArrayType const& _type) const
void ArrayUtils::clearDynamicArray(ArrayType const& _type) const void ArrayUtils::clearDynamicArray(ArrayType const& _type) const
{ {
solAssert(_type.location() == ReferenceType::Location::Storage, ""); solAssert(_type.location() == DataLocation::Storage, "");
solAssert(_type.isDynamicallySized(), ""); solAssert(_type.isDynamicallySized(), "");
unsigned stackHeightStart = m_context.getStackHeight(); unsigned stackHeightStart = m_context.getStackHeight();
@ -314,7 +314,7 @@ void ArrayUtils::clearDynamicArray(ArrayType const& _type) const
void ArrayUtils::resizeDynamicArray(const ArrayType& _type) const void ArrayUtils::resizeDynamicArray(const ArrayType& _type) const
{ {
solAssert(_type.location() == ReferenceType::Location::Storage, ""); solAssert(_type.location() == DataLocation::Storage, "");
solAssert(_type.isDynamicallySized(), ""); solAssert(_type.isDynamicallySized(), "");
if (!_type.isByteArray() && _type.getBaseType()->getStorageBytes() < 32) if (!_type.isByteArray() && _type.getBaseType()->getStorageBytes() < 32)
solAssert(_type.getBaseType()->isValueType(), "Invalid storage size for non-value type."); solAssert(_type.getBaseType()->isValueType(), "Invalid storage size for non-value type.");
@ -399,7 +399,7 @@ void ArrayUtils::clearStorageLoop(Type const& _type) const
void ArrayUtils::convertLengthToSize(ArrayType const& _arrayType, bool _pad) const void ArrayUtils::convertLengthToSize(ArrayType const& _arrayType, bool _pad) const
{ {
if (_arrayType.location() == ReferenceType::Location::Storage) if (_arrayType.location() == DataLocation::Storage)
{ {
if (_arrayType.getBaseType()->getStorageSize() <= 1) if (_arrayType.getBaseType()->getStorageSize() <= 1)
{ {
@ -437,13 +437,13 @@ void ArrayUtils::retrieveLength(ArrayType const& _arrayType) const
m_context << eth::Instruction::DUP1; m_context << eth::Instruction::DUP1;
switch (_arrayType.location()) switch (_arrayType.location())
{ {
case ReferenceType::Location::CallData: case DataLocation::CallData:
// length is stored on the stack // length is stored on the stack
break; break;
case ReferenceType::Location::Memory: case DataLocation::Memory:
m_context << eth::Instruction::MLOAD; m_context << eth::Instruction::MLOAD;
break; break;
case ReferenceType::Location::Storage: case DataLocation::Storage:
m_context << eth::Instruction::SLOAD; m_context << eth::Instruction::SLOAD;
break; break;
} }
@ -452,16 +452,16 @@ void ArrayUtils::retrieveLength(ArrayType const& _arrayType) const
void ArrayUtils::accessIndex(ArrayType const& _arrayType) const void ArrayUtils::accessIndex(ArrayType const& _arrayType) const
{ {
ReferenceType::Location location = _arrayType.location(); DataLocation location = _arrayType.location();
eth::Instruction load = eth::Instruction load =
location == ReferenceType::Location::Storage ? eth::Instruction::SLOAD : location == DataLocation::Storage ? eth::Instruction::SLOAD :
location == ReferenceType::Location::Memory ? eth::Instruction::MLOAD : location == DataLocation::Memory ? eth::Instruction::MLOAD :
eth::Instruction::CALLDATALOAD; eth::Instruction::CALLDATALOAD;
// retrieve length // retrieve length
if (!_arrayType.isDynamicallySized()) if (!_arrayType.isDynamicallySized())
m_context << _arrayType.getLength(); m_context << _arrayType.getLength();
else if (location == ReferenceType::Location::CallData) else if (location == DataLocation::CallData)
// length is stored on the stack // length is stored on the stack
m_context << eth::Instruction::SWAP1; m_context << eth::Instruction::SWAP1;
else else
@ -476,20 +476,20 @@ void ArrayUtils::accessIndex(ArrayType const& _arrayType) const
m_context << eth::Instruction::SWAP1; m_context << eth::Instruction::SWAP1;
if (_arrayType.isDynamicallySized()) if (_arrayType.isDynamicallySized())
{ {
if (location == ReferenceType::Location::Storage) if (location == DataLocation::Storage)
CompilerUtils(m_context).computeHashStatic(); CompilerUtils(m_context).computeHashStatic();
else if (location == ReferenceType::Location::Memory) else if (location == DataLocation::Memory)
m_context << u256(32) << eth::Instruction::ADD; m_context << u256(32) << eth::Instruction::ADD;
} }
// stack: <index> <data_ref> // stack: <index> <data_ref>
switch (location) switch (location)
{ {
case ReferenceType::Location::CallData: case DataLocation::CallData:
if (!_arrayType.isByteArray()) if (!_arrayType.isByteArray())
m_context {
<< eth::Instruction::SWAP1 m_context << eth::Instruction::SWAP1;
<< _arrayType.getBaseType()->getCalldataEncodedSize() m_context << _arrayType.getBaseType()->getCalldataEncodedSize() << eth::Instruction::MUL;
<< eth::Instruction::MUL; }
m_context << eth::Instruction::ADD; m_context << eth::Instruction::ADD;
if (_arrayType.getBaseType()->isValueType()) if (_arrayType.getBaseType()->isValueType())
CompilerUtils(m_context).loadFromMemoryDynamic( CompilerUtils(m_context).loadFromMemoryDynamic(
@ -499,7 +499,7 @@ void ArrayUtils::accessIndex(ArrayType const& _arrayType) const
false false
); );
break; break;
case ReferenceType::Location::Storage: case DataLocation::Storage:
m_context << eth::Instruction::SWAP1; m_context << eth::Instruction::SWAP1;
if (_arrayType.getBaseType()->getStorageBytes() <= 16) if (_arrayType.getBaseType()->getStorageBytes() <= 16)
{ {
@ -527,7 +527,7 @@ void ArrayUtils::accessIndex(ArrayType const& _arrayType) const
m_context << eth::Instruction::ADD << u256(0); m_context << eth::Instruction::ADD << u256(0);
} }
break; break;
case ReferenceType::Location::Memory: case DataLocation::Memory:
solAssert(false, "Memory lvalues not yet implemented."); solAssert(false, "Memory lvalues not yet implemented.");
} }
} }

40
libsolidity/Compiler.cpp

@ -245,21 +245,35 @@ void Compiler::appendCalldataUnpacker(
{ {
// We do not check the calldata size, everything is zero-paddedd // We do not check the calldata size, everything is zero-paddedd
//@todo this does not yet support nested arrays
if (_startOffset == u256(-1)) if (_startOffset == u256(-1))
_startOffset = u256(CompilerUtils::dataStartOffset); _startOffset = u256(CompilerUtils::dataStartOffset);
m_context << _startOffset; m_context << _startOffset;
for (TypePointer const& type: _typeParameters) for (TypePointer const& type: _typeParameters)
{ {
// stack: v1 v2 ... v(k-1) mem_offset
switch (type->getCategory()) switch (type->getCategory())
{ {
case Type::Category::Array: case Type::Category::Array:
{ {
auto const& arrayType = dynamic_cast<ArrayType const&>(*type); auto const& arrayType = dynamic_cast<ArrayType const&>(*type);
if (arrayType.location() == ReferenceType::Location::CallData) solAssert(arrayType.location() != DataLocation::Storage, "");
solAssert(!arrayType.getBaseType()->isDynamicallySized(), "Nested arrays not yet implemented.");
if (_fromMemory)
{
solAssert(arrayType.location() == DataLocation::Memory, "");
// compute data pointer
//@todo once we support nested arrays, this offset needs to be dynamic.
m_context << eth::Instruction::DUP1 << _startOffset << eth::Instruction::ADD;
m_context << eth::Instruction::SWAP1 << u256(0x20) << eth::Instruction::ADD;
}
else
{ {
solAssert(!_fromMemory, ""); // first load from calldata and potentially convert to memory if arrayType is memory
if (type->isDynamicallySized()) TypePointer calldataType = arrayType.copyForLocation(DataLocation::CallData, false);
if (calldataType->isDynamicallySized())
{ {
// put on stack: data_pointer length // put on stack: data_pointer length
CompilerUtils(m_context).loadFromMemoryDynamic(IntegerType(256), !_fromMemory); CompilerUtils(m_context).loadFromMemoryDynamic(IntegerType(256), !_fromMemory);
@ -276,17 +290,17 @@ void Compiler::appendCalldataUnpacker(
{ {
// leave the pointer on the stack // leave the pointer on the stack
m_context << eth::Instruction::DUP1; m_context << eth::Instruction::DUP1;
m_context << u256(type->getCalldataEncodedSize()) << eth::Instruction::ADD; m_context << u256(calldataType->getCalldataEncodedSize()) << eth::Instruction::ADD;
}
if (arrayType.location() == DataLocation::Memory)
{
// copy to memory
// move calldata type up again
CompilerUtils(m_context).moveIntoStack(calldataType->getSizeOnStack());
CompilerUtils(m_context).convertType(*calldataType, arrayType);
// fetch next pointer again
CompilerUtils(m_context).moveToStackTop(arrayType.getSizeOnStack());
} }
}
else
{
solAssert(arrayType.location() == ReferenceType::Location::Memory, "");
// compute data pointer
m_context << eth::Instruction::DUP1 << _startOffset << eth::Instruction::ADD;
if (!_fromMemory)
solAssert(false, "Not yet implemented.");
m_context << eth::Instruction::SWAP1 << u256(0x20) << eth::Instruction::ADD;
} }
break; break;
} }

64
libsolidity/CompilerUtils.cpp

@ -107,16 +107,18 @@ void CompilerUtils::storeInMemoryDynamic(Type const& _type, bool _padToWordBound
auto const& type = dynamic_cast<ArrayType const&>(_type); auto const& type = dynamic_cast<ArrayType const&>(_type);
solAssert(type.isByteArray(), "Non byte arrays not yet implemented here."); solAssert(type.isByteArray(), "Non byte arrays not yet implemented here.");
if (type.location() == ReferenceType::Location::CallData) if (type.location() == DataLocation::CallData)
{ {
if (!type.isDynamicallySized())
m_context << type.getLength();
// stack: target source_offset source_len // stack: target source_offset source_len
m_context << eth::Instruction::DUP1 << eth::Instruction::DUP3 << eth::Instruction::DUP5; m_context << eth::Instruction::DUP1 << eth::Instruction::DUP3 << eth::Instruction::DUP5;
// stack: target source_offset source_len source_len source_offset target // stack: target source_offset source_len source_len source_offset target
m_context << eth::Instruction::CALLDATACOPY; m_context << eth::Instruction::CALLDATACOPY;
m_context << eth::Instruction::DUP3 << eth::Instruction::ADD; m_context << eth::Instruction::DUP3 << eth::Instruction::ADD;
m_context << eth::Instruction::SWAP2 << eth::Instruction::POP << eth::Instruction::POP; m_context << eth::Instruction::SWAP2 << eth::Instruction::POP << eth::Instruction::POP;
} }
else if (type.location() == ReferenceType::Location::Memory) else if (type.location() == DataLocation::Memory)
{ {
// memcpy using the built-in contract // memcpy using the built-in contract
ArrayUtils(m_context).retrieveLength(type); ArrayUtils(m_context).retrieveLength(type);
@ -183,7 +185,7 @@ void CompilerUtils::storeInMemoryDynamic(Type const& _type, bool _padToWordBound
} }
else else
{ {
solAssert(type.location() == ReferenceType::Location::Storage, ""); solAssert(type.location() == DataLocation::Storage, "");
m_context << eth::Instruction::POP; // remove offset, arrays always start new slot m_context << eth::Instruction::POP; // remove offset, arrays always start new slot
m_context << eth::Instruction::DUP1 << eth::Instruction::SLOAD; m_context << eth::Instruction::DUP1 << eth::Instruction::SLOAD;
// stack here: memory_offset storage_offset length_bytes // stack here: memory_offset storage_offset length_bytes
@ -274,10 +276,16 @@ void CompilerUtils::encodeToMemory(
else else
{ {
copyToStackTop(argSize - stackPos + dynPointers + 2, _givenTypes[i]->getSizeOnStack()); copyToStackTop(argSize - stackPos + dynPointers + 2, _givenTypes[i]->getSizeOnStack());
if (targetType->isValueType())
convertType(*_givenTypes[i], *targetType, true);
solAssert(!!targetType, "Externalable type expected."); solAssert(!!targetType, "Externalable type expected.");
storeInMemoryDynamic(*targetType, _padToWordBoundaries); TypePointer type = targetType;
if (
_givenTypes[i]->dataStoredIn(DataLocation::Storage) ||
_givenTypes[i]->dataStoredIn(DataLocation::CallData)
)
type = _givenTypes[i]; // delay conversion
else
convertType(*_givenTypes[i], *targetType, true);
storeInMemoryDynamic(*type, _padToWordBoundaries);
} }
stackPos += _givenTypes[i]->getSizeOnStack(); stackPos += _givenTypes[i]->getSizeOnStack();
} }
@ -304,13 +312,13 @@ void CompilerUtils::encodeToMemory(
// stack: ... <end_of_mem> <value...> // stack: ... <end_of_mem> <value...>
// copy length to memory // copy length to memory
m_context << eth::dupInstruction(1 + arrayType.getSizeOnStack()); m_context << eth::dupInstruction(1 + arrayType.getSizeOnStack());
if (arrayType.location() == ReferenceType::Location::CallData) if (arrayType.location() == DataLocation::CallData)
m_context << eth::Instruction::DUP2; // length is on stack m_context << eth::Instruction::DUP2; // length is on stack
else if (arrayType.location() == ReferenceType::Location::Storage) else if (arrayType.location() == DataLocation::Storage)
m_context << eth::Instruction::DUP3 << eth::Instruction::SLOAD; m_context << eth::Instruction::DUP3 << eth::Instruction::SLOAD;
else else
{ {
solAssert(arrayType.location() == ReferenceType::Location::Memory, ""); solAssert(arrayType.location() == DataLocation::Memory, "");
m_context << eth::Instruction::DUP2 << eth::Instruction::MLOAD; m_context << eth::Instruction::DUP2 << eth::Instruction::MLOAD;
} }
// stack: ... <end_of_mem> <value...> <end_of_mem'> <length> // stack: ... <end_of_mem> <value...> <end_of_mem'> <length>
@ -432,18 +440,18 @@ void CompilerUtils::convertType(Type const& _typeOnStack, Type const& _targetTyp
ArrayType const& targetType = dynamic_cast<ArrayType const&>(_targetType); ArrayType const& targetType = dynamic_cast<ArrayType const&>(_targetType);
switch (targetType.location()) switch (targetType.location())
{ {
case ReferenceType::Location::Storage: case DataLocation::Storage:
// Other cases are done explicitly in LValue::storeValue, and only possible by assignment. // Other cases are done explicitly in LValue::storeValue, and only possible by assignment.
solAssert( solAssert(
targetType.isPointer() && targetType.isPointer() &&
typeOnStack.location() == ReferenceType::Location::Storage, typeOnStack.location() == DataLocation::Storage,
"Invalid conversion to storage type." "Invalid conversion to storage type."
); );
break; break;
case ReferenceType::Location::Memory: case DataLocation::Memory:
{ {
// Copy the array to a free position in memory, unless it is already in memory. // Copy the array to a free position in memory, unless it is already in memory.
if (typeOnStack.location() != ReferenceType::Location::Memory) if (typeOnStack.location() != DataLocation::Memory)
{ {
// stack: <source ref> (variably sized) // stack: <source ref> (variably sized)
unsigned stackSize = typeOnStack.getSizeOnStack(); unsigned stackSize = typeOnStack.getSizeOnStack();
@ -452,7 +460,7 @@ void CompilerUtils::convertType(Type const& _typeOnStack, Type const& _targetTyp
// stack: <mem start> <source ref> (variably sized) // stack: <mem start> <source ref> (variably sized)
if (targetType.isDynamicallySized()) if (targetType.isDynamicallySized())
{ {
bool fromStorage = (typeOnStack.location() == ReferenceType::Location::Storage); bool fromStorage = (typeOnStack.location() == DataLocation::Storage);
// store length // store length
if (fromStorage) if (fromStorage)
{ {
@ -483,11 +491,25 @@ void CompilerUtils::convertType(Type const& _typeOnStack, Type const& _targetTyp
// Stack <mem start> <mem end> // Stack <mem start> <mem end>
storeFreeMemoryPointer(); storeFreeMemoryPointer();
} }
else if (typeOnStack.location() == ReferenceType::Location::CallData) else if (typeOnStack.location() == DataLocation::CallData)
{ {
// Stack: <offset> <length> // Stack: <offset> [<length>]
//@todo // length is present if dynamically sized
solAssert(false, "Not yet implemented."); fetchFreeMemoryPointer();
moveIntoStack(typeOnStack.getSizeOnStack());
// stack: memptr calldataoffset [<length>]
if (typeOnStack.isDynamicallySized())
{
solAssert(targetType.isDynamicallySized(), "");
m_context << eth::Instruction::DUP3 << eth::Instruction::DUP2;
storeInMemoryDynamic(IntegerType(256));
moveIntoStack(typeOnStack.getSizeOnStack());
}
else
m_context << eth::Instruction::DUP2 << eth::Instruction::SWAP1;
// stack: mem_ptr mem_data_ptr calldataoffset [<length>]
storeInMemoryDynamic(typeOnStack);
storeFreeMemoryPointer();
} }
// nothing to do for memory to memory // nothing to do for memory to memory
break; break;
@ -504,8 +526,8 @@ void CompilerUtils::convertType(Type const& _typeOnStack, Type const& _targetTyp
auto& targetType = dynamic_cast<StructType const&>(_targetType); auto& targetType = dynamic_cast<StructType const&>(_targetType);
auto& stackType = dynamic_cast<StructType const&>(_typeOnStack); auto& stackType = dynamic_cast<StructType const&>(_typeOnStack);
solAssert( solAssert(
targetType.location() == ReferenceType::Location::Storage && targetType.location() == DataLocation::Storage &&
stackType.location() == ReferenceType::Location::Storage, stackType.location() == DataLocation::Storage,
"Non-storage structs not yet implemented." "Non-storage structs not yet implemented."
); );
solAssert( solAssert(

5
libsolidity/CompilerUtils.h

@ -99,8 +99,9 @@ public:
bool _copyDynamicDataInPlace = false bool _copyDynamicDataInPlace = false
); );
/// Appends code for an implicit or explicit type conversion. For now this comprises only erasing /// Appends code for an implicit or explicit type conversion. This includes erasing higher
/// higher-order bits (@see appendHighBitCleanup) when widening integer. /// order bits (@see appendHighBitCleanup) when widening integer but also copy to memory
/// if a reference type is converted from calldata or storage to memory.
/// If @a _cleanupNeeded, high order bits cleanup is also done if no type conversion would be /// If @a _cleanupNeeded, high order bits cleanup is also done if no type conversion would be
/// necessary. /// necessary.
void convertType(Type const& _typeOnStack, Type const& _targetType, bool _cleanupNeeded = false); void convertType(Type const& _typeOnStack, Type const& _targetType, bool _cleanupNeeded = false);

55
libsolidity/ExpressionCompiler.cpp

@ -109,34 +109,40 @@ void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const&
} }
unsigned retSizeOnStack = 0; unsigned retSizeOnStack = 0;
solAssert(accessorType.getReturnParameterTypes().size() >= 1, ""); solAssert(accessorType.getReturnParameterTypes().size() >= 1, "");
auto const& returnTypes = accessorType.getReturnParameterTypes();
if (StructType const* structType = dynamic_cast<StructType const*>(returnType.get())) if (StructType const* structType = dynamic_cast<StructType const*>(returnType.get()))
{ {
// remove offset // remove offset
m_context << eth::Instruction::POP; m_context << eth::Instruction::POP;
auto const& names = accessorType.getReturnParameterNames(); auto const& names = accessorType.getReturnParameterNames();
auto const& types = accessorType.getReturnParameterTypes();
// struct // struct
for (size_t i = 0; i < names.size(); ++i) for (size_t i = 0; i < names.size(); ++i)
{ {
if (types[i]->getCategory() == Type::Category::Mapping || types[i]->getCategory() == Type::Category::Array) if (returnTypes[i]->getCategory() == Type::Category::Mapping)
continue; continue;
if (auto arrayType = dynamic_cast<ArrayType const*>(returnTypes[i].get()))
if (!arrayType->isByteArray())
continue;
pair<u256, unsigned> const& offsets = structType->getStorageOffsetsOfMember(names[i]); pair<u256, unsigned> const& offsets = structType->getStorageOffsetsOfMember(names[i]);
m_context << eth::Instruction::DUP1 << u256(offsets.first) << eth::Instruction::ADD << u256(offsets.second); m_context << eth::Instruction::DUP1 << u256(offsets.first) << eth::Instruction::ADD << u256(offsets.second);
StorageItem(m_context, *types[i]).retrieveValue(SourceLocation(), true); TypePointer memberType = structType->getMemberType(names[i]);
solAssert(types[i]->getSizeOnStack() == 1, "Returning struct elements with stack size != 1 is not yet implemented."); StorageItem(m_context, *memberType).retrieveValue(SourceLocation(), true);
m_context << eth::Instruction::SWAP1; utils().convertType(*memberType, *returnTypes[i]);
retSizeOnStack += types[i]->getSizeOnStack(); utils().moveToStackTop(returnTypes[i]->getSizeOnStack());
retSizeOnStack += returnTypes[i]->getSizeOnStack();
} }
// remove slot // remove slot
m_context << eth::Instruction::POP; m_context << eth::Instruction::POP;
} }
else else
{ {
// simple value // simple value or array
solAssert(accessorType.getReturnParameterTypes().size() == 1, ""); solAssert(returnTypes.size() == 1, "");
StorageItem(m_context, *returnType).retrieveValue(SourceLocation(), true); StorageItem(m_context, *returnType).retrieveValue(SourceLocation(), true);
retSizeOnStack = returnType->getSizeOnStack(); utils().convertType(*returnType, *returnTypes.front());
retSizeOnStack = returnTypes.front()->getSizeOnStack();
} }
solAssert(retSizeOnStack == utils().getSizeOnStack(returnTypes), "");
solAssert(retSizeOnStack <= 15, "Stack is too deep."); solAssert(retSizeOnStack <= 15, "Stack is too deep.");
m_context << eth::dupInstruction(retSizeOnStack + 1); m_context << eth::dupInstruction(retSizeOnStack + 1);
m_context.appendJump(eth::AssemblyItem::JumpType::OutOfFunction); m_context.appendJump(eth::AssemblyItem::JumpType::OutOfFunction);
@ -146,10 +152,13 @@ bool ExpressionCompiler::visit(Assignment const& _assignment)
{ {
CompilerContext::LocationSetter locationSetter(m_context, _assignment); CompilerContext::LocationSetter locationSetter(m_context, _assignment);
_assignment.getRightHandSide().accept(*this); _assignment.getRightHandSide().accept(*this);
if (_assignment.getType()->isValueType()) TypePointer type = _assignment.getRightHandSide().getType();
utils().convertType(*_assignment.getRightHandSide().getType(), *_assignment.getType()); if (!_assignment.getType()->dataStoredIn(DataLocation::Storage))
// We need this conversion mostly in the case of compound assignments. For non-value types {
// the conversion is done in LValue::storeValue. utils().convertType(*type, *_assignment.getType());
type = _assignment.getType();
}
_assignment.getLeftHandSide().accept(*this); _assignment.getLeftHandSide().accept(*this);
solAssert(!!m_currentLValue, "LValue not retrieved."); solAssert(!!m_currentLValue, "LValue not retrieved.");
@ -175,7 +184,7 @@ bool ExpressionCompiler::visit(Assignment const& _assignment)
m_context << eth::swapInstruction(itemSize + lvalueSize) << eth::Instruction::POP; m_context << eth::swapInstruction(itemSize + lvalueSize) << eth::Instruction::POP;
} }
} }
m_currentLValue->storeValue(*_assignment.getRightHandSide().getType(), _assignment.getLocation()); m_currentLValue->storeValue(*type, _assignment.getLocation());
m_currentLValue.reset(); m_currentLValue.reset();
return false; return false;
} }
@ -709,10 +718,10 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess)
else else
switch (type.location()) switch (type.location())
{ {
case ReferenceType::Location::CallData: case DataLocation::CallData:
m_context << eth::Instruction::SWAP1 << eth::Instruction::POP; m_context << eth::Instruction::SWAP1 << eth::Instruction::POP;
break; break;
case ReferenceType::Location::Storage: case DataLocation::Storage:
setLValue<StorageArrayLength>(_memberAccess, type); setLValue<StorageArrayLength>(_memberAccess, type);
break; break;
default: default:
@ -755,13 +764,13 @@ bool ExpressionCompiler::visit(IndexAccess const& _indexAccess)
solAssert(_indexAccess.getIndexExpression(), "Index expression expected."); solAssert(_indexAccess.getIndexExpression(), "Index expression expected.");
// remove storage byte offset // remove storage byte offset
if (arrayType.location() == ReferenceType::Location::Storage) if (arrayType.location() == DataLocation::Storage)
m_context << eth::Instruction::POP; m_context << eth::Instruction::POP;
_indexAccess.getIndexExpression()->accept(*this); _indexAccess.getIndexExpression()->accept(*this);
// stack layout: <base_ref> [<length>] <index> // stack layout: <base_ref> [<length>] <index>
ArrayUtils(m_context).accessIndex(arrayType); ArrayUtils(m_context).accessIndex(arrayType);
if (arrayType.location() == ReferenceType::Location::Storage) if (arrayType.location() == DataLocation::Storage)
{ {
if (arrayType.isByteArray()) if (arrayType.isByteArray())
{ {
@ -1119,14 +1128,10 @@ void ExpressionCompiler::appendExternalFunctionCall(
void ExpressionCompiler::appendExpressionCopyToMemory(Type const& _expectedType, Expression const& _expression) void ExpressionCompiler::appendExpressionCopyToMemory(Type const& _expectedType, Expression const& _expression)
{ {
solAssert(_expectedType.isValueType(), "Not implemented for non-value types.");
_expression.accept(*this); _expression.accept(*this);
if (_expectedType.isValueType()) utils().convertType(*_expression.getType(), _expectedType, true);
{ utils().storeInMemoryDynamic(_expectedType);
utils().convertType(*_expression.getType(), _expectedType, true);
utils().storeInMemoryDynamic(_expectedType);
}
else
utils().storeInMemoryDynamic(*_expression.getType()->mobileType());
} }
void ExpressionCompiler::setLValueFromDeclaration(Declaration const& _declaration, Expression const& _expression) void ExpressionCompiler::setLValueFromDeclaration(Declaration const& _declaration, Expression const& _expression)

8
libsolidity/NameAndTypeResolver.cpp

@ -439,7 +439,7 @@ void ReferencesResolver::endVisit(VariableDeclaration& _variable)
"Location has to be calldata for external functions " "Location has to be calldata for external functions "
"(remove the \"memory\" or \"storage\" keyword)." "(remove the \"memory\" or \"storage\" keyword)."
)); ));
type = ref->copyForLocation(ReferenceType::Location::CallData, true); type = ref->copyForLocation(DataLocation::CallData, true);
} }
else if (_variable.isCallableParameter() && _variable.getScope()->isPublic()) else if (_variable.isCallableParameter() && _variable.getScope()->isPublic())
{ {
@ -449,7 +449,7 @@ void ReferencesResolver::endVisit(VariableDeclaration& _variable)
"Location has to be memory for publicly visible functions " "Location has to be memory for publicly visible functions "
"(remove the \"storage\" keyword)." "(remove the \"storage\" keyword)."
)); ));
type = ref->copyForLocation(ReferenceType::Location::Memory, true); type = ref->copyForLocation(DataLocation::Memory, true);
} }
else else
{ {
@ -458,8 +458,8 @@ void ReferencesResolver::endVisit(VariableDeclaration& _variable)
bool isPointer = !_variable.isStateVariable(); bool isPointer = !_variable.isStateVariable();
type = ref->copyForLocation( type = ref->copyForLocation(
loc == Location::Memory ? loc == Location::Memory ?
ReferenceType::Location::Memory : DataLocation::Memory :
ReferenceType::Location::Storage, DataLocation::Storage,
isPointer isPointer
); );
} }

62
libsolidity/Types.cpp

@ -144,12 +144,13 @@ TypePointer Type::fromElementaryTypeName(Token::Value _typeToken)
else if (_typeToken == Token::Bool) else if (_typeToken == Token::Bool)
return make_shared<BoolType>(); return make_shared<BoolType>();
else if (_typeToken == Token::Bytes) else if (_typeToken == Token::Bytes)
return make_shared<ArrayType>(ReferenceType::Location::Storage); return make_shared<ArrayType>(DataLocation::Storage);
else if (_typeToken == Token::String) else if (_typeToken == Token::String)
return make_shared<ArrayType>(ReferenceType::Location::Storage, true); return make_shared<ArrayType>(DataLocation::Storage, true);
else else
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unable to convert elementary typename " + BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment(
std::string(Token::toString(_typeToken)) + " to type.")); "Unable to convert elementary typename " + std::string(Token::toString(_typeToken)) + " to type."
));
} }
TypePointer Type::fromElementaryTypeName(string const& _name) TypePointer Type::fromElementaryTypeName(string const& _name)
@ -180,7 +181,7 @@ TypePointer Type::fromMapping(ElementaryTypeName& _keyType, TypeName& _valueType
if (!valueType) if (!valueType)
BOOST_THROW_EXCEPTION(_valueType.createTypeError("Invalid type name.")); BOOST_THROW_EXCEPTION(_valueType.createTypeError("Invalid type name."));
// Convert value type to storage reference. // Convert value type to storage reference.
valueType = ReferenceType::copyForLocationIfReference(ReferenceType::Location::Storage, valueType); valueType = ReferenceType::copyForLocationIfReference(DataLocation::Storage, valueType);
return make_shared<MappingType>(keyType, valueType); return make_shared<MappingType>(keyType, valueType);
} }
@ -198,10 +199,10 @@ TypePointer Type::fromArrayTypeName(TypeName& _baseTypeName, Expression* _length
auto const* length = dynamic_cast<IntegerConstantType const*>(_length->getType().get()); auto const* length = dynamic_cast<IntegerConstantType const*>(_length->getType().get());
if (!length) if (!length)
BOOST_THROW_EXCEPTION(_length->createTypeError("Invalid array length.")); BOOST_THROW_EXCEPTION(_length->createTypeError("Invalid array length."));
return make_shared<ArrayType>(ReferenceType::Location::Storage, baseType, length->literalValue(nullptr)); return make_shared<ArrayType>(DataLocation::Storage, baseType, length->literalValue(nullptr));
} }
else else
return make_shared<ArrayType>(ReferenceType::Location::Storage, baseType); return make_shared<ArrayType>(DataLocation::Storage, baseType);
} }
TypePointer Type::forLiteral(Literal const& _literal) TypePointer Type::forLiteral(Literal const& _literal)
@ -670,7 +671,7 @@ TypePointer ContractType::unaryOperatorResult(Token::Value _operator) const
return _operator == Token::Delete ? make_shared<VoidType>() : TypePointer(); return _operator == Token::Delete ? make_shared<VoidType>() : TypePointer();
} }
TypePointer ReferenceType::copyForLocationIfReference(Location _location, TypePointer const& _type) TypePointer ReferenceType::copyForLocationIfReference(DataLocation _location, TypePointer const& _type)
{ {
if (auto type = dynamic_cast<ReferenceType const*>(_type.get())) if (auto type = dynamic_cast<ReferenceType const*>(_type.get()))
return type->copyForLocation(_location, false); return type->copyForLocation(_location, false);
@ -686,11 +687,11 @@ string ReferenceType::stringForReferencePart() const
{ {
switch (m_location) switch (m_location)
{ {
case Location::Storage: case DataLocation::Storage:
return string("storage ") + (m_isPointer ? "pointer" : "ref"); return string("storage ") + (m_isPointer ? "pointer" : "ref");
case Location::CallData: case DataLocation::CallData:
return "calldata"; return "calldata";
case Location::Memory: case DataLocation::Memory:
return "memory"; return "memory";
} }
solAssert(false, ""); solAssert(false, "");
@ -705,11 +706,11 @@ bool ArrayType::isImplicitlyConvertibleTo(const Type& _convertTo) const
if (convertTo.isByteArray() != isByteArray() || convertTo.isString() != isString()) if (convertTo.isByteArray() != isByteArray() || convertTo.isString() != isString())
return false; return false;
// memory/calldata to storage can be converted, but only to a direct storage reference // memory/calldata to storage can be converted, but only to a direct storage reference
if (convertTo.location() == Location::Storage && location() != Location::Storage && convertTo.isPointer()) if (convertTo.location() == DataLocation::Storage && location() != DataLocation::Storage && convertTo.isPointer())
return false; return false;
if (convertTo.location() == Location::CallData && location() != convertTo.location()) if (convertTo.location() == DataLocation::CallData && location() != convertTo.location())
return false; return false;
if (convertTo.location() == Location::Storage && !convertTo.isPointer()) if (convertTo.location() == DataLocation::Storage && !convertTo.isPointer())
{ {
// Less restrictive conversion, since we need to copy anyway. // Less restrictive conversion, since we need to copy anyway.
if (!getBaseType()->isImplicitlyConvertibleTo(*convertTo.getBaseType())) if (!getBaseType()->isImplicitlyConvertibleTo(*convertTo.getBaseType()))
@ -788,10 +789,10 @@ u256 ArrayType::getStorageSize() const
unsigned ArrayType::getSizeOnStack() const unsigned ArrayType::getSizeOnStack() const
{ {
if (m_location == Location::CallData) if (m_location == DataLocation::CallData)
// offset [length] (stack top) // offset [length] (stack top)
return 1 + (isDynamicallySized() ? 1 : 0); return 1 + (isDynamicallySized() ? 1 : 0);
else if (m_location == Location::Storage) else if (m_location == DataLocation::Storage)
// storage_key storage_offset // storage_key storage_offset
return 2; return 2;
else else
@ -828,12 +829,12 @@ TypePointer ArrayType::externalType() const
return TypePointer(); return TypePointer();
if (isDynamicallySized()) if (isDynamicallySized())
return std::make_shared<ArrayType>(Location::CallData, m_baseType->externalType()); return std::make_shared<ArrayType>(DataLocation::CallData, m_baseType->externalType());
else else
return std::make_shared<ArrayType>(Location::CallData, m_baseType->externalType(), m_length); return std::make_shared<ArrayType>(DataLocation::CallData, m_baseType->externalType(), m_length);
} }
TypePointer ArrayType::copyForLocation(ReferenceType::Location _location, bool _isPointer) const TypePointer ArrayType::copyForLocation(DataLocation _location, bool _isPointer) const
{ {
auto copy = make_shared<ArrayType>(_location); auto copy = make_shared<ArrayType>(_location);
copy->m_isPointer = _isPointer; copy->m_isPointer = _isPointer;
@ -949,9 +950,9 @@ bool StructType::isImplicitlyConvertibleTo(const Type& _convertTo) const
return false; return false;
auto& convertTo = dynamic_cast<StructType const&>(_convertTo); auto& convertTo = dynamic_cast<StructType const&>(_convertTo);
// memory/calldata to storage can be converted, but only to a direct storage reference // memory/calldata to storage can be converted, but only to a direct storage reference
if (convertTo.location() == Location::Storage && location() != Location::Storage && convertTo.isPointer()) if (convertTo.location() == DataLocation::Storage && location() != DataLocation::Storage && convertTo.isPointer())
return false; return false;
if (convertTo.location() == Location::CallData && location() != convertTo.location()) if (convertTo.location() == DataLocation::CallData && location() != convertTo.location())
return false; return false;
return this->m_struct == convertTo.m_struct; return this->m_struct == convertTo.m_struct;
} }
@ -1009,7 +1010,7 @@ MemberList const& StructType::getMembers() const
return *m_members; return *m_members;
} }
TypePointer StructType::copyForLocation(ReferenceType::Location _location, bool _isPointer) const TypePointer StructType::copyForLocation(DataLocation _location, bool _isPointer) const
{ {
auto copy = make_shared<StructType>(m_struct); auto copy = make_shared<StructType>(m_struct);
copy->m_location = _location; copy->m_location = _location;
@ -1115,6 +1116,9 @@ FunctionType::FunctionType(VariableDeclaration const& _varDecl):
} }
else if (auto arrayType = dynamic_cast<ArrayType const*>(returnType.get())) else if (auto arrayType = dynamic_cast<ArrayType const*>(returnType.get()))
{ {
if (arrayType->isByteArray())
// Return byte arrays as as whole.
break;
returnType = arrayType->getBaseType(); returnType = arrayType->getBaseType();
paramNames.push_back(""); paramNames.push_back("");
paramTypes.push_back(make_shared<IntegerType>(256)); paramTypes.push_back(make_shared<IntegerType>(256));
@ -1128,15 +1132,21 @@ FunctionType::FunctionType(VariableDeclaration const& _varDecl):
if (auto structType = dynamic_cast<StructType const*>(returnType.get())) if (auto structType = dynamic_cast<StructType const*>(returnType.get()))
{ {
for (auto const& member: structType->getMembers()) for (auto const& member: structType->getMembers())
if (member.type->getCategory() != Category::Mapping && member.type->getCategory() != Category::Array) if (member.type->getCategory() != Category::Mapping)
{ {
retParamNames.push_back(member.name); if (auto arrayType = dynamic_cast<ArrayType const*>(member.type.get()))
if (!arrayType->isByteArray())
continue;
retParams.push_back(member.type); retParams.push_back(member.type);
retParamNames.push_back(member.name);
} }
} }
else else
{ {
retParams.push_back(returnType); retParams.push_back(ReferenceType::copyForLocationIfReference(
DataLocation::Memory,
returnType
));
retParamNames.push_back(""); retParamNames.push_back("");
} }
@ -1549,7 +1559,7 @@ MagicType::MagicType(MagicType::Kind _kind):
{"sender", make_shared<IntegerType>(0, IntegerType::Modifier::Address)}, {"sender", make_shared<IntegerType>(0, IntegerType::Modifier::Address)},
{"gas", make_shared<IntegerType>(256)}, {"gas", make_shared<IntegerType>(256)},
{"value", make_shared<IntegerType>(256)}, {"value", make_shared<IntegerType>(256)},
{"data", make_shared<ArrayType>(ReferenceType::Location::CallData)}, {"data", make_shared<ArrayType>(DataLocation::CallData)},
{"sig", make_shared<FixedBytesType>(4)} {"sig", make_shared<FixedBytesType>(4)}
}); });
break; break;

32
libsolidity/Types.h

@ -44,6 +44,8 @@ using FunctionTypePointer = std::shared_ptr<FunctionType const>;
using TypePointers = std::vector<TypePointer>; using TypePointers = std::vector<TypePointer>;
enum class DataLocation { Storage, CallData, Memory };
/** /**
* Helper class to compute storage offsets of members of structs and contracts. * Helper class to compute storage offsets of members of structs and contracts.
*/ */
@ -202,6 +204,9 @@ public:
/// This returns the corresponding integer type for IntegerConstantTypes and the pointer type /// This returns the corresponding integer type for IntegerConstantTypes and the pointer type
/// for storage reference types. /// for storage reference types.
virtual TypePointer mobileType() const { return shared_from_this(); } virtual TypePointer mobileType() const { return shared_from_this(); }
/// @returns true if this is a non-value type and the data of this type is stored at the
/// given location.
virtual bool dataStoredIn(DataLocation) const { return false; }
/// Returns the list of all members of this type. Default implementation: no members. /// Returns the list of all members of this type. Default implementation: no members.
virtual MemberList const& getMembers() const { return EmptyMemberList; } virtual MemberList const& getMembers() const { return EmptyMemberList; }
@ -365,15 +370,15 @@ public:
class ReferenceType: public Type class ReferenceType: public Type
{ {
public: public:
enum class Location { Storage, CallData, Memory }; explicit ReferenceType(DataLocation _location): m_location(_location) {}
explicit ReferenceType(Location _location): m_location(_location) {} DataLocation location() const { return m_location; }
Location location() const { return m_location; }
/// @returns a copy of this type with location (recursively) changed to @a _location, /// @returns a copy of this type with location (recursively) changed to @a _location,
/// whereas isPointer is only shallowly changed - the deep copy is always a bound reference. /// whereas isPointer is only shallowly changed - the deep copy is always a bound reference.
virtual TypePointer copyForLocation(Location _location, bool _isPointer) const = 0; virtual TypePointer copyForLocation(DataLocation _location, bool _isPointer) const = 0;
virtual TypePointer mobileType() const override { return copyForLocation(m_location, true); } virtual TypePointer mobileType() const override { return copyForLocation(m_location, true); }
virtual bool dataStoredIn(DataLocation _location) const override { return m_location == _location; }
/// Storage references can be pointers or bound references. In general, local variables are of /// Storage references can be pointers or bound references. In general, local variables are of
/// pointer type, state variables are bound references. Assignments to pointers or deleting /// pointer type, state variables are bound references. Assignments to pointers or deleting
@ -389,14 +394,14 @@ public:
/// @returns a copy of @a _type having the same location as this (and is not a pointer type) /// @returns a copy of @a _type having the same location as this (and is not a pointer type)
/// if _type is a reference type and an unmodified copy of _type otherwise. /// if _type is a reference type and an unmodified copy of _type otherwise.
/// This function is mostly useful to modify inner types appropriately. /// This function is mostly useful to modify inner types appropriately.
static TypePointer copyForLocationIfReference(Location _location, TypePointer const& _type); static TypePointer copyForLocationIfReference(DataLocation _location, TypePointer const& _type);
protected: protected:
TypePointer copyForLocationIfReference(TypePointer const& _type) const; TypePointer copyForLocationIfReference(TypePointer const& _type) const;
/// @returns a human-readable description of the reference part of the type. /// @returns a human-readable description of the reference part of the type.
std::string stringForReferencePart() const; std::string stringForReferencePart() const;
Location m_location = Location::Storage; DataLocation m_location = DataLocation::Storage;
bool m_isPointer = true; bool m_isPointer = true;
}; };
@ -413,20 +418,20 @@ public:
virtual Category getCategory() const override { return Category::Array; } virtual Category getCategory() const override { return Category::Array; }
/// Constructor for a byte array ("bytes") and string. /// Constructor for a byte array ("bytes") and string.
explicit ArrayType(Location _location, bool _isString = false): explicit ArrayType(DataLocation _location, bool _isString = false):
ReferenceType(_location), ReferenceType(_location),
m_arrayKind(_isString ? ArrayKind::String : ArrayKind::Bytes), m_arrayKind(_isString ? ArrayKind::String : ArrayKind::Bytes),
m_baseType(std::make_shared<FixedBytesType>(1)) m_baseType(std::make_shared<FixedBytesType>(1))
{ {
} }
/// Constructor for a dynamically sized array type ("type[]") /// Constructor for a dynamically sized array type ("type[]")
ArrayType(Location _location, TypePointer const& _baseType): ArrayType(DataLocation _location, TypePointer const& _baseType):
ReferenceType(_location), ReferenceType(_location),
m_baseType(copyForLocationIfReference(_baseType)) m_baseType(copyForLocationIfReference(_baseType))
{ {
} }
/// Constructor for a fixed-size array type ("type[20]") /// Constructor for a fixed-size array type ("type[20]")
ArrayType(Location _location, TypePointer const& _baseType, u256 const& _length): ArrayType(DataLocation _location, TypePointer const& _baseType, u256 const& _length):
ReferenceType(_location), ReferenceType(_location),
m_baseType(copyForLocationIfReference(_baseType)), m_baseType(copyForLocationIfReference(_baseType)),
m_hasDynamicLength(false), m_hasDynamicLength(false),
@ -454,7 +459,7 @@ public:
TypePointer const& getBaseType() const { solAssert(!!m_baseType, ""); return m_baseType;} TypePointer const& getBaseType() const { solAssert(!!m_baseType, ""); return m_baseType;}
u256 const& getLength() const { return m_length; } u256 const& getLength() const { return m_length; }
TypePointer copyForLocation(Location _location, bool _isPointer) const override; TypePointer copyForLocation(DataLocation _location, bool _isPointer) const override;
private: private:
/// String is interpreted as a subtype of Bytes. /// String is interpreted as a subtype of Bytes.
@ -533,7 +538,7 @@ public:
virtual Category getCategory() const override { return Category::Struct; } virtual Category getCategory() const override { return Category::Struct; }
explicit StructType(StructDefinition const& _struct): explicit StructType(StructDefinition const& _struct):
//@todo only storage until we have non-storage structs //@todo only storage until we have non-storage structs
ReferenceType(Location::Storage), m_struct(_struct) {} ReferenceType(DataLocation::Storage), m_struct(_struct) {}
virtual bool isImplicitlyConvertibleTo(const Type& _convertTo) const override; virtual bool isImplicitlyConvertibleTo(const Type& _convertTo) const override;
virtual TypePointer unaryOperatorResult(Token::Value _operator) const override; virtual TypePointer unaryOperatorResult(Token::Value _operator) const override;
virtual bool operator==(Type const& _other) const override; virtual bool operator==(Type const& _other) const override;
@ -544,7 +549,7 @@ public:
virtual MemberList const& getMembers() const override; virtual MemberList const& getMembers() const override;
TypePointer copyForLocation(Location _location, bool _isPointer) const override; TypePointer copyForLocation(DataLocation _location, bool _isPointer) const override;
std::pair<u256, unsigned> const& getStorageOffsetsOfMember(std::string const& _name) const; std::pair<u256, unsigned> const& getStorageOffsetsOfMember(std::string const& _name) const;
@ -636,8 +641,11 @@ public:
FunctionTypePointer externalFunctionType() const; FunctionTypePointer externalFunctionType() const;
virtual TypePointer externalType() const override { return externalFunctionType(); } virtual TypePointer externalType() const override { return externalFunctionType(); }
/// Creates the type of a function.
explicit FunctionType(FunctionDefinition const& _function, bool _isInternal = true); explicit FunctionType(FunctionDefinition const& _function, bool _isInternal = true);
/// Creates the accessor function type of a state variable.
explicit FunctionType(VariableDeclaration const& _varDecl); explicit FunctionType(VariableDeclaration const& _varDecl);
/// Creates the function type of an event.
explicit FunctionType(EventDefinition const& _event); explicit FunctionType(EventDefinition const& _event);
FunctionType( FunctionType(
strings const& _parameterTypes, strings const& _parameterTypes,

2
libweb3jsonrpc/JsonHelper.h

@ -43,7 +43,7 @@ namespace eth
{ {
class Transaction; class Transaction;
class BlockDetails; struct BlockDetails;
class Interface; class Interface;
using Transactions = std::vector<Transaction>; using Transactions = std::vector<Transaction>;
using UncleHashes = h256s; using UncleHashes = h256s;

1
mix/MixClient.h

@ -59,6 +59,7 @@ public:
dev::eth::ExecutionResult call(Address const& _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice, eth::BlockNumber _blockNumber = eth::PendingBlock, eth::FudgeFactor _ff = eth::FudgeFactor::Strict) override; dev::eth::ExecutionResult call(Address const& _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice, eth::BlockNumber _blockNumber = eth::PendingBlock, eth::FudgeFactor _ff = eth::FudgeFactor::Strict) override;
dev::eth::ExecutionResult create(Address const& _secret, u256 _value, bytes const& _data = bytes(), u256 _gas = 10000, u256 _gasPrice = 10 * eth::szabo, eth::BlockNumber _blockNumber = eth::PendingBlock, eth::FudgeFactor _ff = eth::FudgeFactor::Strict) override; dev::eth::ExecutionResult create(Address const& _secret, u256 _value, bytes const& _data = bytes(), u256 _gas = 10000, u256 _gasPrice = 10 * eth::szabo, eth::BlockNumber _blockNumber = eth::PendingBlock, eth::FudgeFactor _ff = eth::FudgeFactor::Strict) override;
using ClientBase::submitTransaction;
void submitTransaction(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice, bool _gasAuto); void submitTransaction(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice, bool _gasAuto);
Address submitTransaction(Secret _secret, u256 _endowment, bytes const& _init, u256 _gas, u256 _gasPrice, bool _gasAuto); Address submitTransaction(Secret _secret, u256 _endowment, bytes const& _init, u256 _gas, u256 _gasPrice, bool _gasAuto);
dev::eth::ExecutionResult call(Address const& _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice, eth::BlockNumber _blockNumber, bool _gasAuto, eth::FudgeFactor _ff = eth::FudgeFactor::Strict); dev::eth::ExecutionResult call(Address const& _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice, eth::BlockNumber _blockNumber, bool _gasAuto, eth::FudgeFactor _ff = eth::FudgeFactor::Strict);

6
test/TestHelper.cpp

@ -754,6 +754,10 @@ Options::Options()
checkState = true; checkState = true;
else if (arg == "--wallet") else if (arg == "--wallet")
wallet = true; wallet = true;
else if (arg == "--nonetwork")
nonetwork = true;
else if (arg == "--nodag")
nodag = true;
else if (arg == "--all") else if (arg == "--all")
{ {
performance = true; performance = true;
@ -761,7 +765,7 @@ Options::Options()
memory = true; memory = true;
inputLimits = true; inputLimits = true;
bigData = true; bigData = true;
wallet= true; wallet = true;
} }
else if (arg == "--singletest" && i + 1 < argc) else if (arg == "--singletest" && i + 1 < argc)
{ {

2
test/TestHelper.h

@ -223,6 +223,8 @@ public:
bool inputLimits = false; bool inputLimits = false;
bool bigData = false; bool bigData = false;
bool wallet = false; bool wallet = false;
bool nonetwork = false;
bool nodag = false;
/// @} /// @}
/// Get reference to options /// Get reference to options

11
test/TestUtils.cpp

@ -22,6 +22,7 @@
#include <thread> #include <thread>
#include <boost/test/unit_test.hpp> #include <boost/test/unit_test.hpp>
#include <boost/filesystem.hpp> #include <boost/filesystem.hpp>
#include <libdevcrypto/Common.h>
#include <libtestutils/Common.h> #include <libtestutils/Common.h>
#include <libtestutils/BlockChainLoader.h> #include <libtestutils/BlockChainLoader.h>
#include <libtestutils/FixedClient.h> #include <libtestutils/FixedClient.h>
@ -116,3 +117,13 @@ void ParallelClientBaseFixture::enumerateClients(std::function<void(Json::Value
}); });
}); });
} }
MoveNonceToTempDir::MoveNonceToTempDir()
{
crypto::Nonce::setSeedFilePath(m_dir.path() + "/seed");
}
MoveNonceToTempDir::~MoveNonceToTempDir()
{
crypto::Nonce::reset();
}

9
test/TestUtils.h

@ -24,6 +24,7 @@
#include <functional> #include <functional>
#include <string> #include <string>
#include <json/json.h> #include <json/json.h>
#include <libdevcore/TransientDirectory.h>
#include <libethereum/BlockChain.h> #include <libethereum/BlockChain.h>
#include <libethereum/ClientBase.h> #include <libethereum/ClientBase.h>
@ -78,5 +79,13 @@ struct JsonRpcFixture: public ClientBaseFixture
}; };
struct MoveNonceToTempDir
{
MoveNonceToTempDir();
~MoveNonceToTempDir();
private:
TransientDirectory m_dir;
};
} }
} }

138
test/libdevcrypto/SecretStore.cpp

@ -29,11 +29,16 @@
#include <libdevcore/TrieDB.h> #include <libdevcore/TrieDB.h>
#include <libdevcore/TrieHash.h> #include <libdevcore/TrieHash.h>
#include "MemTrie.h" #include "MemTrie.h"
#include "../TestHelper.h" #include <test/TestHelper.h>
#include <test/TestUtils.h>
using namespace std; using namespace std;
using namespace dev; using namespace dev;
using namespace dev::test;
namespace js = json_spirit; namespace js = json_spirit;
namespace fs = boost::filesystem;
BOOST_GLOBAL_FIXTURE( MoveNonceToTempDir )
BOOST_AUTO_TEST_SUITE(KeyStore) BOOST_AUTO_TEST_SUITE(KeyStore)
@ -52,7 +57,8 @@ BOOST_AUTO_TEST_CASE(basic_tests)
{ {
cnote << i.first; cnote << i.first;
js::mObject& o = i.second.get_obj(); js::mObject& o = i.second.get_obj();
SecretStore store("."); TransientDirectory tmpDir;
SecretStore store(tmpDir.path());
h128 u = store.readKeyContent(js::write_string(o["json"], false)); h128 u = store.readKeyContent(js::write_string(o["json"], false));
cdebug << "read uuid" << u; cdebug << "read uuid" << u;
bytes s = store.secret(u, [&](){ return o["password"].get_str(); }); bytes s = store.secret(u, [&](){ return o["password"].get_str(); });
@ -61,4 +67,132 @@ BOOST_AUTO_TEST_CASE(basic_tests)
} }
} }
BOOST_AUTO_TEST_CASE(import_key_from_file)
{
// Imports a key from an external file. Tests that the imported key is there
// and that the external file is not deleted.
TransientDirectory importDir;
string importFile = importDir.path() + "/import.json";
TransientDirectory storeDir;
string keyData = R"({
"version": 3,
"crypto": {
"ciphertext": "d69313b6470ac1942f75d72ebf8818a0d484ac78478a132ee081cd954d6bd7a9",
"cipherparams": { "iv": "ffffffffffffffffffffffffffffffff" },
"kdf": "pbkdf2",
"kdfparams": { "dklen": 32, "c": 262144, "prf": "hmac-sha256", "salt": "c82ef14476014cbf438081a42709e2ed" },
"mac": "cf6bfbcc77142a22c4a908784b4a16f1023a1d0e2aff404c20158fa4f1587177",
"cipher": "aes-128-ctr",
"version": 1
},
"id": "abb67040-8dbe-0dad-fc39-2b082ef0ee5f"
})";
string password = "bar";
string priv = "0202020202020202020202020202020202020202020202020202020202020202";
writeFile(importFile, keyData);
h128 uuid;
{
SecretStore store(storeDir.path());
BOOST_CHECK_EQUAL(store.keys().size(), 0);
uuid = store.importKey(importFile);
BOOST_CHECK(!!uuid);
BOOST_CHECK(contentsString(importFile) == keyData);
BOOST_CHECK_EQUAL(priv, toHex(store.secret(uuid, [&](){ return password; })));
BOOST_CHECK_EQUAL(store.keys().size(), 1);
}
fs::remove(importFile);
// now do it again to check whether SecretStore properly stored it on disk
{
SecretStore store(storeDir.path());
BOOST_CHECK_EQUAL(store.keys().size(), 1);
BOOST_CHECK_EQUAL(priv, toHex(store.secret(uuid, [&](){ return password; })));
}
}
BOOST_AUTO_TEST_CASE(import_secret)
{
for (string const& password: {"foobar", ""})
{
TransientDirectory storeDir;
string priv = "0202020202020202020202020202020202020202020202020202020202020202";
h128 uuid;
{
SecretStore store(storeDir.path());
BOOST_CHECK_EQUAL(store.keys().size(), 0);
uuid = store.importSecret(fromHex(priv), password);
BOOST_CHECK(!!uuid);
BOOST_CHECK_EQUAL(priv, toHex(store.secret(uuid, [&](){ return password; })));
BOOST_CHECK_EQUAL(store.keys().size(), 1);
}
{
SecretStore store(storeDir.path());
BOOST_CHECK_EQUAL(store.keys().size(), 1);
BOOST_CHECK_EQUAL(priv, toHex(store.secret(uuid, [&](){ return password; })));
}
}
}
BOOST_AUTO_TEST_CASE(wrong_password)
{
TransientDirectory storeDir;
SecretStore store(storeDir.path());
string password = "foobar";
string priv = "0202020202020202020202020202020202020202020202020202020202020202";
h128 uuid;
{
SecretStore store(storeDir.path());
BOOST_CHECK_EQUAL(store.keys().size(), 0);
uuid = store.importSecret(fromHex(priv), password);
BOOST_CHECK(!!uuid);
BOOST_CHECK_EQUAL(priv, toHex(store.secret(uuid, [&](){ return password; })));
BOOST_CHECK_EQUAL(store.keys().size(), 1);
// password will not be queried
BOOST_CHECK_EQUAL(priv, toHex(store.secret(uuid, [&](){ return "abcdefg"; })));
}
{
SecretStore store(storeDir.path());
BOOST_CHECK_EQUAL(store.keys().size(), 1);
BOOST_CHECK(store.secret(uuid, [&](){ return "abcdefg"; }).empty());
}
}
BOOST_AUTO_TEST_CASE(recode)
{
TransientDirectory storeDir;
SecretStore store(storeDir.path());
string password = "foobar";
string changedPassword = "abcdefg";
string priv = "0202020202020202020202020202020202020202020202020202020202020202";
h128 uuid;
{
SecretStore store(storeDir.path());
BOOST_CHECK_EQUAL(store.keys().size(), 0);
uuid = store.importSecret(fromHex(priv), password);
BOOST_CHECK(!!uuid);
BOOST_CHECK_EQUAL(priv, toHex(store.secret(uuid, [&](){ return password; })));
BOOST_CHECK_EQUAL(store.keys().size(), 1);
}
{
SecretStore store(storeDir.path());
BOOST_CHECK_EQUAL(store.keys().size(), 1);
BOOST_CHECK(store.secret(uuid, [&](){ return "abcdefg"; }).empty());
BOOST_CHECK(store.recode(uuid, changedPassword, [&](){ return password; }));
BOOST_CHECK_EQUAL(store.keys().size(), 1);
BOOST_CHECK_EQUAL(priv, toHex(store.secret(uuid, [&](){ return changedPassword; })));
store.clearCache();
BOOST_CHECK(store.secret(uuid, [&](){ return password; }).empty());
BOOST_CHECK_EQUAL(priv, toHex(store.secret(uuid, [&](){ return changedPassword; })));
}
{
SecretStore store(storeDir.path());
BOOST_CHECK_EQUAL(store.keys().size(), 1);
BOOST_CHECK(store.secret(uuid, [&](){ return password; }).empty());
BOOST_CHECK_EQUAL(priv, toHex(store.secret(uuid, [&](){ return changedPassword; })));
}
}
BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END()

4
test/libdevcrypto/crypto.cpp

@ -31,12 +31,16 @@
#include <libdevcore/SHA3.h> #include <libdevcore/SHA3.h>
#include <libdevcrypto/ECDHE.h> #include <libdevcrypto/ECDHE.h>
#include <libdevcrypto/CryptoPP.h> #include <libdevcrypto/CryptoPP.h>
#include <test/TestUtils.h>
using namespace std; using namespace std;
using namespace dev; using namespace dev;
using namespace dev::test;
using namespace dev::crypto; using namespace dev::crypto;
using namespace CryptoPP; using namespace CryptoPP;
BOOST_GLOBAL_FIXTURE( MoveNonceToTempDir )
BOOST_AUTO_TEST_SUITE(devcrypto) BOOST_AUTO_TEST_SUITE(devcrypto)
static Secp256k1 s_secp256k1; static Secp256k1 s_secp256k1;

6
test/libethereum/stateOriginal.cpp

@ -27,7 +27,8 @@
#include <libethereum/State.h> #include <libethereum/State.h>
#include <libethcore/Farm.h> #include <libethcore/Farm.h>
#include <libethereum/Defaults.h> #include <libethereum/Defaults.h>
#include "../TestHelper.h" #include <test/TestHelper.h>
using namespace std; using namespace std;
using namespace dev; using namespace dev;
using namespace dev::eth; using namespace dev::eth;
@ -48,6 +49,9 @@ BOOST_AUTO_TEST_CASE(Basic)
BOOST_AUTO_TEST_CASE(Complex) BOOST_AUTO_TEST_CASE(Complex)
{ {
if (test::Options::get().nodag)
return;
cnote << "Testing State..."; cnote << "Testing State...";
KeyPair me = sha3("Gav Wood"); KeyPair me = sha3("Gav Wood");

4
test/libp2p/capability.cpp

@ -27,6 +27,7 @@ along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
#include <libp2p/Session.h> #include <libp2p/Session.h>
#include <libp2p/Capability.h> #include <libp2p/Capability.h>
#include <libp2p/HostCapability.h> #include <libp2p/HostCapability.h>
#include <test/TestHelper.h>
using namespace std; using namespace std;
using namespace dev; using namespace dev;
@ -98,6 +99,9 @@ BOOST_FIXTURE_TEST_SUITE(p2pCapability, P2PFixture)
BOOST_AUTO_TEST_CASE(capability) BOOST_AUTO_TEST_CASE(capability)
{ {
if (test::Options::get().nonetwork)
return;
VerbosityHolder verbosityHolder(10); VerbosityHolder verbosityHolder(10);
cnote << "Testing Capability..."; cnote << "Testing Capability...";

26
test/libp2p/net.cpp

@ -26,6 +26,8 @@
#include <libdevcrypto/Common.h> #include <libdevcrypto/Common.h>
#include <libp2p/UDP.h> #include <libp2p/UDP.h>
#include <libp2p/NodeTable.h> #include <libp2p/NodeTable.h>
#include <test/TestHelper.h>
using namespace std; using namespace std;
using namespace dev; using namespace dev;
using namespace dev::p2p; using namespace dev::p2p;
@ -153,6 +155,9 @@ public:
BOOST_AUTO_TEST_CASE(requestTimeout) BOOST_AUTO_TEST_CASE(requestTimeout)
{ {
if (test::Options::get().nonetwork)
return;
using TimePoint = std::chrono::steady_clock::time_point; using TimePoint = std::chrono::steady_clock::time_point;
using RequestTimeout = std::pair<NodeId, TimePoint>; using RequestTimeout = std::pair<NodeId, TimePoint>;
@ -220,6 +225,9 @@ BOOST_AUTO_TEST_CASE(isIPAddressType)
BOOST_AUTO_TEST_CASE(v2PingNodePacket) BOOST_AUTO_TEST_CASE(v2PingNodePacket)
{ {
if (test::Options::get().nonetwork)
return;
// test old versino of pingNode packet w/new // test old versino of pingNode packet w/new
RLPStream s; RLPStream s;
s.appendList(3); s << "1.1.1.1" << 30303 << std::chrono::duration_cast<std::chrono::seconds>((std::chrono::system_clock::now() + chrono::seconds(60)).time_since_epoch()).count(); s.appendList(3); s << "1.1.1.1" << 30303 << std::chrono::duration_cast<std::chrono::seconds>((std::chrono::system_clock::now() + chrono::seconds(60)).time_since_epoch()).count();
@ -231,6 +239,9 @@ BOOST_AUTO_TEST_CASE(v2PingNodePacket)
BOOST_AUTO_TEST_CASE(neighboursPacketLength) BOOST_AUTO_TEST_CASE(neighboursPacketLength)
{ {
if (test::Options::get().nonetwork)
return;
KeyPair k = KeyPair::create(); KeyPair k = KeyPair::create();
std::vector<std::pair<KeyPair,unsigned>> testNodes(TestNodeTable::createTestNodes(16)); std::vector<std::pair<KeyPair,unsigned>> testNodes(TestNodeTable::createTestNodes(16));
bi::udp::endpoint to(boost::asio::ip::address::from_string("127.0.0.1"), 30000); bi::udp::endpoint to(boost::asio::ip::address::from_string("127.0.0.1"), 30000);
@ -256,6 +267,9 @@ BOOST_AUTO_TEST_CASE(neighboursPacketLength)
BOOST_AUTO_TEST_CASE(neighboursPacket) BOOST_AUTO_TEST_CASE(neighboursPacket)
{ {
if (test::Options::get().nonetwork)
return;
KeyPair k = KeyPair::create(); KeyPair k = KeyPair::create();
std::vector<std::pair<KeyPair,unsigned>> testNodes(TestNodeTable::createTestNodes(16)); std::vector<std::pair<KeyPair,unsigned>> testNodes(TestNodeTable::createTestNodes(16));
bi::udp::endpoint to(boost::asio::ip::address::from_string("127.0.0.1"), 30000); bi::udp::endpoint to(boost::asio::ip::address::from_string("127.0.0.1"), 30000);
@ -291,6 +305,9 @@ BOOST_AUTO_TEST_CASE(test_findnode_neighbours)
BOOST_AUTO_TEST_CASE(kademlia) BOOST_AUTO_TEST_CASE(kademlia)
{ {
if (test::Options::get().nonetwork)
return;
// Not yet a 'real' test. // Not yet a 'real' test.
TestNodeTableHost node(8); TestNodeTableHost node(8);
node.start(); node.start();
@ -324,6 +341,9 @@ BOOST_AUTO_TEST_CASE(kademlia)
BOOST_AUTO_TEST_CASE(udpOnce) BOOST_AUTO_TEST_CASE(udpOnce)
{ {
if (test::Options::get().nonetwork)
return;
UDPDatagram d(bi::udp::endpoint(boost::asio::ip::address::from_string("127.0.0.1"), 30300), bytes({65,65,65,65})); UDPDatagram d(bi::udp::endpoint(boost::asio::ip::address::from_string("127.0.0.1"), 30300), bytes({65,65,65,65}));
TestUDPSocket a; a.m_socket->connect(); a.start(); TestUDPSocket a; a.m_socket->connect(); a.start();
a.m_socket->send(d); a.m_socket->send(d);
@ -337,6 +357,9 @@ BOOST_AUTO_TEST_SUITE(netTypes)
BOOST_AUTO_TEST_CASE(unspecifiedNode) BOOST_AUTO_TEST_CASE(unspecifiedNode)
{ {
if (test::Options::get().nonetwork)
return;
Node n = UnspecifiedNode; Node n = UnspecifiedNode;
BOOST_REQUIRE(!n); BOOST_REQUIRE(!n);
@ -350,6 +373,9 @@ BOOST_AUTO_TEST_CASE(unspecifiedNode)
BOOST_AUTO_TEST_CASE(nodeTableReturnsUnspecifiedNode) BOOST_AUTO_TEST_CASE(nodeTableReturnsUnspecifiedNode)
{ {
if (test::Options::get().nonetwork)
return;
ba::io_service io; ba::io_service io;
NodeTable t(io, KeyPair::create(), NodeIPEndpoint(bi::address::from_string("127.0.0.1"), 30303, 30303)); NodeTable t(io, KeyPair::create(), NodeIPEndpoint(bi::address::from_string("127.0.0.1"), 30303, 30303));
if (Node n = t.node(NodeId())) if (Node n = t.node(NodeId()))

17
test/libp2p/peer.cpp

@ -24,6 +24,8 @@
#include <chrono> #include <chrono>
#include <thread> #include <thread>
#include <libp2p/Host.h> #include <libp2p/Host.h>
#include <test/TestHelper.h>
using namespace std; using namespace std;
using namespace dev; using namespace dev;
using namespace dev::p2p; using namespace dev::p2p;
@ -38,6 +40,9 @@ BOOST_FIXTURE_TEST_SUITE(p2p, P2PFixture)
BOOST_AUTO_TEST_CASE(host) BOOST_AUTO_TEST_CASE(host)
{ {
if (test::Options::get().nonetwork)
return;
VerbosityHolder sentinel(10); VerbosityHolder sentinel(10);
NetworkPreferences host1prefs("127.0.0.1", 30301, false); NetworkPreferences host1prefs("127.0.0.1", 30301, false);
@ -64,6 +69,9 @@ BOOST_AUTO_TEST_CASE(host)
BOOST_AUTO_TEST_CASE(networkConfig) BOOST_AUTO_TEST_CASE(networkConfig)
{ {
if (test::Options::get().nonetwork)
return;
Host save("Test", NetworkPreferences(false)); Host save("Test", NetworkPreferences(false));
bytes store(save.saveNetwork()); bytes store(save.saveNetwork());
@ -73,6 +81,9 @@ BOOST_AUTO_TEST_CASE(networkConfig)
BOOST_AUTO_TEST_CASE(saveNodes) BOOST_AUTO_TEST_CASE(saveNodes)
{ {
if (test::Options::get().nonetwork)
return;
VerbosityHolder reduceVerbosity(2); VerbosityHolder reduceVerbosity(2);
std::list<Host*> hosts; std::list<Host*> hosts;
@ -134,6 +145,9 @@ BOOST_FIXTURE_TEST_SUITE(p2pPeer, P2PFixture)
BOOST_AUTO_TEST_CASE(requirePeer) BOOST_AUTO_TEST_CASE(requirePeer)
{ {
if (test::Options::get().nonetwork)
return;
VerbosityHolder reduceVerbosity(10); VerbosityHolder reduceVerbosity(10);
const char* const localhost = "127.0.0.1"; const char* const localhost = "127.0.0.1";
@ -186,6 +200,9 @@ BOOST_AUTO_TEST_SUITE(peerTypes)
BOOST_AUTO_TEST_CASE(emptySharedPeer) BOOST_AUTO_TEST_CASE(emptySharedPeer)
{ {
if (test::Options::get().nonetwork)
return;
shared_ptr<Peer> p; shared_ptr<Peer> p;
BOOST_REQUIRE(!p); BOOST_REQUIRE(!p);

88
test/libsolidity/SolidityEndToEndTest.cpp

@ -4243,9 +4243,9 @@ BOOST_AUTO_TEST_CASE(return_string)
function get1() returns (string r) { function get1() returns (string r) {
return s; return s;
} }
// function get2() returns (string r) { function get2() returns (string r) {
// r = s; r = s;
// } }
} }
)"; )";
compileAndRun(sourceCode, 0, "Main"); compileAndRun(sourceCode, 0, "Main");
@ -4253,8 +4253,86 @@ BOOST_AUTO_TEST_CASE(return_string)
bytes args = encodeArgs(u256(0x20), u256(s.length()), s); bytes args = encodeArgs(u256(0x20), u256(s.length()), s);
BOOST_REQUIRE(callContractFunction("set(string)", asString(args)) == encodeArgs()); BOOST_REQUIRE(callContractFunction("set(string)", asString(args)) == encodeArgs());
BOOST_CHECK(callContractFunction("get1()") == args); BOOST_CHECK(callContractFunction("get1()") == args);
// BOOST_CHECK(callContractFunction("get2()") == args); BOOST_CHECK(callContractFunction("get2()") == args);
// BOOST_CHECK(callContractFunction("s()") == args); BOOST_CHECK(callContractFunction("s()") == args);
}
BOOST_AUTO_TEST_CASE(return_multiple_strings_of_various_sizes)
{
char const* sourceCode = R"(
contract Main {
string public s1;
string public s2;
function set(string _s1, uint x, string _s2) external returns (uint) {
s1 = _s1;
s2 = _s2;
return x;
}
function get() returns (string r1, string r2) {
r1 = s1;
r2 = s2;
}
}
)";
compileAndRun(sourceCode, 0, "Main");
string s1(
"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz"
"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz"
"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz"
"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz"
);
string s2(
"ABCDEFGHIJKLMNOPQRSTUVXYZABCDEFGHIJKLMNOPQRSTUVXYZABCDEFGHIJKLMNOPQRSTUVXYZ"
"ABCDEFGHIJKLMNOPQRSTUVXYZABCDEFGHIJKLMNOPQRSTUVXYZABCDEFGHIJKLMNOPQRSTUVXYZ"
"ABCDEFGHIJKLMNOPQRSTUVXYZABCDEFGHIJKLMNOPQRSTUVXYZABCDEFGHIJKLMNOPQRSTUVXYZ"
"ABCDEFGHIJKLMNOPQRSTUVXYZABCDEFGHIJKLMNOPQRSTUVXYZABCDEFGHIJKLMNOPQRSTUVXYZ"
"ABCDEFGHIJKLMNOPQRSTUVXYZABCDEFGHIJKLMNOPQRSTUVXYZABCDEFGHIJKLMNOPQRSTUVXYZ"
);
vector<size_t> lengthes{0, 30, 32, 63, 64, 65, 210, 300};
for (auto l1: lengthes)
for (auto l2: lengthes)
{
bytes dyn1 = encodeArgs(u256(l1), s1.substr(0, l1));
bytes dyn2 = encodeArgs(u256(l2), s2.substr(0, l2));
bytes args = encodeArgs(u256(0x60), u256(l1), u256(0x60 + dyn1.size())) + dyn1 + dyn2;
BOOST_REQUIRE(
callContractFunction("set(string,uint256,string)", asString(args)) ==
encodeArgs(u256(l1))
);
bytes result = encodeArgs(u256(0x40), u256(0x40 + dyn1.size())) + dyn1 + dyn2;
BOOST_CHECK(callContractFunction("get()") == result);
BOOST_CHECK(callContractFunction("s1()") == encodeArgs(0x20) + dyn1);
BOOST_CHECK(callContractFunction("s2()") == encodeArgs(0x20) + dyn2);
}
}
BOOST_AUTO_TEST_CASE(accessor_involving_strings)
{
char const* sourceCode = R"(
contract Main {
struct stringData { string a; uint b; string c; }
mapping(uint => stringData[]) public data;
function set(uint x, uint y, string a, uint b, string c) external returns (bool) {
data[x].length = y + 1;
data[x][y].a = a;
data[x][y].b = b;
data[x][y].c = c;
return true;
}
}
)";
compileAndRun(sourceCode, 0, "Main");
string s1("abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz");
string s2("ABCDEFGHIJKLMNOPQRSTUVXYZABCDEFGHIJKLMNOPQRSTUVXYZABCDEFGHIJKLMNOPQRSTUVXYZ");
bytes s1Data = encodeArgs(u256(s1.length()), s1);
bytes s2Data = encodeArgs(u256(s2.length()), s2);
u256 b = 765;
u256 x = 7;
u256 y = 123;
bytes args = encodeArgs(x, y, u256(0xa0), b, u256(0xa0 + s1Data.size()), s1Data, s2Data);
bytes result = encodeArgs(u256(0x60), b, u256(0x60 + s1Data.size()), s1Data, s2Data);
BOOST_REQUIRE(callContractFunction("set(uint256,uint256,string,uint256,string)", asString(args)) == encodeArgs(true));
BOOST_REQUIRE(callContractFunction("data(uint256,uint256)", x, y) == result);
} }
BOOST_AUTO_TEST_CASE(storage_array_ref) BOOST_AUTO_TEST_CASE(storage_array_ref)

14
test/libsolidity/SolidityTypes.cpp

@ -77,13 +77,13 @@ BOOST_AUTO_TEST_CASE(storage_layout_mapping)
BOOST_AUTO_TEST_CASE(storage_layout_arrays) BOOST_AUTO_TEST_CASE(storage_layout_arrays)
{ {
BOOST_CHECK(ArrayType(ReferenceType::Location::Storage, make_shared<FixedBytesType>(1), 32).getStorageSize() == 1); BOOST_CHECK(ArrayType(DataLocation::Storage, make_shared<FixedBytesType>(1), 32).getStorageSize() == 1);
BOOST_CHECK(ArrayType(ReferenceType::Location::Storage, make_shared<FixedBytesType>(1), 33).getStorageSize() == 2); BOOST_CHECK(ArrayType(DataLocation::Storage, make_shared<FixedBytesType>(1), 33).getStorageSize() == 2);
BOOST_CHECK(ArrayType(ReferenceType::Location::Storage, make_shared<FixedBytesType>(2), 31).getStorageSize() == 2); BOOST_CHECK(ArrayType(DataLocation::Storage, make_shared<FixedBytesType>(2), 31).getStorageSize() == 2);
BOOST_CHECK(ArrayType(ReferenceType::Location::Storage, make_shared<FixedBytesType>(7), 8).getStorageSize() == 2); BOOST_CHECK(ArrayType(DataLocation::Storage, make_shared<FixedBytesType>(7), 8).getStorageSize() == 2);
BOOST_CHECK(ArrayType(ReferenceType::Location::Storage, make_shared<FixedBytesType>(7), 9).getStorageSize() == 3); BOOST_CHECK(ArrayType(DataLocation::Storage, make_shared<FixedBytesType>(7), 9).getStorageSize() == 3);
BOOST_CHECK(ArrayType(ReferenceType::Location::Storage, make_shared<FixedBytesType>(31), 9).getStorageSize() == 9); BOOST_CHECK(ArrayType(DataLocation::Storage, make_shared<FixedBytesType>(31), 9).getStorageSize() == 9);
BOOST_CHECK(ArrayType(ReferenceType::Location::Storage, make_shared<FixedBytesType>(32), 9).getStorageSize() == 9); BOOST_CHECK(ArrayType(DataLocation::Storage, make_shared<FixedBytesType>(32), 9).getStorageSize() == 9);
} }
BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END()

11
test/libwhisper/whisperTopic.cpp

@ -25,6 +25,8 @@
#include <libp2p/Host.h> #include <libp2p/Host.h>
#include <libwhisper/WhisperPeer.h> #include <libwhisper/WhisperPeer.h>
#include <libwhisper/WhisperHost.h> #include <libwhisper/WhisperHost.h>
#include <test/TestHelper.h>
using namespace std; using namespace std;
using namespace dev; using namespace dev;
using namespace dev::p2p; using namespace dev::p2p;
@ -40,6 +42,9 @@ BOOST_FIXTURE_TEST_SUITE(whisper, P2PFixture)
BOOST_AUTO_TEST_CASE(topic) BOOST_AUTO_TEST_CASE(topic)
{ {
if (test::Options::get().nonetwork)
return;
cnote << "Testing Whisper..."; cnote << "Testing Whisper...";
VerbosityHolder setTemporaryLevel(0); VerbosityHolder setTemporaryLevel(0);
@ -103,6 +108,9 @@ BOOST_AUTO_TEST_CASE(topic)
BOOST_AUTO_TEST_CASE(forwarding) BOOST_AUTO_TEST_CASE(forwarding)
{ {
if (test::Options::get().nonetwork)
return;
cnote << "Testing Whisper forwarding..."; cnote << "Testing Whisper forwarding...";
VerbosityHolder setTemporaryLevel(0); VerbosityHolder setTemporaryLevel(0);
@ -203,6 +211,9 @@ BOOST_AUTO_TEST_CASE(forwarding)
BOOST_AUTO_TEST_CASE(asyncforwarding) BOOST_AUTO_TEST_CASE(asyncforwarding)
{ {
if (test::Options::get().nonetwork)
return;
cnote << "Testing Whisper async forwarding..."; cnote << "Testing Whisper async forwarding...";
VerbosityHolder setTemporaryLevel(2); VerbosityHolder setTemporaryLevel(2);

Loading…
Cancel
Save