Browse Source

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

cl-refactor
arkpar 9 years ago
parent
commit
1a3163f670
  1. 2
      alethzero/MainWin.cpp
  2. 21
      libdevcore/CommonIO.cpp
  3. 9
      libdevcore/CommonIO.h
  4. 111
      libdevcrypto/Common.cpp
  5. 27
      libdevcrypto/Common.h
  6. 35
      libdevcrypto/Exceptions.h
  7. 120
      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. 38
      libsolidity/Compiler.cpp
  14. 62
      libsolidity/CompilerUtils.cpp
  15. 5
      libsolidity/CompilerUtils.h
  16. 51
      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();
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.endpoint.address.to_string()))
.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 <cstdlib>
#include <fstream>
#include "Exceptions.h"
#include <stdio.h>
#ifdef _WIN32
#include <windows.h>
#else
#include <termios.h>
#endif
#include <boost/filesystem.hpp>
#include "Exceptions.h"
using namespace std;
using namespace dev;
@ -95,9 +96,23 @@ string dev::contentsString(string const& _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)

9
libdevcore/CommonIO.h

@ -56,10 +56,13 @@ std::string contentsString(std::string const& _file);
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.
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.
inline void writeFile(std::string const& _file, bytes const& _data) { writeFile(_file, bytesConstRef(&_data)); }
inline void writeFile(std::string const& _file, std::string 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, bool _writeDeleteRename = false) { writeFile(_file, bytesConstRef(_data), _writeDeleteRename); }
/// 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.

111
libdevcrypto/Common.cpp

@ -31,6 +31,7 @@
#include <libdevcore/FileSystem.h>
#include "AES.h"
#include "CryptoPP.h"
#include "Exceptions.h"
using namespace std;
using namespace dev;
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 ret(_dkLen);
PKCS5_PBKDF2_HMAC<SHA256> pbkdf;
pbkdf.DeriveKey(ret.data(), ret.size(), 0, (byte*)_pass.data(), _pass.size(), _salt.data(), _salt.size(), _iterations);
if (PKCS5_PBKDF2_HMAC<SHA256>().DeriveKey(
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;
}
bytes dev::scrypt(std::string const& _pass, bytes const& _salt, uint64_t _n, uint32_t _r, uint32_t _p, unsigned _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;
}
@ -233,42 +254,84 @@ h256 crypto::kdf(Secret const& _priv, h256 const& _hash)
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: encrypt
static h256 s_seed;
static string s_seedFile(getDataDir() + "/seed");
static mutex s_x;
Guard l(s_x);
if (!s_seed)
{
static Nonce s_nonce;
bytes b = contents(s_seedFile);
Guard l(Nonce::s_x);
return Nonce::singleton().next();
}
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(s_seed.data(), b.data(), 32);
memcpy(m_value.data(), b.data(), 32);
else
{
// 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);
m_value[i] = byte(d(s_eng));
}
if (!s_seed)
if (!m_value)
BOOST_THROW_EXCEPTION(InvalidState());
// prevent seed reuse if process terminates abnormally
writeFile(s_seedFile, bytes());
}
h256 prev(s_seed);
sha3(prev.ref(), s_seed.ref());
if (_commit)
writeFile(s_seedFile, s_seed.asBytes());
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
#include <mutex>
#include <libdevcore/Common.h>
#include <libdevcore/FixedHash.h>
#include <libdevcore/Exceptions.h>
@ -180,14 +181,36 @@ struct InvalidState: public dev::Exception {};
h256 kdf(Secret const& _priv, h256 const& _hash);
/**
* @brief Generator for nonce material
* @brief Generator for nonce material.
*/
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:
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);
}
}

120
libdevcrypto/SecretStore.cpp

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

50
libdevcrypto/SecretStore.h

@ -35,41 +35,81 @@ enum class KDF {
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
{
public:
/// Construct a new SecretStore and read all keys in the given directory.
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;
/// 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; }
/// 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; }
/// 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);
/// 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);
/// Removes the key specified by @a _uuid from both memory and disk.
void kill(h128 const& _uuid);
/// Returns the uuids of all stored 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;
// Doesn't save().
h128 readKey(std::string const& _file, bool _deleteFile);
/// Import the key from the file @a _file, but do not copy it to the managed directory yet.
/// @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());
/// Store all keys in the directory @a _keysPath.
void save(std::string const& _keysPath);
/// Store all keys in the managed directory.
void save() { save(m_path); }
/// @returns the default path for the managed directory.
static std::string defaultPath() { return getDataDir("web3") + "/keys"; }
private:
struct EncryptedKey
{
std::string encryptedKey;
std::string filename;
};
/// Loads all keys in the given directory.
void load(std::string const& _keysPath);
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);
/// 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);
/// Stores decrypted keys by uuid.
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;
};

2
libethereum/BlockChain.cpp

@ -1122,6 +1122,6 @@ VerifiedBlockRef BlockChain::verifyBlock(bytes const& _block, function<void(Exce
++i;
}
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; });
/// 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 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.
bool ensureInitialised();
virtual void onStarting() { startWorking(); }
virtual void onStopping() { stopWorking(); }
virtual void onStarting() override { startWorking(); }
virtual void onStopping() override { stopWorking(); }
BlockChainSync& sync();

10
libsolidity/AST.cpp

@ -919,7 +919,7 @@ void MemberAccess::checkTypeRequirements(TypePointers const* _argumentTypes)
{
auto const& arrayType(dynamic_cast<ArrayType const&>(type));
m_isLValue = (*m_memberName == "length" &&
arrayType.location() != ReferenceType::Location::CallData && arrayType.isDynamicallySized());
arrayType.location() != DataLocation::CallData && arrayType.isDynamicallySized());
}
else
m_isLValue = false;
@ -942,7 +942,7 @@ void IndexAccess::checkTypeRequirements(TypePointers const*)
m_type = make_shared<FixedBytesType>(1);
else
m_type = type.getBaseType();
m_isLValue = type.location() != ReferenceType::Location::CallData;
m_isLValue = type.location() != DataLocation::CallData;
break;
}
case Type::Category::Mapping:
@ -959,7 +959,7 @@ void IndexAccess::checkTypeRequirements(TypePointers const*)
{
TypeType const& type = dynamic_cast<TypeType const&>(*m_base->getType());
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
{
m_index->checkTypeRequirements(nullptr);
@ -967,7 +967,9 @@ void IndexAccess::checkTypeRequirements(TypePointers const*)
if (!length)
BOOST_THROW_EXCEPTION(m_index->createTypeError("Integer constant expected."));
m_type = make_shared<TypeType>(make_shared<ArrayType>(
ReferenceType::Location::Memory, type.getActualType(), length->literalValue(nullptr)));
DataLocation::Memory, type.getActualType(),
length->literalValue(nullptr)
));
}
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
// 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(
_sourceType.location() == ReferenceType::Location::CallData ||
_sourceType.location() == ReferenceType::Location::Storage,
_sourceType.location() == DataLocation::CallData ||
_sourceType.location() == DataLocation::Storage,
"Given array location not implemented."
);
@ -51,7 +51,7 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons
// 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 haveByteOffsetSource = !directCopy && sourceIsStorage && sourceBaseType->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;
// stack: target_ref source_ref [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
// stack: target_ref source_ref source_length
m_context << eth::Instruction::DUP3;
@ -82,7 +82,7 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons
if (sourceBaseType->getCategory() == Type::Category::Mapping)
{
solAssert(targetBaseType->getCategory() == Type::Category::Mapping, "");
solAssert(_sourceType.location() == ReferenceType::Location::Storage, "");
solAssert(_sourceType.location() == DataLocation::Storage, "");
// nothing to copy
m_context
<< eth::Instruction::POP << eth::Instruction::POP
@ -106,7 +106,7 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons
eth::AssemblyItem copyLoopEndWithoutByteOffset = m_context.newTag();
m_context.appendConditionalJumpTo(copyLoopEndWithoutByteOffset);
if (_sourceType.location() == ReferenceType::Location::Storage && _sourceType.isDynamicallySized())
if (_sourceType.location() == DataLocation::Storage && _sourceType.isDynamicallySized())
CompilerUtils(m_context).computeHashStatic();
// stack: target_ref target_data_end source_length target_data_pos source_data_pos
m_context << eth::Instruction::SWAP2;
@ -155,7 +155,7 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons
// checking is easier.
// 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);
if (_sourceType.location() == ReferenceType::Location::Storage)
if (_sourceType.location() == DataLocation::Storage)
{
if (haveByteOffsetSource)
m_context << eth::Instruction::DUP2;
@ -231,7 +231,7 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons
void ArrayUtils::clearArray(ArrayType const& _type) const
{
unsigned stackHeightStart = m_context.getStackHeight();
solAssert(_type.location() == ReferenceType::Location::Storage, "");
solAssert(_type.location() == DataLocation::Storage, "");
if (_type.getBaseType()->getStorageBytes() < 32)
{
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
{
solAssert(_type.location() == ReferenceType::Location::Storage, "");
solAssert(_type.location() == DataLocation::Storage, "");
solAssert(_type.isDynamicallySized(), "");
unsigned stackHeightStart = m_context.getStackHeight();
@ -314,7 +314,7 @@ void ArrayUtils::clearDynamicArray(ArrayType const& _type) const
void ArrayUtils::resizeDynamicArray(const ArrayType& _type) const
{
solAssert(_type.location() == ReferenceType::Location::Storage, "");
solAssert(_type.location() == DataLocation::Storage, "");
solAssert(_type.isDynamicallySized(), "");
if (!_type.isByteArray() && _type.getBaseType()->getStorageBytes() < 32)
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
{
if (_arrayType.location() == ReferenceType::Location::Storage)
if (_arrayType.location() == DataLocation::Storage)
{
if (_arrayType.getBaseType()->getStorageSize() <= 1)
{
@ -437,13 +437,13 @@ void ArrayUtils::retrieveLength(ArrayType const& _arrayType) const
m_context << eth::Instruction::DUP1;
switch (_arrayType.location())
{
case ReferenceType::Location::CallData:
case DataLocation::CallData:
// length is stored on the stack
break;
case ReferenceType::Location::Memory:
case DataLocation::Memory:
m_context << eth::Instruction::MLOAD;
break;
case ReferenceType::Location::Storage:
case DataLocation::Storage:
m_context << eth::Instruction::SLOAD;
break;
}
@ -452,16 +452,16 @@ void ArrayUtils::retrieveLength(ArrayType const& _arrayType) const
void ArrayUtils::accessIndex(ArrayType const& _arrayType) const
{
ReferenceType::Location location = _arrayType.location();
DataLocation location = _arrayType.location();
eth::Instruction load =
location == ReferenceType::Location::Storage ? eth::Instruction::SLOAD :
location == ReferenceType::Location::Memory ? eth::Instruction::MLOAD :
location == DataLocation::Storage ? eth::Instruction::SLOAD :
location == DataLocation::Memory ? eth::Instruction::MLOAD :
eth::Instruction::CALLDATALOAD;
// retrieve length
if (!_arrayType.isDynamicallySized())
m_context << _arrayType.getLength();
else if (location == ReferenceType::Location::CallData)
else if (location == DataLocation::CallData)
// length is stored on the stack
m_context << eth::Instruction::SWAP1;
else
@ -476,20 +476,20 @@ void ArrayUtils::accessIndex(ArrayType const& _arrayType) const
m_context << eth::Instruction::SWAP1;
if (_arrayType.isDynamicallySized())
{
if (location == ReferenceType::Location::Storage)
if (location == DataLocation::Storage)
CompilerUtils(m_context).computeHashStatic();
else if (location == ReferenceType::Location::Memory)
else if (location == DataLocation::Memory)
m_context << u256(32) << eth::Instruction::ADD;
}
// stack: <index> <data_ref>
switch (location)
{
case ReferenceType::Location::CallData:
case DataLocation::CallData:
if (!_arrayType.isByteArray())
m_context
<< eth::Instruction::SWAP1
<< _arrayType.getBaseType()->getCalldataEncodedSize()
<< eth::Instruction::MUL;
{
m_context << eth::Instruction::SWAP1;
m_context << _arrayType.getBaseType()->getCalldataEncodedSize() << eth::Instruction::MUL;
}
m_context << eth::Instruction::ADD;
if (_arrayType.getBaseType()->isValueType())
CompilerUtils(m_context).loadFromMemoryDynamic(
@ -499,7 +499,7 @@ void ArrayUtils::accessIndex(ArrayType const& _arrayType) const
false
);
break;
case ReferenceType::Location::Storage:
case DataLocation::Storage:
m_context << eth::Instruction::SWAP1;
if (_arrayType.getBaseType()->getStorageBytes() <= 16)
{
@ -527,7 +527,7 @@ void ArrayUtils::accessIndex(ArrayType const& _arrayType) const
m_context << eth::Instruction::ADD << u256(0);
}
break;
case ReferenceType::Location::Memory:
case DataLocation::Memory:
solAssert(false, "Memory lvalues not yet implemented.");
}
}

38
libsolidity/Compiler.cpp

@ -245,21 +245,35 @@ void Compiler::appendCalldataUnpacker(
{
// We do not check the calldata size, everything is zero-paddedd
//@todo this does not yet support nested arrays
if (_startOffset == u256(-1))
_startOffset = u256(CompilerUtils::dataStartOffset);
m_context << _startOffset;
for (TypePointer const& type: _typeParameters)
{
// stack: v1 v2 ... v(k-1) mem_offset
switch (type->getCategory())
{
case Type::Category::Array:
{
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, "");
if (type->isDynamicallySized())
// first load from calldata and potentially convert to memory if arrayType is memory
TypePointer calldataType = arrayType.copyForLocation(DataLocation::CallData, false);
if (calldataType->isDynamicallySized())
{
// put on stack: data_pointer length
CompilerUtils(m_context).loadFromMemoryDynamic(IntegerType(256), !_fromMemory);
@ -276,17 +290,17 @@ void Compiler::appendCalldataUnpacker(
{
// leave the pointer on the stack
m_context << eth::Instruction::DUP1;
m_context << u256(type->getCalldataEncodedSize()) << eth::Instruction::ADD;
}
m_context << u256(calldataType->getCalldataEncodedSize()) << eth::Instruction::ADD;
}
else
if (arrayType.location() == DataLocation::Memory)
{
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;
// 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());
}
}
break;
}

62
libsolidity/CompilerUtils.cpp

@ -107,8 +107,10 @@ void CompilerUtils::storeInMemoryDynamic(Type const& _type, bool _padToWordBound
auto const& type = dynamic_cast<ArrayType const&>(_type);
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
m_context << eth::Instruction::DUP1 << eth::Instruction::DUP3 << eth::Instruction::DUP5;
// stack: target source_offset source_len source_len source_offset target
@ -116,7 +118,7 @@ void CompilerUtils::storeInMemoryDynamic(Type const& _type, bool _padToWordBound
m_context << eth::Instruction::DUP3 << eth::Instruction::ADD;
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
ArrayUtils(m_context).retrieveLength(type);
@ -183,7 +185,7 @@ void CompilerUtils::storeInMemoryDynamic(Type const& _type, bool _padToWordBound
}
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::DUP1 << eth::Instruction::SLOAD;
// stack here: memory_offset storage_offset length_bytes
@ -274,10 +276,16 @@ void CompilerUtils::encodeToMemory(
else
{
copyToStackTop(argSize - stackPos + dynPointers + 2, _givenTypes[i]->getSizeOnStack());
if (targetType->isValueType())
convertType(*_givenTypes[i], *targetType, true);
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();
}
@ -304,13 +312,13 @@ void CompilerUtils::encodeToMemory(
// stack: ... <end_of_mem> <value...>
// copy length to memory
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
else if (arrayType.location() == ReferenceType::Location::Storage)
else if (arrayType.location() == DataLocation::Storage)
m_context << eth::Instruction::DUP3 << eth::Instruction::SLOAD;
else
{
solAssert(arrayType.location() == ReferenceType::Location::Memory, "");
solAssert(arrayType.location() == DataLocation::Memory, "");
m_context << eth::Instruction::DUP2 << eth::Instruction::MLOAD;
}
// 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);
switch (targetType.location())
{
case ReferenceType::Location::Storage:
case DataLocation::Storage:
// Other cases are done explicitly in LValue::storeValue, and only possible by assignment.
solAssert(
targetType.isPointer() &&
typeOnStack.location() == ReferenceType::Location::Storage,
typeOnStack.location() == DataLocation::Storage,
"Invalid conversion to storage type."
);
break;
case ReferenceType::Location::Memory:
case DataLocation::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)
unsigned stackSize = typeOnStack.getSizeOnStack();
@ -452,7 +460,7 @@ void CompilerUtils::convertType(Type const& _typeOnStack, Type const& _targetTyp
// stack: <mem start> <source ref> (variably sized)
if (targetType.isDynamicallySized())
{
bool fromStorage = (typeOnStack.location() == ReferenceType::Location::Storage);
bool fromStorage = (typeOnStack.location() == DataLocation::Storage);
// store length
if (fromStorage)
{
@ -483,11 +491,25 @@ void CompilerUtils::convertType(Type const& _typeOnStack, Type const& _targetTyp
// Stack <mem start> <mem end>
storeFreeMemoryPointer();
}
else if (typeOnStack.location() == ReferenceType::Location::CallData)
else if (typeOnStack.location() == DataLocation::CallData)
{
// Stack: <offset> [<length>]
// length is present if dynamically sized
fetchFreeMemoryPointer();
moveIntoStack(typeOnStack.getSizeOnStack());
// stack: memptr calldataoffset [<length>]
if (typeOnStack.isDynamicallySized())
{
// Stack: <offset> <length>
//@todo
solAssert(false, "Not yet implemented.");
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
break;
@ -504,8 +526,8 @@ void CompilerUtils::convertType(Type const& _typeOnStack, Type const& _targetTyp
auto& targetType = dynamic_cast<StructType const&>(_targetType);
auto& stackType = dynamic_cast<StructType const&>(_typeOnStack);
solAssert(
targetType.location() == ReferenceType::Location::Storage &&
stackType.location() == ReferenceType::Location::Storage,
targetType.location() == DataLocation::Storage &&
stackType.location() == DataLocation::Storage,
"Non-storage structs not yet implemented."
);
solAssert(

5
libsolidity/CompilerUtils.h

@ -99,8 +99,9 @@ public:
bool _copyDynamicDataInPlace = false
);
/// Appends code for an implicit or explicit type conversion. For now this comprises only erasing
/// higher-order bits (@see appendHighBitCleanup) when widening integer.
/// Appends code for an implicit or explicit type conversion. This includes erasing higher
/// 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
/// necessary.
void convertType(Type const& _typeOnStack, Type const& _targetType, bool _cleanupNeeded = false);

51
libsolidity/ExpressionCompiler.cpp

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

62
libsolidity/Types.cpp

@ -144,12 +144,13 @@ TypePointer Type::fromElementaryTypeName(Token::Value _typeToken)
else if (_typeToken == Token::Bool)
return make_shared<BoolType>();
else if (_typeToken == Token::Bytes)
return make_shared<ArrayType>(ReferenceType::Location::Storage);
return make_shared<ArrayType>(DataLocation::Storage);
else if (_typeToken == Token::String)
return make_shared<ArrayType>(ReferenceType::Location::Storage, true);
return make_shared<ArrayType>(DataLocation::Storage, true);
else
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unable to convert elementary typename " +
std::string(Token::toString(_typeToken)) + " to type."));
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment(
"Unable to convert elementary typename " + std::string(Token::toString(_typeToken)) + " to type."
));
}
TypePointer Type::fromElementaryTypeName(string const& _name)
@ -180,7 +181,7 @@ TypePointer Type::fromMapping(ElementaryTypeName& _keyType, TypeName& _valueType
if (!valueType)
BOOST_THROW_EXCEPTION(_valueType.createTypeError("Invalid type name."));
// 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);
}
@ -198,10 +199,10 @@ TypePointer Type::fromArrayTypeName(TypeName& _baseTypeName, Expression* _length
auto const* length = dynamic_cast<IntegerConstantType const*>(_length->getType().get());
if (!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
return make_shared<ArrayType>(ReferenceType::Location::Storage, baseType);
return make_shared<ArrayType>(DataLocation::Storage, baseType);
}
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();
}
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()))
return type->copyForLocation(_location, false);
@ -686,11 +687,11 @@ string ReferenceType::stringForReferencePart() const
{
switch (m_location)
{
case Location::Storage:
case DataLocation::Storage:
return string("storage ") + (m_isPointer ? "pointer" : "ref");
case Location::CallData:
case DataLocation::CallData:
return "calldata";
case Location::Memory:
case DataLocation::Memory:
return "memory";
}
solAssert(false, "");
@ -705,11 +706,11 @@ bool ArrayType::isImplicitlyConvertibleTo(const Type& _convertTo) const
if (convertTo.isByteArray() != isByteArray() || convertTo.isString() != isString())
return false;
// 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;
if (convertTo.location() == Location::CallData && location() != convertTo.location())
if (convertTo.location() == DataLocation::CallData && location() != convertTo.location())
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.
if (!getBaseType()->isImplicitlyConvertibleTo(*convertTo.getBaseType()))
@ -788,10 +789,10 @@ u256 ArrayType::getStorageSize() const
unsigned ArrayType::getSizeOnStack() const
{
if (m_location == Location::CallData)
if (m_location == DataLocation::CallData)
// offset [length] (stack top)
return 1 + (isDynamicallySized() ? 1 : 0);
else if (m_location == Location::Storage)
else if (m_location == DataLocation::Storage)
// storage_key storage_offset
return 2;
else
@ -828,12 +829,12 @@ TypePointer ArrayType::externalType() const
return TypePointer();
if (isDynamicallySized())
return std::make_shared<ArrayType>(Location::CallData, m_baseType->externalType());
return std::make_shared<ArrayType>(DataLocation::CallData, m_baseType->externalType());
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);
copy->m_isPointer = _isPointer;
@ -949,9 +950,9 @@ bool StructType::isImplicitlyConvertibleTo(const Type& _convertTo) const
return false;
auto& convertTo = dynamic_cast<StructType const&>(_convertTo);
// 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;
if (convertTo.location() == Location::CallData && location() != convertTo.location())
if (convertTo.location() == DataLocation::CallData && location() != convertTo.location())
return false;
return this->m_struct == convertTo.m_struct;
}
@ -1009,7 +1010,7 @@ MemberList const& StructType::getMembers() const
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);
copy->m_location = _location;
@ -1115,6 +1116,9 @@ FunctionType::FunctionType(VariableDeclaration const& _varDecl):
}
else if (auto arrayType = dynamic_cast<ArrayType const*>(returnType.get()))
{
if (arrayType->isByteArray())
// Return byte arrays as as whole.
break;
returnType = arrayType->getBaseType();
paramNames.push_back("");
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()))
{
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);
retParamNames.push_back(member.name);
}
}
else
{
retParams.push_back(returnType);
retParams.push_back(ReferenceType::copyForLocationIfReference(
DataLocation::Memory,
returnType
));
retParamNames.push_back("");
}
@ -1549,7 +1559,7 @@ MagicType::MagicType(MagicType::Kind _kind):
{"sender", make_shared<IntegerType>(0, IntegerType::Modifier::Address)},
{"gas", 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)}
});
break;

32
libsolidity/Types.h

@ -44,6 +44,8 @@ using FunctionTypePointer = std::shared_ptr<FunctionType const>;
using TypePointers = std::vector<TypePointer>;
enum class DataLocation { Storage, CallData, Memory };
/**
* 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
/// for storage reference types.
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.
virtual MemberList const& getMembers() const { return EmptyMemberList; }
@ -365,15 +370,15 @@ public:
class ReferenceType: public Type
{
public:
enum class Location { Storage, CallData, Memory };
explicit ReferenceType(Location _location): m_location(_location) {}
Location location() const { return m_location; }
explicit ReferenceType(DataLocation _location): m_location(_location) {}
DataLocation location() const { return m_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.
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 bool dataStoredIn(DataLocation _location) const override { return m_location == _location; }
/// 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
@ -389,14 +394,14 @@ public:
/// @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.
/// 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:
TypePointer copyForLocationIfReference(TypePointer const& _type) const;
/// @returns a human-readable description of the reference part of the type.
std::string stringForReferencePart() const;
Location m_location = Location::Storage;
DataLocation m_location = DataLocation::Storage;
bool m_isPointer = true;
};
@ -413,20 +418,20 @@ public:
virtual Category getCategory() const override { return Category::Array; }
/// Constructor for a byte array ("bytes") and string.
explicit ArrayType(Location _location, bool _isString = false):
explicit ArrayType(DataLocation _location, bool _isString = false):
ReferenceType(_location),
m_arrayKind(_isString ? ArrayKind::String : ArrayKind::Bytes),
m_baseType(std::make_shared<FixedBytesType>(1))
{
}
/// Constructor for a dynamically sized array type ("type[]")
ArrayType(Location _location, TypePointer const& _baseType):
ArrayType(DataLocation _location, TypePointer const& _baseType):
ReferenceType(_location),
m_baseType(copyForLocationIfReference(_baseType))
{
}
/// 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),
m_baseType(copyForLocationIfReference(_baseType)),
m_hasDynamicLength(false),
@ -454,7 +459,7 @@ public:
TypePointer const& getBaseType() const { solAssert(!!m_baseType, ""); return m_baseType;}
u256 const& getLength() const { return m_length; }
TypePointer copyForLocation(Location _location, bool _isPointer) const override;
TypePointer copyForLocation(DataLocation _location, bool _isPointer) const override;
private:
/// String is interpreted as a subtype of Bytes.
@ -533,7 +538,7 @@ public:
virtual Category getCategory() const override { return Category::Struct; }
explicit StructType(StructDefinition const& _struct):
//@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 TypePointer unaryOperatorResult(Token::Value _operator) const override;
virtual bool operator==(Type const& _other) const override;
@ -544,7 +549,7 @@ public:
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;
@ -636,8 +641,11 @@ public:
FunctionTypePointer externalFunctionType() const;
virtual TypePointer externalType() const override { return externalFunctionType(); }
/// Creates the type of a function.
explicit FunctionType(FunctionDefinition const& _function, bool _isInternal = true);
/// Creates the accessor function type of a state variable.
explicit FunctionType(VariableDeclaration const& _varDecl);
/// Creates the function type of an event.
explicit FunctionType(EventDefinition const& _event);
FunctionType(
strings const& _parameterTypes,

2
libweb3jsonrpc/JsonHelper.h

@ -43,7 +43,7 @@ namespace eth
{
class Transaction;
class BlockDetails;
struct BlockDetails;
class Interface;
using Transactions = std::vector<Transaction>;
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 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);
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);

6
test/TestHelper.cpp

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

2
test/TestHelper.h

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

11
test/TestUtils.cpp

@ -22,6 +22,7 @@
#include <thread>
#include <boost/test/unit_test.hpp>
#include <boost/filesystem.hpp>
#include <libdevcrypto/Common.h>
#include <libtestutils/Common.h>
#include <libtestutils/BlockChainLoader.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 <string>
#include <json/json.h>
#include <libdevcore/TransientDirectory.h>
#include <libethereum/BlockChain.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/TrieHash.h>
#include "MemTrie.h"
#include "../TestHelper.h"
#include <test/TestHelper.h>
#include <test/TestUtils.h>
using namespace std;
using namespace dev;
using namespace dev::test;
namespace js = json_spirit;
namespace fs = boost::filesystem;
BOOST_GLOBAL_FIXTURE( MoveNonceToTempDir )
BOOST_AUTO_TEST_SUITE(KeyStore)
@ -52,7 +57,8 @@ BOOST_AUTO_TEST_CASE(basic_tests)
{
cnote << i.first;
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));
cdebug << "read uuid" << u;
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()

4
test/libdevcrypto/crypto.cpp

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

6
test/libethereum/stateOriginal.cpp

@ -27,7 +27,8 @@
#include <libethereum/State.h>
#include <libethcore/Farm.h>
#include <libethereum/Defaults.h>
#include "../TestHelper.h"
#include <test/TestHelper.h>
using namespace std;
using namespace dev;
using namespace dev::eth;
@ -48,6 +49,9 @@ BOOST_AUTO_TEST_CASE(Basic)
BOOST_AUTO_TEST_CASE(Complex)
{
if (test::Options::get().nodag)
return;
cnote << "Testing State...";
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/Capability.h>
#include <libp2p/HostCapability.h>
#include <test/TestHelper.h>
using namespace std;
using namespace dev;
@ -98,6 +99,9 @@ BOOST_FIXTURE_TEST_SUITE(p2pCapability, P2PFixture)
BOOST_AUTO_TEST_CASE(capability)
{
if (test::Options::get().nonetwork)
return;
VerbosityHolder verbosityHolder(10);
cnote << "Testing Capability...";

26
test/libp2p/net.cpp

@ -26,6 +26,8 @@
#include <libdevcrypto/Common.h>
#include <libp2p/UDP.h>
#include <libp2p/NodeTable.h>
#include <test/TestHelper.h>
using namespace std;
using namespace dev;
using namespace dev::p2p;
@ -153,6 +155,9 @@ public:
BOOST_AUTO_TEST_CASE(requestTimeout)
{
if (test::Options::get().nonetwork)
return;
using TimePoint = std::chrono::steady_clock::time_point;
using RequestTimeout = std::pair<NodeId, TimePoint>;
@ -220,6 +225,9 @@ BOOST_AUTO_TEST_CASE(isIPAddressType)
BOOST_AUTO_TEST_CASE(v2PingNodePacket)
{
if (test::Options::get().nonetwork)
return;
// test old versino of pingNode packet w/new
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();
@ -231,6 +239,9 @@ BOOST_AUTO_TEST_CASE(v2PingNodePacket)
BOOST_AUTO_TEST_CASE(neighboursPacketLength)
{
if (test::Options::get().nonetwork)
return;
KeyPair k = KeyPair::create();
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);
@ -256,6 +267,9 @@ BOOST_AUTO_TEST_CASE(neighboursPacketLength)
BOOST_AUTO_TEST_CASE(neighboursPacket)
{
if (test::Options::get().nonetwork)
return;
KeyPair k = KeyPair::create();
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);
@ -291,6 +305,9 @@ BOOST_AUTO_TEST_CASE(test_findnode_neighbours)
BOOST_AUTO_TEST_CASE(kademlia)
{
if (test::Options::get().nonetwork)
return;
// Not yet a 'real' test.
TestNodeTableHost node(8);
node.start();
@ -324,6 +341,9 @@ BOOST_AUTO_TEST_CASE(kademlia)
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}));
TestUDPSocket a; a.m_socket->connect(); a.start();
a.m_socket->send(d);
@ -337,6 +357,9 @@ BOOST_AUTO_TEST_SUITE(netTypes)
BOOST_AUTO_TEST_CASE(unspecifiedNode)
{
if (test::Options::get().nonetwork)
return;
Node n = UnspecifiedNode;
BOOST_REQUIRE(!n);
@ -350,6 +373,9 @@ BOOST_AUTO_TEST_CASE(unspecifiedNode)
BOOST_AUTO_TEST_CASE(nodeTableReturnsUnspecifiedNode)
{
if (test::Options::get().nonetwork)
return;
ba::io_service io;
NodeTable t(io, KeyPair::create(), NodeIPEndpoint(bi::address::from_string("127.0.0.1"), 30303, 30303));
if (Node n = t.node(NodeId()))

17
test/libp2p/peer.cpp

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

88
test/libsolidity/SolidityEndToEndTest.cpp

@ -4243,9 +4243,9 @@ BOOST_AUTO_TEST_CASE(return_string)
function get1() returns (string r) {
return s;
}
// function get2() returns (string r) {
// r = s;
// }
function get2() returns (string r) {
r = s;
}
}
)";
compileAndRun(sourceCode, 0, "Main");
@ -4253,8 +4253,86 @@ BOOST_AUTO_TEST_CASE(return_string)
bytes args = encodeArgs(u256(0x20), u256(s.length()), s);
BOOST_REQUIRE(callContractFunction("set(string)", asString(args)) == encodeArgs());
BOOST_CHECK(callContractFunction("get1()") == args);
// BOOST_CHECK(callContractFunction("get2()") == args);
// BOOST_CHECK(callContractFunction("s()") == args);
BOOST_CHECK(callContractFunction("get2()") == 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)

14
test/libsolidity/SolidityTypes.cpp

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

11
test/libwhisper/whisperTopic.cpp

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

Loading…
Cancel
Save