Browse Source

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

mix_test

Conflicts:
	mix/qml.qrc
	mix/qml/LogsPane.qml
	mix/qml/StatusPane.qml
cl-refactor
arkpar 10 years ago
parent
commit
8a98da73e6
  1. 2
      CodingStandards.txt
  2. 1
      alethzero/MainWin.cpp
  3. 5
      libdevcore/CommonJS.h
  4. 2
      libdevcrypto/MemoryDB.h
  5. 7
      libethash/CMakeLists.txt
  6. 4
      libethash/data_sizes.h
  7. 133
      libethash/ethash.h
  8. 44
      libethash/internal.c
  9. 8
      libethash/internal.h
  10. 89
      libethash/io.c
  11. 116
      libethash/io.h
  12. 76
      libethash/io_posix.c
  13. 73
      libethash/io_win32.c
  14. 25
      libethcore/Ethasher.cpp
  15. 27
      libethcore/Ethasher.h
  16. 21
      libethereum/BlockChain.cpp
  17. 2
      libethereum/State.cpp
  18. 55
      libevmcore/Assembly.cpp
  19. 2
      libevmcore/Assembly.h
  20. 5
      libevmcore/AssemblyItem.h
  21. 330
      libevmcore/CommonSubexpressionEliminator.cpp
  22. 99
      libevmcore/CommonSubexpressionEliminator.h
  23. 100
      libevmcore/ExpressionClasses.cpp
  24. 20
      libevmcore/ExpressionClasses.h
  25. 107
      libevmcore/SemanticInformation.cpp
  26. 50
      libevmcore/SemanticInformation.h
  27. 69
      libp2p/Host.cpp
  28. 16
      libsolidity/AST.cpp
  29. 17
      libsolidity/Types.cpp
  30. 2
      libsolidity/Types.h
  31. 2
      libweb3jsonrpc/WebThreeStubServerBase.cpp
  32. 21
      mix/CodeModel.cpp
  33. 3
      mix/qml.qrc
  34. 1
      mix/qml/CodeEditorView.qml
  35. 27
      mix/qml/Debugger.qml
  36. 586
      mix/qml/LogsPane.qml
  37. 18
      mix/qml/LogsPaneStyle.qml
  38. 1
      mix/qml/ProjectModel.qml
  39. 260
      mix/qml/StatesComboBox.qml
  40. 63
      mix/qml/StatusPane.qml
  41. 4
      mix/qml/StorageView.qml
  42. 59
      mix/qml/TransactionLog.qml
  43. 7
      mix/qml/WebPreview.qml
  44. 41
      mix/qml/html/codeeditor.js
  45. BIN
      mix/qml/img/broom.png
  46. BIN
      mix/qml/img/clearicon.png
  47. BIN
      mix/qml/img/cleariconactive.png
  48. BIN
      mix/qml/img/copyicon.png
  49. BIN
      mix/qml/img/copyiconactive.png
  50. BIN
      mix/qml/img/edit_combox.png
  51. BIN
      mix/qml/img/pause_button.png
  52. BIN
      mix/qml/img/pause_button2x.png
  53. BIN
      mix/qml/img/play_button.png
  54. BIN
      mix/qml/img/play_button2x.png
  55. BIN
      mix/qml/img/searchicon.png
  56. BIN
      mix/qml/img/stop_button2x.png
  57. 10
      mix/qml/js/ProjectModel.js
  58. 12
      mix/res.qrc
  59. 19
      mix/style.xml
  60. 37
      test/ClientBase.cpp
  61. 14
      test/SolidityNameAndTypeResolution.cpp
  62. 265
      test/SolidityOptimizer.cpp
  63. 4
      test/dagger.cpp
  64. 9
      test/peer.cpp

2
CodingStandards.txt

@ -13,7 +13,7 @@ c. Don't use braces for condition-body one-liners.
d. Never place condition bodies on same line as condition.
e. Space between first paren and keyword, but *not* following first paren or preceeding final paren.
f. No spaces when fewer than intra-expression three parens together; when three or more, space according to clarity.
g. No spaces for subscripting.
g. No spaces for subscripting or unary operators.
h. No space before ':' but one after it, except in the ternary operator: one on both sides.
i. Space all other operators.
j. Braces, when used, always have their own lines and are at same indentation level as "parent" scope.

1
alethzero/MainWin.cpp

@ -1774,6 +1774,7 @@ void Main::on_net_triggered()
else
{
ui->downloadView->setDownloadMan(nullptr);
writeSettings();
web3()->stopNetwork();
}
}

5
libdevcore/CommonJS.h

@ -38,7 +38,10 @@ template <unsigned S> std::string toJS(FixedHash<S> const& _h)
template <unsigned N> std::string toJS(boost::multiprecision::number<boost::multiprecision::cpp_int_backend<N, N, boost::multiprecision::unsigned_magnitude, boost::multiprecision::unchecked, void>> const& _n)
{
return "0x" + toHex(toCompactBigEndian(_n, 1));
std::string h = toHex(toCompactBigEndian(_n, 1));
// remove first 0, if it is necessary;
std::string res = h[0] != '0' ? h : h.substr(1);
return "0x" + res;
}
inline std::string toJS(bytes const& _n, std::size_t _padding = 0)

2
libdevcrypto/MemoryDB.h

@ -32,8 +32,10 @@ namespace dev
{
struct DBChannel: public LogChannel { static const char* name() { return "TDB"; } static const int verbosity = 18; };
struct DBWarn: public LogChannel { static const char* name() { return "TDB"; } static const int verbosity = 1; };
#define dbdebug clog(DBChannel)
#define dbwarn clog(DBWarn)
class MemoryDB
{

7
libethash/CMakeLists.txt

@ -12,6 +12,7 @@ endif()
set(FILES util.c
util.h
io.c
internal.c
ethash.h
endian.h
@ -19,6 +20,12 @@ set(FILES util.c
fnv.h
data_sizes.h)
if (MSVC)
list(APPEND FILES io_win32.c)
else()
list(APPEND FILES io_posix.c)
endif()
if (NOT CRYPTOPP_FOUND)
find_package(CryptoPP 5.6.2)
endif()

4
libethash/data_sizes.h

@ -48,7 +48,7 @@ extern "C" {
// Sow[i*HashBytes]; j++]]]][[2]][[1]]
static const size_t dag_sizes[2048] = {
static const uint64_t dag_sizes[2048] = {
1073739904U, 1082130304U, 1090514816U, 1098906752U, 1107293056U,
1115684224U, 1124070016U, 1132461952U, 1140849536U, 1149232768U,
1157627776U, 1166013824U, 1174404736U, 1182786944U, 1191180416U,
@ -477,7 +477,7 @@ static const size_t dag_sizes[2048] = {
// While[! PrimeQ[i], i--];
// Sow[i*HashBytes]; j++]]]][[2]][[1]]
const size_t cache_sizes[2048] = {
const uint64_t cache_sizes[2048] = {
16776896U, 16907456U, 17039296U, 17170112U, 17301056U, 17432512U, 17563072U,
17693888U, 17824192U, 17955904U, 18087488U, 18218176U, 18349504U, 18481088U,
18611392U, 18742336U, 18874304U, 19004224U, 19135936U, 19267264U, 19398208U,

133
libethash/ethash.h

@ -24,92 +24,115 @@
#include <stdbool.h>
#include <string.h>
#include <stddef.h>
#include <stdlib.h>
#include "compiler.h"
#define REVISION 23
#define DATASET_BYTES_INIT 1073741824U // 2**30
#define DATASET_BYTES_GROWTH 8388608U // 2**23
#define CACHE_BYTES_INIT 1073741824U // 2**24
#define CACHE_BYTES_GROWTH 131072U // 2**17
#define EPOCH_LENGTH 30000U
#define MIX_BYTES 128
#define HASH_BYTES 64
#define DATASET_PARENTS 256
#define CACHE_ROUNDS 3
#define ACCESSES 64
#define ETHASH_REVISION 23
#define ETHASH_DATASET_BYTES_INIT 1073741824U // 2**30
#define ETHASH_DATASET_BYTES_GROWTH 8388608U // 2**23
#define ETHASH_CACHE_BYTES_INIT 1073741824U // 2**24
#define ETHASH_CACHE_BYTES_GROWTH 131072U // 2**17
#define ETHASH_EPOCH_LENGTH 30000U
#define ETHASH_MIX_BYTES 128
#define ETHASH_HASH_BYTES 64
#define ETHASH_DATASET_PARENTS 256
#define ETHASH_CACHE_ROUNDS 3
#define ETHASH_ACCESSES 64
#ifdef __cplusplus
extern "C" {
#endif
typedef struct ethash_params {
size_t full_size; // Size of full data set (in bytes, multiple of mix size (128)).
size_t cache_size; // Size of compute cache (in bytes, multiple of node size (64)).
uint64_t full_size; // Size of full data set (in bytes, multiple of mix size (128)).
uint64_t cache_size; // Size of compute cache (in bytes, multiple of node size (64)).
} ethash_params;
typedef struct ethash_return_value {
uint8_t result[32];
uint8_t mix_hash[32];
uint8_t result[32];
uint8_t mix_hash[32];
} ethash_return_value;
size_t ethash_get_datasize(const uint32_t block_number);
size_t ethash_get_cachesize(const uint32_t block_number);
uint64_t ethash_get_datasize(const uint32_t block_number);
uint64_t ethash_get_cachesize(const uint32_t block_number);
// initialize the parameters
static inline void ethash_params_init(ethash_params *params, const uint32_t block_number) {
params->full_size = ethash_get_datasize(block_number);
params->cache_size = ethash_get_cachesize(block_number);
params->full_size = ethash_get_datasize(block_number);
params->cache_size = ethash_get_cachesize(block_number);
}
typedef struct ethash_cache {
void *mem;
} ethash_cache;
/***********************************
* OLD API *************************
***********************************
******************** (deprecated) *
***********************************/
void ethash_mkcache(ethash_cache *cache, ethash_params const *params, const uint8_t seed[32]);
void ethash_compute_full_data(void *mem, ethash_params const *params, ethash_cache const *cache);
void ethash_full(ethash_return_value *ret, void const *full_mem, ethash_params const *params, const uint8_t header_hash[32], const uint64_t nonce);
void ethash_light(ethash_return_value *ret, ethash_cache const *cache, ethash_params const *params, const uint8_t header_hash[32], const uint64_t nonce);
void ethash_get_seedhash(uint8_t seedhash[32], const uint32_t block_number);
void ethash_mkcache(void *cache, ethash_params const *params, const uint8_t seed[32]);
void ethash_light(ethash_return_value *ret, void const *cache, ethash_params const *params, const uint8_t header_hash[32], const uint64_t nonce);
void ethash_compute_full_data(void *mem, ethash_params const *params, void const *cache);
void ethash_full(ethash_return_value *ret, void const *full_mem, ethash_params const *params, const uint8_t header_hash[32], const uint64_t nonce);
static inline void ethash_prep_light(void *cache, ethash_params const *params, const uint8_t seed[32]) {
ethash_cache c;
c.mem = cache;
ethash_mkcache(&c, params, seed);
}
/***********************************
* NEW API *************************
***********************************/
// TODO: compute params and seed in ethash_new_light; it should take only block_number
// TODO: store params in ethash_light_t/ethash_full_t to avoid having to repass into compute/new_full
typedef uint8_t const ethash_seedhash_t[32];
static inline void ethash_compute_light(ethash_return_value *ret, void const *cache, ethash_params const *params, const uint8_t header_hash[32], const uint64_t nonce) {
ethash_cache c;
c.mem = (void *) cache;
ethash_light(ret, &c, params, header_hash, nonce);
typedef void const* ethash_light_t;
static inline ethash_light_t ethash_new_light(ethash_params const* params, ethash_seedhash_t seed) {
void* ret = malloc(params->cache_size);
ethash_mkcache(ret, params, seed);
return ret;
}
static inline void ethash_compute_light(ethash_return_value *ret, ethash_light_t light, ethash_params const *params, const uint8_t header_hash[32], const uint64_t nonce) {
ethash_light(ret, light, params, header_hash, nonce);
}
static inline void ethash_delete_light(ethash_light_t light) {
free((void*)light);
}
typedef void const* ethash_full_t;
static inline ethash_full_t ethash_new_full(ethash_params const* params, ethash_light_t light) {
void* ret = malloc(params->full_size);
ethash_compute_full_data(ret, params, light);
return ret;
}
static inline void ethash_prep_full(void *full, ethash_params const *params, void const *cache) {
ethash_cache c;
c.mem = (void *) cache;
ethash_compute_full_data(full, params, &c);
ethash_compute_full_data(full, params, cache);
}
static inline void ethash_compute_full(ethash_return_value *ret, void const *full, ethash_params const *params, const uint8_t header_hash[32], const uint64_t nonce) {
ethash_full(ret, full, params, header_hash, nonce);
ethash_full(ret, full, params, header_hash, nonce);
}
// Returns if hash is less than or equal to difficulty
static inline int ethash_check_difficulty(
const uint8_t hash[32],
const uint8_t difficulty[32]) {
// Difficulty is big endian
for (int i = 0; i < 32; i++) {
if (hash[i] == difficulty[i]) continue;
return hash[i] < difficulty[i];
}
return 1;
/// @brief Compare two s256-bit big-endian values.
/// @returns 1 if @a a is less than or equal to @a b, 0 otherwise.
/// Both parameters are 256-bit big-endian values.
static inline int ethash_leq_be256(const uint8_t a[32], const uint8_t b[32]) {
// Boundary is big endian
for (int i = 0; i < 32; i++) {
if (a[i] == b[i])
continue;
return a[i] < b[i];
}
return 1;
}
int ethash_quick_check_difficulty(
const uint8_t header_hash[32],
const uint64_t nonce,
const uint8_t mix_hash[32],
const uint8_t difficulty[32]);
/// Perofrms a cursory check on the validity of the nonce.
/// @returns 1 if the nonce may possibly be valid for the given header_hash & boundary.
/// @p boundary equivalent to 2 ^ 256 / block_difficulty, represented as a 256-bit big-endian.
int ethash_preliminary_check_boundary(
const uint8_t header_hash[32],
const uint64_t nonce,
const uint8_t mix_hash[32],
const uint8_t boundary[32]);
#define ethash_quick_check_difficulty ethash_preliminary_check_boundary
#define ethash_check_difficulty ethash_leq_be256
#ifdef __cplusplus
}

44
libethash/internal.c

@ -37,14 +37,14 @@
#include "sha3.h"
#endif // WITH_CRYPTOPP
size_t ethash_get_datasize(const uint32_t block_number) {
assert(block_number / EPOCH_LENGTH < 2048);
return dag_sizes[block_number / EPOCH_LENGTH];
uint64_t ethash_get_datasize(const uint32_t block_number) {
assert(block_number / ETHASH_EPOCH_LENGTH < 2048);
return dag_sizes[block_number / ETHASH_EPOCH_LENGTH];
}
size_t ethash_get_cachesize(const uint32_t block_number) {
assert(block_number / EPOCH_LENGTH < 2048);
return cache_sizes[block_number / EPOCH_LENGTH];
uint64_t ethash_get_cachesize(const uint32_t block_number) {
assert(block_number / ETHASH_EPOCH_LENGTH < 2048);
return cache_sizes[block_number / ETHASH_EPOCH_LENGTH];
}
// Follows Sergio's "STRICT MEMORY HARD HASHING FUNCTIONS" (2014)
@ -63,7 +63,7 @@ void static ethash_compute_cache_nodes(
SHA3_512(nodes[i].bytes, nodes[i - 1].bytes, 64);
}
for (unsigned j = 0; j != CACHE_ROUNDS; j++) {
for (unsigned j = 0; j != ETHASH_CACHE_ROUNDS; j++) {
for (unsigned i = 0; i != num_nodes; i++) {
uint32_t const idx = nodes[i].words[0] % num_nodes;
node data;
@ -85,10 +85,10 @@ void static ethash_compute_cache_nodes(
}
void ethash_mkcache(
ethash_cache *cache,
void *cache,
ethash_params const *params,
const uint8_t seed[32]) {
node *nodes = (node *) cache->mem;
node *nodes = (node *) cache;
ethash_compute_cache_nodes(nodes, params, seed);
}
@ -96,10 +96,10 @@ void ethash_calculate_dag_item(
node *const ret,
const unsigned node_index,
const struct ethash_params *params,
const struct ethash_cache *cache) {
const void *cache) {
uint32_t num_parent_nodes = (uint32_t) (params->cache_size / sizeof(node));
node const *cache_nodes = (node const *) cache->mem;
node const *cache_nodes = (node const *) cache;
node const *init = &cache_nodes[node_index % num_parent_nodes];
memcpy(ret, init, sizeof(node));
@ -114,7 +114,7 @@ void ethash_calculate_dag_item(
__m128i xmm3 = ret->xmm[3];
#endif
for (unsigned i = 0; i != DATASET_PARENTS; ++i) {
for (unsigned i = 0; i != ETHASH_DATASET_PARENTS; ++i) {
uint32_t parent_index = ((node_index ^ i) * FNV_PRIME ^ ret->words[i % NODE_WORDS]) % num_parent_nodes;
node const *parent = &cache_nodes[parent_index];
@ -150,7 +150,7 @@ void ethash_calculate_dag_item(
void ethash_compute_full_data(
void *mem,
ethash_params const *params,
ethash_cache const *cache) {
void const *cache) {
assert((params->full_size % (sizeof(uint32_t) * MIX_WORDS)) == 0);
assert((params->full_size % sizeof(node)) == 0);
node *full_nodes = mem;
@ -164,7 +164,7 @@ void ethash_compute_full_data(
static void ethash_hash(
ethash_return_value *ret,
node const *full_nodes,
ethash_cache const *cache,
void const *cache,
ethash_params const *params,
const uint8_t header_hash[32],
const uint64_t nonce) {
@ -201,7 +201,7 @@ static void ethash_hash(
num_full_pages = (unsigned) (params->full_size / page_size);
for (unsigned i = 0; i != ACCESSES; ++i) {
for (unsigned i = 0; i != ETHASH_ACCESSES; ++i) {
uint32_t const index = ((s_mix->words[0] ^ i) * FNV_PRIME ^ mix->words[i % MIX_WORDS]) % num_full_pages;
for (unsigned n = 0; n != MIX_NODES; ++n) {
@ -275,26 +275,26 @@ void ethash_quick_hash(
void ethash_get_seedhash(uint8_t seedhash[32], const uint32_t block_number) {
memset(seedhash, 0, 32);
const uint32_t epochs = block_number / EPOCH_LENGTH;
const uint32_t epochs = block_number / ETHASH_EPOCH_LENGTH;
for (uint32_t i = 0; i < epochs; ++i)
SHA3_256(seedhash, seedhash, 32);
}
int ethash_quick_check_difficulty(
int ethash_preliminary_check_boundary(
const uint8_t header_hash[32],
const uint64_t nonce,
const uint8_t mix_hash[32],
const uint8_t difficulty[32]) {
const uint8_t difficulty[32]) {
uint8_t return_hash[32];
uint8_t return_hash[32];
ethash_quick_hash(return_hash, header_hash, nonce, mix_hash);
return ethash_check_difficulty(return_hash, difficulty);
return ethash_leq_be256(return_hash, difficulty);
}
void ethash_full(ethash_return_value *ret, void const *full_mem, ethash_params const *params, const uint8_t previous_hash[32], const uint64_t nonce) {
ethash_hash(ret, (node const *) full_mem, NULL, params, previous_hash, nonce);
}
void ethash_light(ethash_return_value *ret, ethash_cache const *cache, ethash_params const *params, const uint8_t previous_hash[32], const uint64_t nonce) {
void ethash_light(ethash_return_value *ret, void const *cache, ethash_params const *params, const uint8_t previous_hash[32], const uint64_t nonce) {
ethash_hash(ret, NULL, cache, params, previous_hash, nonce);
}
}

8
libethash/internal.h

@ -3,7 +3,7 @@
#include "endian.h"
#include "ethash.h"
#define ENABLE_SSE 1
#define ENABLE_SSE 0
#if defined(_M_X64) && ENABLE_SSE
#include <smmintrin.h>
@ -15,7 +15,7 @@ extern "C" {
// compile time settings
#define NODE_WORDS (64/4)
#define MIX_WORDS (MIX_BYTES/4)
#define MIX_WORDS (ETHASH_MIX_BYTES/4)
#define MIX_NODES (MIX_WORDS / NODE_WORDS)
#include <stdint.h>
@ -34,7 +34,7 @@ void ethash_calculate_dag_item(
node *const ret,
const unsigned node_index,
ethash_params const *params,
ethash_cache const *cache
void const *cache
);
void ethash_quick_hash(
@ -45,4 +45,4 @@ void ethash_quick_hash(
#ifdef __cplusplus
}
#endif
#endif

89
libethash/io.c

@ -0,0 +1,89 @@
/*
This file is part of ethash.
ethash 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.
ethash 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 ethash. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file io.c
* @author Lefteris Karapetsas <lefteris@ethdev.com>
* @date 2015
*/
#include "io.h"
#include <string.h>
#include <stdio.h>
// silly macro to save some typing
#define PASS_ARR(c_) (c_), sizeof(c_)
static bool ethash_io_write_file(char const *dirname,
char const* filename,
size_t filename_length,
void const* data,
size_t data_size)
{
bool ret = false;
char *fullname = ethash_io_create_filename(dirname, filename, filename_length);
if (!fullname) {
return false;
}
FILE *f = fopen(fullname, "wb");
if (!f) {
goto free_name;
}
if (data_size != fwrite(data, 1, data_size, f)) {
goto close;
}
ret = true;
close:
fclose(f);
free_name:
free(fullname);
return ret;
}
bool ethash_io_write(char const *dirname,
ethash_params const* params,
ethash_blockhash_t seedhash,
void const* cache,
uint8_t **data,
size_t *data_size)
{
char info_buffer[DAG_MEMO_BYTESIZE];
// allocate the bytes
uint8_t *temp_data_ptr = malloc(params->full_size);
if (!temp_data_ptr) {
goto end;
}
ethash_compute_full_data(temp_data_ptr, params, cache);
if (!ethash_io_write_file(dirname, PASS_ARR(DAG_FILE_NAME), temp_data_ptr, params->full_size)) {
goto fail_free;
}
ethash_io_serialize_info(ETHASH_REVISION, seedhash, info_buffer);
if (!ethash_io_write_file(dirname, PASS_ARR(DAG_MEMO_NAME), info_buffer, DAG_MEMO_BYTESIZE)) {
goto fail_free;
}
*data = temp_data_ptr;
*data_size = params->full_size;
return true;
fail_free:
free(temp_data_ptr);
end:
return false;
}
#undef PASS_ARR

116
libethash/io.h

@ -0,0 +1,116 @@
/*
This file is part of ethash.
ethash 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.
ethash 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 ethash. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file io.h
* @author Lefteris Karapetsas <lefteris@ethdev.com>
* @date 2015
*/
#pragma once
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include "ethash.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef struct ethash_blockhash { uint8_t b[32]; } ethash_blockhash_t;
static const char DAG_FILE_NAME[] = "full";
static const char DAG_MEMO_NAME[] = "full.info";
// MSVC thinks that "static const unsigned int" is not a compile time variable. Sorry for the #define :(
#define DAG_MEMO_BYTESIZE 36
/// Possible return values of @see ethash_io_prepare
enum ethash_io_rc {
ETHASH_IO_FAIL = 0, ///< There has been an IO failure
ETHASH_IO_MEMO_MISMATCH, ///< Memo file either did not exist or there was content mismatch
ETHASH_IO_MEMO_MATCH, ///< Memo file existed and contents matched. No need to do anything
};
/**
* Prepares io for ethash
*
* Create the DAG directory if it does not exist, and check if the memo file matches.
* If it does not match then it's deleted to pave the way for @ref ethash_io_write()
*
* @param dirname A null terminated c-string of the path of the ethash
* data directory. If it does not exist it's created.
* @param seedhash The seedhash of the current block number
* @return For possible return values @see enum ethash_io_rc
*/
enum ethash_io_rc ethash_io_prepare(char const *dirname, ethash_blockhash_t seedhash);
/**
* Fully computes data and writes it to the file on disk.
*
* This function should be called after @see ethash_io_prepare() and only if
* its return value is @c ETHASH_IO_MEMO_MISMATCH. Will write both the full data
* and the memo file.
*
* @param[in] dirname A null terminated c-string of the path of the ethash
* data directory. Has to exist.
* @param[in] params An ethash_params object containing the full size
* and the cache size
* @param[in] seedhash The seedhash of the current block number
* @param[in] cache The cache data. Would have usually been calulated by
* @see ethash_prep_light().
* @param[out] data Pass a pointer to uint8_t by reference here. If the
* function is succesfull then this point to the allocated
* data calculated by @see ethash_prep_full(). Memory
* ownership is transfered to the callee. Remember that
* you eventually need to free this with a call to free().
* @param[out] data_size Pass a size_t by value. If the function is succesfull
* then this will contain the number of bytes allocated
* for @a data.
* @return True for success and false in case of failure.
*/
bool ethash_io_write(char const *dirname,
ethash_params const* params,
ethash_blockhash_t seedhash,
void const* cache,
uint8_t **data,
size_t *data_size);
static inline void ethash_io_serialize_info(uint32_t revision,
ethash_blockhash_t seed_hash,
char *output)
{
// if .info is only consumed locally we don't really care about endianess
memcpy(output, &revision, 4);
memcpy(output + 4, &seed_hash, 32);
}
static inline char *ethash_io_create_filename(char const *dirname,
char const* filename,
size_t filename_length)
{
// in C the cast is not needed, but a C++ compiler will complain for invalid conversion
char *name = (char*)malloc(strlen(dirname) + filename_length);
if (!name) {
return NULL;
}
name[0] = '\0';
strcat(name, dirname);
strcat(name, filename);
return name;
}
#ifdef __cplusplus
}
#endif

76
libethash/io_posix.c

@ -0,0 +1,76 @@
/*
This file is part of ethash.
ethash 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.
ethash 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 ethash. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file io_posix.c
* @author Lefteris Karapetsas <lefteris@ethdev.com>
* @date 2015
*/
#include "io.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <libgen.h>
#include <stdio.h>
#include <unistd.h>
enum ethash_io_rc ethash_io_prepare(char const *dirname, ethash_blockhash_t seedhash)
{
char read_buffer[DAG_MEMO_BYTESIZE];
char expect_buffer[DAG_MEMO_BYTESIZE];
enum ethash_io_rc ret = ETHASH_IO_FAIL;
// assert directory exists, full owner permissions and read/search for others
int rc = mkdir(dirname, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
if (rc == -1 && errno != EEXIST) {
goto end;
}
char *memofile = ethash_io_create_filename(dirname, DAG_MEMO_NAME, sizeof(DAG_MEMO_NAME));
if (!memofile) {
goto end;
}
// try to open memo file
FILE *f = fopen(memofile, "rb");
if (!f) {
// file does not exist, so no checking happens. All is fine.
ret = ETHASH_IO_MEMO_MISMATCH;
goto free_memo;
}
if (fread(read_buffer, 1, DAG_MEMO_BYTESIZE, f) != DAG_MEMO_BYTESIZE) {
goto close;
}
ethash_io_serialize_info(ETHASH_REVISION, seedhash, expect_buffer);
if (memcmp(read_buffer, expect_buffer, DAG_MEMO_BYTESIZE) != 0) {
// we have different memo contents so delete the memo file
if (unlink(memofile) != 0) {
goto close;
}
ret = ETHASH_IO_MEMO_MISMATCH;
}
ret = ETHASH_IO_MEMO_MATCH;
close:
fclose(f);
free_memo:
free(memofile);
end:
return ret;
}

73
libethash/io_win32.c

@ -0,0 +1,73 @@
/*
This file is part of ethash.
ethash 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.
ethash 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 ethash. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file io_win32.c
* @author Lefteris Karapetsas <lefteris@ethdev.com>
* @date 2015
*/
#include "io.h"
#include <direct.h>
#include <errno.h>
#include <stdio.h>
enum ethash_io_rc ethash_io_prepare(char const *dirname, ethash_blockhash_t seedhash)
{
char read_buffer[DAG_MEMO_BYTESIZE];
char expect_buffer[DAG_MEMO_BYTESIZE];
enum ethash_io_rc ret = ETHASH_IO_FAIL;
// assert directory exists
int rc = _mkdir(dirname);
if (rc == -1 && errno != EEXIST) {
goto end;
}
char *memofile = ethash_io_create_filename(dirname, DAG_MEMO_NAME, sizeof(DAG_MEMO_NAME));
if (!memofile) {
goto end;
}
// try to open memo file
FILE *f = fopen(memofile, "rb");
if (!f) {
// file does not exist, so no checking happens. All is fine.
ret = ETHASH_IO_MEMO_MISMATCH;
goto free_memo;
}
if (fread(read_buffer, 1, DAG_MEMO_BYTESIZE, f) != DAG_MEMO_BYTESIZE) {
goto close;
}
ethash_io_serialize_info(ETHASH_REVISION, seedhash, expect_buffer);
if (memcmp(read_buffer, expect_buffer, DAG_MEMO_BYTESIZE) != 0) {
// we have different memo contents so delete the memo file
if (_unlink(memofile) != 0) {
goto close;
}
ret = ETHASH_IO_MEMO_MISMATCH;
}
ret = ETHASH_IO_MEMO_MATCH;
close:
fclose(f);
free_memo:
free(memofile);
end:
return ret;
}

25
libethcore/Ethasher.cpp

@ -41,7 +41,23 @@ using namespace eth;
Ethasher* dev::eth::Ethasher::s_this = nullptr;
bytes const& Ethasher::cache(BlockInfo const& _header)
Ethasher::~Ethasher()
{
while (!m_caches.empty())
killCache(m_caches.begin()->first);
}
void Ethasher::killCache(h256 const& _s)
{
RecursiveGuard l(x_this);
if (m_caches.count(_s))
{
ethash_delete_light(m_caches.at(_s));
m_caches.erase(_s);
}
}
void const* Ethasher::cache(BlockInfo const& _header)
{
RecursiveGuard l(x_this);
if (_header.number > c_ethashEpochLength * 2048)
@ -54,8 +70,7 @@ bytes const& Ethasher::cache(BlockInfo const& _header)
if (!m_caches.count(_header.seedHash()))
{
ethash_params p = params((unsigned)_header.number);
m_caches[_header.seedHash()].resize(p.cache_size);
ethash_prep_light(m_caches[_header.seedHash()].data(), &p, _header.seedHash().data());
m_caches[_header.seedHash()] = ethash_new_light(&p, _header.seedHash().data());
}
return m_caches[_header.seedHash()];
}
@ -84,7 +99,7 @@ bytesConstRef Ethasher::full(BlockInfo const& _header)
ethash_params p = params((unsigned)_header.number);
m_fulls[_header.seedHash()] = bytesRef(new byte[p.full_size], p.full_size);
auto c = cache(_header);
ethash_prep_full(m_fulls[_header.seedHash()].data(), &p, c.data());
ethash_prep_full(m_fulls[_header.seedHash()].data(), &p, c);
writeFile(memoFile, m_fulls[_header.seedHash()]);
writeFile(memoFile + ".info", info);
}
@ -147,7 +162,7 @@ Ethasher::Result Ethasher::eval(BlockInfo const& _header, Nonce const& _nonce)
{
auto p = Ethasher::params(_header);
ethash_return_value r;
ethash_compute_light(&r, Ethasher::get()->cache(_header).data(), &p, _header.headerHash(WithoutNonce).data(), (uint64_t)(u64)_nonce);
ethash_compute_light(&r, Ethasher::get()->cache(_header), &p, _header.headerHash(WithoutNonce).data(), (uint64_t)(u64)_nonce);
// cdebug << "Ethasher::eval sha3(cache):" << sha3(Ethasher::get()->cache(_header)) << "hh:" << _header.headerHash(WithoutNonce) << "nonce:" << _nonce << " => " << h256(r.result, h256::ConstructFromPointer);
return Result{h256(r.result, h256::ConstructFromPointer), h256(r.mix_hash, h256::ConstructFromPointer)};
}

27
libethcore/Ethasher.h

@ -30,21 +30,8 @@
#include <libdevcore/Log.h>
#include <libdevcrypto/SHA3.h>
#include <libethash/ethash.h> // TODO: REMOVE once everything merged into this class and an opaque API can be provided.
static const unsigned c_ethashRevision = REVISION;
static const unsigned c_ethashEpochLength = EPOCH_LENGTH;
#undef REVISION
#undef DATASET_BYTES_INIT
#undef DATASET_BYTES_GROWTH
#undef CACHE_BYTES_INIT
#undef CACHE_BYTES_GROWTH
#undef DAGSIZE_BYTES_INIT
#undef DAG_GROWTH
#undef EPOCH_LENGTH
#undef MIX_BYTES
#undef HASH_BYTES
#undef DATASET_PARENTS
#undef CACHE_ROUNDS
#undef ACCESSES
static const unsigned c_ethashRevision = ETHASH_REVISION;
static const unsigned c_ethashEpochLength = ETHASH_EPOCH_LENGTH;
#include "Common.h"
#include "BlockInfo.h"
@ -57,10 +44,14 @@ class Ethasher
{
public:
Ethasher() {}
~Ethasher();
static Ethasher* get() { if (!s_this) s_this = new Ethasher(); return s_this; }
bytes const& cache(BlockInfo const& _header);
using LightType = void const*;
using FullType = void const*;
LightType cache(BlockInfo const& _header);
bytesConstRef full(BlockInfo const& _header);
static ethash_params params(BlockInfo const& _header);
static ethash_params params(unsigned _n);
@ -104,9 +95,11 @@ public:
};
private:
void killCache(h256 const& _s);
static Ethasher* s_this;
RecursiveMutex x_this;
std::map<h256, bytes> m_caches;
std::map<h256, LightType> m_caches;
std::map<h256, bytesRef> m_fulls;
};

21
libethereum/BlockChain.cpp

@ -339,6 +339,13 @@ h256s BlockChain::import(bytes const& _block, OverlayDB const& _db)
#endif
// All ok - insert into DB
{
// ensure parent is cached for later addition.
// TODO: this is a bit horrible would be better refactored into an enveloping UpgradableGuard
// together with an "ensureCachedWithUpdatableLock(l)" method.
// This is safe in practice since the caches don't get flushed nearly often enough to be
// done here.
details(bi.parentHash);
WriteGuard l(x_details);
m_details[newHash] = BlockDetails((unsigned)pd.number + 1, td, bi.parentHash, {});
m_details[bi.parentHash].children.push_back(newHash);
@ -455,14 +462,14 @@ h256s BlockChain::import(bytes const& _block, OverlayDB const& _db)
h256s BlockChain::treeRoute(h256 const& _from, h256 const& _to, h256* o_common, bool _pre, bool _post) const
{
// cdebug << "treeRoute" << _from.abridged() << "..." << _to.abridged();
cdebug << "treeRoute" << _from.abridged() << "..." << _to.abridged();
if (!_from || !_to)
return h256s();
h256s ret;
h256s back;
unsigned fn = details(_from).number;
unsigned tn = details(_to).number;
// cdebug << "treeRoute" << fn << "..." << tn;
cdebug << "treeRoute" << fn << "..." << tn;
h256 from = _from;
while (fn > tn)
{
@ -470,7 +477,7 @@ h256s BlockChain::treeRoute(h256 const& _from, h256 const& _to, h256* o_common,
ret.push_back(from);
from = details(from).parent;
fn--;
// cdebug << "from:" << fn << _from.abridged();
cdebug << "from:" << fn << _from.abridged();
}
h256 to = _to;
while (fn < tn)
@ -479,12 +486,14 @@ h256s BlockChain::treeRoute(h256 const& _from, h256 const& _to, h256* o_common,
back.push_back(to);
to = details(to).parent;
tn--;
// cdebug << "to:" << tn << _to.abridged();
cdebug << "to:" << tn << _to.abridged();
}
while (from != to)
{
assert(from);
assert(to);
if (!from)
assert(from);
if (!to)
assert(to);
from = details(from).parent;
to = details(to).parent;
if (_pre)

2
libethereum/State.cpp

@ -725,7 +725,7 @@ void State::commitToMine(BlockChain const& _bc)
uncommitToMine();
// cnote << "Committing to mine on block" << m_previousBlock.hash.abridged();
#ifdef ETH_PARANOIA
#if ETH_PARANOIA && 0
commit();
cnote << "Pre-reward stateRoot:" << m_state.root();
#endif

55
libevmcore/Assembly.cpp

@ -34,9 +34,9 @@ void Assembly::append(Assembly const& _a)
for (AssemblyItem i: _a.m_items)
{
if (i.type() == Tag || i.type() == PushTag)
i.m_data += m_usedTags;
i.setData(i.data() + m_usedTags);
else if (i.type() == PushSub || i.type() == PushSubSize)
i.m_data += m_subs.size();
i.setData(i.data() + m_usedTags);
append(i);
}
m_deposit = newDeposit;
@ -106,34 +106,34 @@ ostream& Assembly::stream(ostream& _out, string const& _prefix, StringMap const&
for (AssemblyItem const& i: m_items)
{
_out << _prefix;
switch (i.m_type)
switch (i.type())
{
case Operation:
_out << " " << instructionInfo(i.instruction()).name << "\t" << i.getJumpTypeAsString();
break;
case Push:
_out << " PUSH " << i.m_data;
_out << " PUSH " << i.data();
break;
case PushString:
_out << " PUSH \"" << m_strings.at((h256)i.m_data) << "\"";
_out << " PUSH \"" << m_strings.at((h256)i.data()) << "\"";
break;
case PushTag:
_out << " PUSH [tag" << i.m_data << "]";
_out << " PUSH [tag" << i.data() << "]";
break;
case PushSub:
_out << " PUSH [$" << h256(i.m_data).abridged() << "]";
_out << " PUSH [$" << h256(i.data()).abridged() << "]";
break;
case PushSubSize:
_out << " PUSH #[$" << h256(i.m_data).abridged() << "]";
_out << " PUSH #[$" << h256(i.data()).abridged() << "]";
break;
case PushProgramSize:
_out << " PUSHSIZE";
break;
case Tag:
_out << "tag" << i.m_data << ": " << endl << _prefix << " JUMPDEST";
_out << "tag" << i.data() << ": " << endl << _prefix << " JUMPDEST";
break;
case PushData:
_out << " PUSH [" << hex << (unsigned)i.m_data << "]";
_out << " PUSH [" << hex << (unsigned)i.data() << "]";
break;
default:
BOOST_THROW_EXCEPTION(InvalidOpcode());
@ -187,20 +187,9 @@ Assembly& Assembly::optimise(bool _enable)
{
if (!_enable)
return *this;
std::vector<pair<AssemblyItems, function<AssemblyItems(AssemblyItemsConstRef)>>> rules =
{
{ { Push, Instruction::POP }, [](AssemblyItemsConstRef) -> AssemblyItems { return {}; } },
{ { PushTag, Instruction::POP }, [](AssemblyItemsConstRef) -> AssemblyItems { return {}; } },
{ { PushString, Instruction::POP }, [](AssemblyItemsConstRef) -> AssemblyItems { return {}; } },
{ { PushSub, Instruction::POP }, [](AssemblyItemsConstRef) -> AssemblyItems { return {}; } },
{ { PushSubSize, Instruction::POP }, [](AssemblyItemsConstRef) -> AssemblyItems { return {}; } },
{ { PushProgramSize, Instruction::POP }, [](AssemblyItemsConstRef) -> AssemblyItems { return {}; } },
{ { Push, PushTag, Instruction::JUMPI }, [](AssemblyItemsConstRef m) -> AssemblyItems { if (m[0].data()) return { m[1], Instruction::JUMP }; else return {}; } },
{ { Instruction::ISZERO, Instruction::ISZERO }, [](AssemblyItemsConstRef) -> AssemblyItems { return {}; } },
};
std::vector<pair<AssemblyItems, function<AssemblyItems(AssemblyItemsConstRef)>>> rules;
// jump to next instruction
rules.push_back({ { PushTag, Instruction::JUMP, Tag }, [](AssemblyItemsConstRef m) -> AssemblyItems { if (m[0].m_data == m[2].m_data) return {m[2]}; else return m.toVector(); }});
rules.push_back({ { PushTag, Instruction::JUMP, Tag }, [](AssemblyItemsConstRef m) -> AssemblyItems { if (m[0].data() == m[2].data()) return {m[2]}; else return m.toVector(); }});
unsigned total = 0;
for (unsigned count = 1; count > 0; total += count)
@ -235,8 +224,6 @@ Assembly& Assembly::optimise(bool _enable)
*orig = move(*moveIter);
iter = m_items.erase(orig, iter);
}
if (iter != m_items.end())
++iter;
}
for (unsigned i = 0; i < m_items.size(); ++i)
@ -344,16 +331,16 @@ bytes Assembly::assemble() const
// m_data must not change from here on
for (AssemblyItem const& i: m_items)
switch (i.m_type)
switch (i.type())
{
case Operation:
ret.push_back((byte)i.m_data);
ret.push_back((byte)i.data());
break;
case PushString:
{
ret.push_back((byte)Instruction::PUSH32);
unsigned ii = 0;
for (auto j: m_strings.at((h256)i.m_data))
for (auto j: m_strings.at((h256)i.data()))
if (++ii > 32)
break;
else
@ -364,30 +351,30 @@ bytes Assembly::assemble() const
}
case Push:
{
byte b = max<unsigned>(1, dev::bytesRequired(i.m_data));
byte b = max<unsigned>(1, dev::bytesRequired(i.data()));
ret.push_back((byte)Instruction::PUSH1 - 1 + b);
ret.resize(ret.size() + b);
bytesRef byr(&ret.back() + 1 - b, b);
toBigEndian(i.m_data, byr);
toBigEndian(i.data(), byr);
break;
}
case PushTag:
{
ret.push_back(tagPush);
tagRef[ret.size()] = (unsigned)i.m_data;
tagRef[ret.size()] = (unsigned)i.data();
ret.resize(ret.size() + bytesPerTag);
break;
}
case PushData: case PushSub:
{
ret.push_back(dataRefPush);
dataRef.insert(make_pair((h256)i.m_data, ret.size()));
dataRef.insert(make_pair((h256)i.data(), ret.size()));
ret.resize(ret.size() + bytesPerDataRef);
break;
}
case PushSubSize:
{
auto s = m_data[i.m_data].size();
auto s = m_data[i.data()].size();
byte b = max<unsigned>(1, dev::bytesRequired(s));
ret.push_back((byte)Instruction::PUSH1 - 1 + b);
ret.resize(ret.size() + b);
@ -403,7 +390,7 @@ bytes Assembly::assemble() const
break;
}
case Tag:
tagPos[(unsigned)i.m_data] = ret.size();
tagPos[(unsigned)i.data()] = ret.size();
ret.push_back((byte)Instruction::JUMPDEST);
break;
default:

2
libevmcore/Assembly.h

@ -65,7 +65,7 @@ public:
template <class T> Assembly& operator<<(T const& _d) { append(_d); return *this; }
AssemblyItems const& getItems() const { return m_items; }
AssemblyItem const& back() const { return m_items.back(); }
std::string backString() const { return m_items.size() && m_items.back().m_type == PushString ? m_strings.at((h256)m_items.back().m_data) : std::string(); }
std::string backString() const { return m_items.size() && m_items.back().type() == PushString ? m_strings.at((h256)m_items.back().data()) : std::string(); }
void onePath() { if (asserts(!m_totalDeposit && !m_baseDeposit)) BOOST_THROW_EXCEPTION(InvalidDeposit()); m_baseDeposit = m_deposit; m_totalDeposit = INT_MAX; }
void otherPath() { donePath(); m_totalDeposit = m_deposit; m_deposit = m_baseDeposit; }

5
libevmcore/AssemblyItem.h

@ -40,8 +40,6 @@ class Assembly;
class AssemblyItem
{
friend class Assembly;
public:
enum class JumpType { Ordinary, IntoFunction, OutOfFunction };
@ -54,6 +52,9 @@ public:
AssemblyItemType type() const { return m_type; }
u256 const& data() const { return m_data; }
void setType(AssemblyItemType const _type) { m_type = _type; }
void setData(u256 const& _data) { m_data = _data; }
/// @returns the instruction of this item (only valid if type() == Operation)
Instruction instruction() const { return Instruction(byte(m_data)); }

330
libevmcore/CommonSubexpressionEliminator.cpp

@ -24,7 +24,7 @@
#include <functional>
#include <boost/range/adaptor/reversed.hpp>
#include <libevmcore/CommonSubexpressionEliminator.h>
#include <libevmcore/Assembly.h>
#include <libevmcore/AssemblyItem.h>
using namespace std;
using namespace dev;
@ -32,6 +32,8 @@ using namespace dev::eth;
vector<AssemblyItem> CommonSubexpressionEliminator::getOptimizedItems()
{
optimizeBreakingItem();
map<int, ExpressionClasses::Id> initialStackContents;
map<int, ExpressionClasses::Id> targetStackContents;
int minHeight = m_stackHeight + 1;
@ -45,19 +47,27 @@ vector<AssemblyItem> CommonSubexpressionEliminator::getOptimizedItems()
// Debug info:
//stream(cout, initialStackContents, targetStackContents);
return CSECodeGenerator(m_expressionClasses).generateCode(initialStackContents, targetStackContents);
AssemblyItems items = CSECodeGenerator(m_expressionClasses, m_storeOperations).generateCode(
initialStackContents,
targetStackContents
);
if (m_breakingItem)
items.push_back(*m_breakingItem);
return items;
}
ostream& CommonSubexpressionEliminator::stream(
ostream& _out,
map<int, ExpressionClasses::Id> _currentStack,
map<int, ExpressionClasses::Id> _initialStack,
map<int, ExpressionClasses::Id> _targetStack
) const
{
auto streamExpressionClass = [this](ostream& _out, ExpressionClasses::Id _id)
{
auto const& expr = m_expressionClasses.representative(_id);
_out << " " << _id << ": " << *expr.item;
_out << " " << dec << _id << ": " << *expr.item;
if (expr.sequenceNumber)
_out << "@" << dec << expr.sequenceNumber;
_out << "(";
for (ExpressionClasses::Id arg: expr.arguments)
_out << dec << arg << ",";
@ -66,18 +76,12 @@ ostream& CommonSubexpressionEliminator::stream(
_out << "Optimizer analysis:" << endl;
_out << "Final stack height: " << dec << m_stackHeight << endl;
_out << "Stack elements: " << endl;
for (auto const& it: m_stackElements)
{
_out << " " << dec << it.first << " = ";
streamExpressionClass(_out, it.second);
}
_out << "Equivalence classes: " << endl;
for (ExpressionClasses::Id eqClass = 0; eqClass < m_expressionClasses.size(); ++eqClass)
streamExpressionClass(_out, eqClass);
_out << "Current stack: " << endl;
for (auto const& it: _currentStack)
_out << "Initial stack: " << endl;
for (auto const& it: _initialStack)
{
_out << " " << dec << it.first << ": ";
streamExpressionClass(_out, it.second);
@ -92,13 +96,12 @@ ostream& CommonSubexpressionEliminator::stream(
return _out;
}
void CommonSubexpressionEliminator::feedItem(AssemblyItem const& _item)
void CommonSubexpressionEliminator::feedItem(AssemblyItem const& _item, bool _copyItem)
{
if (_item.type() != Operation)
{
if (_item.deposit() != 1)
BOOST_THROW_EXCEPTION(InvalidDeposit());
setStackElement(++m_stackHeight, m_expressionClasses.find(_item, {}));
assertThrow(_item.deposit() == 1, InvalidDeposit, "");
setStackElement(++m_stackHeight, m_expressionClasses.find(_item, {}, _copyItem));
}
else
{
@ -119,12 +122,47 @@ void CommonSubexpressionEliminator::feedItem(AssemblyItem const& _item)
vector<ExpressionClasses::Id> arguments(info.args);
for (int i = 0; i < info.args; ++i)
arguments[i] = stackElement(m_stackHeight - i);
setStackElement(m_stackHeight + _item.deposit(), m_expressionClasses.find(_item, arguments));
if (_item.instruction() == Instruction::SSTORE)
storeInStorage(arguments[0], arguments[1]);
else if (_item.instruction() == Instruction::SLOAD)
setStackElement(m_stackHeight + _item.deposit(), loadFromStorage(arguments[0]));
else if (_item.instruction() == Instruction::MSTORE)
storeInMemory(arguments[0], arguments[1]);
else if (_item.instruction() == Instruction::MLOAD)
setStackElement(m_stackHeight + _item.deposit(), loadFromMemory(arguments[0]));
else
setStackElement(m_stackHeight + _item.deposit(), m_expressionClasses.find(_item, arguments, _copyItem));
}
m_stackHeight += _item.deposit();
}
}
void CommonSubexpressionEliminator::optimizeBreakingItem()
{
if (!m_breakingItem || *m_breakingItem != AssemblyItem(Instruction::JUMPI))
return;
using Id = ExpressionClasses::Id;
static AssemblyItem s_jump = Instruction::JUMP;
Id condition = stackElement(m_stackHeight - 1);
Id zero = m_expressionClasses.find(u256(0));
if (m_expressionClasses.knownToBeDifferent(condition, zero))
{
feedItem(Instruction::SWAP1, true);
feedItem(Instruction::POP, true);
m_breakingItem = &s_jump;
return;
}
Id negatedCondition = m_expressionClasses.find(Instruction::ISZERO, {condition});
if (m_expressionClasses.knownToBeDifferent(negatedCondition, zero))
{
feedItem(Instruction::POP, true);
feedItem(Instruction::POP, true);
m_breakingItem = nullptr;
}
}
void CommonSubexpressionEliminator::setStackElement(int _stackHeight, ExpressionClasses::Id _class)
{
m_stackElements[_stackHeight] = _class;
@ -132,8 +170,7 @@ void CommonSubexpressionEliminator::setStackElement(int _stackHeight, Expression
void CommonSubexpressionEliminator::swapStackElements(int _stackHeightA, int _stackHeightB)
{
if (_stackHeightA == _stackHeightB)
BOOST_THROW_EXCEPTION(OptimizerException() << errinfo_comment("Swap on same stack elements."));
assertThrow(_stackHeightA != _stackHeightB, OptimizerException, "Swap on same stack elements.");
// ensure they are created
stackElement(_stackHeightA);
stackElement(_stackHeightB);
@ -157,65 +194,68 @@ ExpressionClasses::Id CommonSubexpressionEliminator::initialStackElement(int _st
return m_expressionClasses.find(AssemblyItem(dupInstruction(1 - _stackHeight)));
}
bool SemanticInformation::breaksBasicBlock(AssemblyItem const& _item)
void CommonSubexpressionEliminator::storeInStorage(ExpressionClasses::Id _slot, ExpressionClasses::Id _value)
{
switch (_item.type())
{
default:
case UndefinedItem:
case Tag:
return true;
case Push:
case PushString:
case PushTag:
case PushSub:
case PushSubSize:
case PushProgramSize:
case PushData:
return false;
case Operation:
{
if (isSwapInstruction(_item) || isDupInstruction(_item))
return false;
if (_item.instruction() == Instruction::GAS || _item.instruction() == Instruction::PC)
return true; // GAS and PC assume a specific order of opcodes
InstructionInfo info = instructionInfo(_item.instruction());
// the second requirement will be lifted once it is implemented
return info.sideEffects || info.args > 2;
}
}
if (m_storageContent.count(_slot) && m_storageContent[_slot] == _value)
// do not execute the storage if we know that the value is already there
return;
m_sequenceNumber++;
decltype(m_storageContent) storageContents;
// copy over values at points where we know that they are different from _slot
for (auto const& storageItem: m_storageContent)
if (m_expressionClasses.knownToBeDifferent(storageItem.first, _slot))
storageContents.insert(storageItem);
m_storageContent = move(storageContents);
ExpressionClasses::Id id = m_expressionClasses.find(Instruction::SSTORE, {_slot, _value}, true, m_sequenceNumber);
m_storeOperations.push_back(StoreOperation(StoreOperation::Storage, _slot, m_sequenceNumber, id));
m_storageContent[_slot] = _value;
// increment a second time so that we get unique sequence numbers for writes
m_sequenceNumber++;
}
bool SemanticInformation::isCommutativeOperation(AssemblyItem const& _item)
ExpressionClasses::Id CommonSubexpressionEliminator::loadFromStorage(ExpressionClasses::Id _slot)
{
if (_item.type() != Operation)
return false;
switch (_item.instruction())
{
case Instruction::ADD:
case Instruction::MUL:
case Instruction::EQ:
case Instruction::AND:
case Instruction::OR:
case Instruction::XOR:
return true;
default:
return false;
}
if (m_storageContent.count(_slot))
return m_storageContent.at(_slot);
else
return m_storageContent[_slot] = m_expressionClasses.find(Instruction::SLOAD, {_slot}, true, m_sequenceNumber);
}
bool SemanticInformation::isDupInstruction(AssemblyItem const& _item)
void CommonSubexpressionEliminator::storeInMemory(ExpressionClasses::Id _slot, ExpressionClasses::Id _value)
{
if (_item.type() != Operation)
return false;
return Instruction::DUP1 <= _item.instruction() && _item.instruction() <= Instruction::DUP16;
if (m_memoryContent.count(_slot) && m_memoryContent[_slot] == _value)
// do not execute the store if we know that the value is already there
return;
m_sequenceNumber++;
decltype(m_memoryContent) memoryContents;
// copy over values at points where we know that they are different from _slot by at least 32
for (auto const& memoryItem: m_memoryContent)
if (m_expressionClasses.knownToBeDifferentBy32(memoryItem.first, _slot))
memoryContents.insert(memoryItem);
m_memoryContent = move(memoryContents);
ExpressionClasses::Id id = m_expressionClasses.find(Instruction::MSTORE, {_slot, _value}, true, m_sequenceNumber);
m_storeOperations.push_back(StoreOperation(StoreOperation::Memory, _slot, m_sequenceNumber, id));
m_memoryContent[_slot] = _value;
// increment a second time so that we get unique sequence numbers for writes
m_sequenceNumber++;
}
bool SemanticInformation::isSwapInstruction(AssemblyItem const& _item)
ExpressionClasses::Id CommonSubexpressionEliminator::loadFromMemory(ExpressionClasses::Id _slot)
{
if (_item.type() != Operation)
return false;
return Instruction::SWAP1 <= _item.instruction() && _item.instruction() <= Instruction::SWAP16;
if (m_memoryContent.count(_slot))
return m_memoryContent.at(_slot);
else
return m_memoryContent[_slot] = m_expressionClasses.find(Instruction::MLOAD, {_slot}, true, m_sequenceNumber);
}
CSECodeGenerator::CSECodeGenerator(
ExpressionClasses& _expressionClasses,
vector<CSECodeGenerator::StoreOperation> const& _storeOperations
):
m_expressionClasses(_expressionClasses)
{
for (auto const& store: _storeOperations)
m_storeOperations[make_pair(store.target, store.slot)].push_back(store);
}
AssemblyItems CSECodeGenerator::generateCode(
@ -230,26 +270,40 @@ AssemblyItems CSECodeGenerator::generateCode(
// @todo: provide information about the positions of copies of class elements
// generate the dependency graph
// generate the dependency graph starting from final storage and memory writes and target stack contents
for (auto const& p: m_storeOperations)
addDependencies(p.second.back().expression);
for (auto const& targetItem: _targetStackContents)
{
m_finalClasses.insert(targetItem.second);
addDependencies(targetItem.second);
}
// generate the actual elements
// store all needed sequenced expressions
set<pair<unsigned, ExpressionClasses::Id>> sequencedExpressions;
for (auto const& p: m_neededBy)
for (auto id: {p.first, p.second})
if (unsigned seqNr = m_expressionClasses.representative(id).sequenceNumber)
sequencedExpressions.insert(make_pair(seqNr, id));
// Perform all operations on storage and memory in order, if they are needed.
for (auto const& seqAndId: sequencedExpressions)
if (!m_classPositions.count(seqAndId.second))
generateClassElement(seqAndId.second, true);
// generate the target stack elements
for (auto const& targetItem: _targetStackContents)
{
removeStackTopIfPossible();
int position = generateClassElement(targetItem.second);
assertThrow(position != c_invalidPosition, OptimizerException, "");
if (position == targetItem.first)
continue;
if (position < targetItem.first)
// it is already at its target, we need another copy
appendDup(position);
else
appendSwapOrRemove(position);
appendSwapOrRemove(targetItem.first);
appendOrRemoveSwap(position);
appendOrRemoveSwap(targetItem.first);
}
// remove surplus elements
@ -270,23 +324,59 @@ AssemblyItems CSECodeGenerator::generateCode(
// neither initial no target stack, no change in height
finalHeight = 0;
assertThrow(finalHeight == m_stackHeight, OptimizerException, "Incorrect final stack height.");
return m_generatedItems;
}
void CSECodeGenerator::addDependencies(ExpressionClasses::Id _c)
{
if (m_neededBy.count(_c))
return;
for (ExpressionClasses::Id argument: m_expressionClasses.representative(_c).arguments)
return; // we already computed the dependencies for _c
ExpressionClasses::Expression expr = m_expressionClasses.representative(_c);
for (ExpressionClasses::Id argument: expr.arguments)
{
addDependencies(argument);
m_neededBy.insert(make_pair(argument, _c));
}
if (expr.item->type() == Operation && (
expr.item->instruction() == Instruction::SLOAD ||
expr.item->instruction() == Instruction::MLOAD
))
{
// this loads an unknown value from storage or memory and thus, in addition to its
// arguments, depends on all store operations to addresses where we do not know that
// they are different that occur before this load
StoreOperation::Target target = expr.item->instruction() == Instruction::SLOAD ?
StoreOperation::Storage : StoreOperation::Memory;
ExpressionClasses::Id slotToLoadFrom = expr.arguments.at(0);
for (auto const& p: m_storeOperations)
{
if (p.first.first != target)
continue;
ExpressionClasses::Id slot = p.first.second;
StoreOperations const& storeOps = p.second;
if (storeOps.front().sequenceNumber > expr.sequenceNumber)
continue;
if (
(target == StoreOperation::Memory && m_expressionClasses.knownToBeDifferentBy32(slot, slotToLoadFrom)) ||
(target == StoreOperation::Storage && m_expressionClasses.knownToBeDifferent(slot, slotToLoadFrom))
)
continue;
// note that store and load never have the same sequence number
ExpressionClasses::Id latestStore = storeOps.front().expression;
for (auto it = ++storeOps.begin(); it != storeOps.end(); ++it)
if (it->sequenceNumber < expr.sequenceNumber)
latestStore = it->expression;
addDependencies(latestStore);
m_neededBy.insert(make_pair(latestStore, _c));
}
}
}
int CSECodeGenerator::generateClassElement(ExpressionClasses::Id _c)
int CSECodeGenerator::generateClassElement(ExpressionClasses::Id _c, bool _allowSequenced)
{
// do some cleanup
removeStackTopIfPossible();
if (m_classPositions.count(_c))
{
assertThrow(
@ -296,7 +386,13 @@ int CSECodeGenerator::generateClassElement(ExpressionClasses::Id _c)
);
return m_classPositions[_c];
}
ExpressionClasses::Ids const& arguments = m_expressionClasses.representative(_c).arguments;
ExpressionClasses::Expression const& expr = m_expressionClasses.representative(_c);
assertThrow(
_allowSequenced || expr.sequenceNumber == 0,
OptimizerException,
"Sequence constrained operation requested out of sequence."
);
ExpressionClasses::Ids const& arguments = expr.arguments;
for (ExpressionClasses::Id arg: boost::adaptors::reverse(arguments))
generateClassElement(arg);
@ -307,42 +403,42 @@ int CSECodeGenerator::generateClassElement(ExpressionClasses::Id _c)
if (arguments.size() == 1)
{
if (canBeRemoved(arguments[0], _c))
appendSwapOrRemove(generateClassElement(arguments[0]));
appendOrRemoveSwap(classElementPosition(arguments[0]));
else
appendDup(generateClassElement(arguments[0]));
appendDup(classElementPosition(arguments[0]));
}
else if (arguments.size() == 2)
{
if (canBeRemoved(arguments[1], _c))
{
appendSwapOrRemove(generateClassElement(arguments[1]));
appendOrRemoveSwap(classElementPosition(arguments[1]));
if (arguments[0] == arguments[1])
appendDup(m_stackHeight);
else if (canBeRemoved(arguments[0], _c))
{
appendSwapOrRemove(m_stackHeight - 1);
appendSwapOrRemove(generateClassElement(arguments[0]));
appendOrRemoveSwap(m_stackHeight - 1);
appendOrRemoveSwap(classElementPosition(arguments[0]));
}
else
appendDup(generateClassElement(arguments[0]));
appendDup(classElementPosition(arguments[0]));
}
else
{
if (arguments[0] == arguments[1])
{
appendDup(generateClassElement(arguments[0]));
appendDup(classElementPosition(arguments[0]));
appendDup(m_stackHeight);
}
else if (canBeRemoved(arguments[0], _c))
{
appendSwapOrRemove(generateClassElement(arguments[0]));
appendDup(generateClassElement(arguments[1]));
appendSwapOrRemove(m_stackHeight - 1);
appendOrRemoveSwap(classElementPosition(arguments[0]));
appendDup(classElementPosition(arguments[1]));
appendOrRemoveSwap(m_stackHeight - 1);
}
else
{
appendDup(generateClassElement(arguments[1]));
appendDup(generateClassElement(arguments[0]));
appendDup(classElementPosition(arguments[1]));
appendDup(classElementPosition(arguments[0]));
}
}
}
@ -355,20 +451,41 @@ int CSECodeGenerator::generateClassElement(ExpressionClasses::Id _c)
for (size_t i = 0; i < arguments.size(); ++i)
assertThrow(m_stack[m_stackHeight - i] == arguments[i], OptimizerException, "Expected arguments not present." );
AssemblyItem const& item = *m_expressionClasses.representative(_c).item;
while (SemanticInformation::isCommutativeOperation(item) &&
while (SemanticInformation::isCommutativeOperation(*expr.item) &&
!m_generatedItems.empty() &&
m_generatedItems.back() == AssemblyItem(Instruction::SWAP1))
// this will not append a swap but remove the one that is already there
appendSwapOrRemove(m_stackHeight - 1);
appendOrRemoveSwap(m_stackHeight - 1);
for (auto arg: arguments)
if (canBeRemoved(arg, _c))
m_classPositions[arg] = c_invalidPosition;
for (size_t i = 0; i < arguments.size(); ++i)
m_stack.erase(m_stackHeight - i);
appendItem(*m_expressionClasses.representative(_c).item);
m_stack[m_stackHeight] = _c;
return m_classPositions[_c] = m_stackHeight;
appendItem(*expr.item);
if (expr.item->type() != Operation || instructionInfo(expr.item->instruction()).ret == 1)
{
m_stack[m_stackHeight] = _c;
return m_classPositions[_c] = m_stackHeight;
}
else
{
assertThrow(
instructionInfo(expr.item->instruction()).ret == 0,
OptimizerException,
"Invalid number of return values."
);
return m_classPositions[_c] = c_invalidPosition;
}
}
int CSECodeGenerator::classElementPosition(ExpressionClasses::Id _id) const
{
assertThrow(
m_classPositions.count(_id) && m_classPositions.at(_id) != c_invalidPosition,
OptimizerException,
"Element requested but is not present."
);
return m_classPositions.at(_id);
}
bool CSECodeGenerator::canBeRemoved(ExpressionClasses::Id _element, ExpressionClasses::Id _result)
@ -401,22 +518,23 @@ bool CSECodeGenerator::removeStackTopIfPossible()
void CSECodeGenerator::appendDup(int _fromPosition)
{
int nr = 1 + m_stackHeight - _fromPosition;
assertThrow(nr <= 16, StackTooDeepException, "Stack too deep.");
assertThrow(1 <= nr, OptimizerException, "Invalid stack access.");
m_generatedItems.push_back(AssemblyItem(dupInstruction(nr)));
m_stackHeight++;
assertThrow(_fromPosition != c_invalidPosition, OptimizerException, "");
int instructionNum = 1 + m_stackHeight - _fromPosition;
assertThrow(instructionNum <= 16, StackTooDeepException, "Stack too deep.");
assertThrow(1 <= instructionNum, OptimizerException, "Invalid stack access.");
appendItem(AssemblyItem(dupInstruction(instructionNum)));
m_stack[m_stackHeight] = m_stack[_fromPosition];
}
void CSECodeGenerator::appendSwapOrRemove(int _fromPosition)
void CSECodeGenerator::appendOrRemoveSwap(int _fromPosition)
{
assertThrow(_fromPosition != c_invalidPosition, OptimizerException, "");
if (_fromPosition == m_stackHeight)
return;
int nr = m_stackHeight - _fromPosition;
assertThrow(nr <= 16, StackTooDeepException, "Stack too deep.");
assertThrow(1 <= nr, OptimizerException, "Invalid stack access.");
m_generatedItems.push_back(AssemblyItem(swapInstruction(nr)));
int instructionNum = m_stackHeight - _fromPosition;
assertThrow(instructionNum <= 16, StackTooDeepException, "Stack too deep.");
assertThrow(1 <= instructionNum, OptimizerException, "Invalid stack access.");
appendItem(AssemblyItem(swapInstruction(instructionNum)));
// The value of a class can be present in multiple locations on the stack. We only update the
// "canonical" one that is tracked by m_classPositions
if (m_classPositions[m_stack[m_stackHeight]] == m_stackHeight)

99
libevmcore/CommonSubexpressionEliminator.h

@ -25,10 +25,13 @@
#include <vector>
#include <map>
#include <set>
#include <tuple>
#include <ostream>
#include <libdevcore/CommonIO.h>
#include <libdevcore/Exceptions.h>
#include <libevmcore/ExpressionClasses.h>
#include <libevmcore/SemanticInformation.h>
namespace dev
{
@ -44,9 +47,9 @@ using AssemblyItems = std::vector<AssemblyItem>;
* known to be equal only once.
*
* The general workings are that for each assembly item that is fed into the eliminator, an
* equivalence class is derived from the operation and the equivalence class of its arguments and
* it is assigned to the next sequence number of a stack item. DUPi, SWAPi and some arithmetic
* instructions are used to infer equivalences while these classes are determined.
* equivalence class is derived from the operation and the equivalence class of its arguments.
* DUPi, SWAPi and some arithmetic instructions are used to infer equivalences while these
* classes are determined.
*
* When the list of optimized items is requested, they are generated in a bottom-up fashion,
* adding code for equivalence classes that were not yet computed.
@ -54,6 +57,21 @@ using AssemblyItems = std::vector<AssemblyItem>;
class CommonSubexpressionEliminator
{
public:
struct StoreOperation
{
enum Target { Memory, Storage };
StoreOperation(
Target _target,
ExpressionClasses::Id _slot,
unsigned _sequenceNumber,
ExpressionClasses::Id _expression
): target(_target), slot(_slot), sequenceNumber(_sequenceNumber), expression(_expression) {}
Target target;
ExpressionClasses::Id slot;
unsigned sequenceNumber;
ExpressionClasses::Id expression;
};
/// Feeds AssemblyItems into the eliminator and @returns the iterator pointing at the first
/// item that must be fed into a new instance of the eliminator.
template <class _AssemblyItemIterator>
@ -65,13 +83,16 @@ public:
/// Streams debugging information to @a _out.
std::ostream& stream(
std::ostream& _out,
std::map<int, ExpressionClasses::Id> _currentStack = std::map<int, ExpressionClasses::Id>(),
std::map<int, ExpressionClasses::Id> _initialStack = std::map<int, ExpressionClasses::Id>(),
std::map<int, ExpressionClasses::Id> _targetStack = std::map<int, ExpressionClasses::Id>()
) const;
private:
/// Feeds the item into the system for analysis.
void feedItem(AssemblyItem const& _item);
void feedItem(AssemblyItem const& _item, bool _copyItem = false);
/// Tries to optimize the item that breaks the basic block at the end.
void optimizeBreakingItem();
/// Simplifies the given item using
/// Assigns a new equivalence class to the next sequence number of the given stack element.
@ -85,26 +106,38 @@ private:
/// (must not be positive).
ExpressionClasses::Id initialStackElement(int _stackHeight);
/// Increments the sequence number, deletes all storage information that might be overwritten
/// and stores the new value at the given slot.
void storeInStorage(ExpressionClasses::Id _slot, ExpressionClasses::Id _value);
/// Retrieves the current value at the given slot in storage or creates a new special sload class.
ExpressionClasses::Id loadFromStorage(ExpressionClasses::Id _slot);
/// Increments the sequence number, deletes all memory information that might be overwritten
/// and stores the new value at the given slot.
void storeInMemory(ExpressionClasses::Id _slot, ExpressionClasses::Id _value);
/// Retrieves the current value at the given slot in memory or creates a new special mload class.
ExpressionClasses::Id loadFromMemory(ExpressionClasses::Id _slot);
/// Current stack height, can be negative.
int m_stackHeight = 0;
/// Current stack layout, mapping stack height -> equivalence class
std::map<int, ExpressionClasses::Id> m_stackElements;
/// Current sequence number, this is incremented with each modification to storage or memory.
unsigned m_sequenceNumber = 1;
/// Knowledge about storage content.
std::map<ExpressionClasses::Id, ExpressionClasses::Id> m_storageContent;
/// Knowledge about memory content. Keys are memory addresses, note that the values overlap
/// and are not contained here if they are not completely known.
std::map<ExpressionClasses::Id, ExpressionClasses::Id> m_memoryContent;
/// Keeps information about which storage or memory slots were written to at which sequence
/// number with what instruction.
std::vector<StoreOperation> m_storeOperations;
/// Structure containing the classes of equivalent expressions.
ExpressionClasses m_expressionClasses;
};
/**
* Helper functions to provide context-independent information about assembly items.
*/
struct SemanticInformation
{
/// @returns true if the given items starts a new basic block
static bool breaksBasicBlock(AssemblyItem const& _item);
/// @returns true if the item is a two-argument operation whose value does not depend on the
/// order of its arguments.
static bool isCommutativeOperation(AssemblyItem const& _item);
static bool isDupInstruction(AssemblyItem const& _item);
static bool isSwapInstruction(AssemblyItem const& _item);
/// The item that breaks the basic block, can be nullptr.
/// It is usually appended to the block but can be optimized in some cases.
AssemblyItem const* m_breakingItem = nullptr;
};
/**
@ -114,14 +147,16 @@ struct SemanticInformation
class CSECodeGenerator
{
public:
CSECodeGenerator(ExpressionClasses const& _expressionClasses):
m_expressionClasses(_expressionClasses)
{}
using StoreOperation = CommonSubexpressionEliminator::StoreOperation;
using StoreOperations = std::vector<StoreOperation>;
/// Initializes the code generator with the given classes and store operations.
/// The store operations have to be sorted by sequence number in ascending order.
CSECodeGenerator(ExpressionClasses& _expressionClasses, StoreOperations const& _storeOperations);
/// @returns the assembly items generated from the given requirements
/// @param _initialStack current contents of the stack (up to stack height of zero)
/// @param _targetStackContents final contents of the stack, by stack height relative to initial
/// @param _equivalenceClasses equivalence classes as expressions of how to compute them
/// @note should only be called once on each object.
AssemblyItems generateCode(
std::map<int, ExpressionClasses::Id> const& _initialStack,
@ -133,8 +168,13 @@ private:
void addDependencies(ExpressionClasses::Id _c);
/// Produce code that generates the given element if it is not yet present.
/// @returns the stack position of the element.
int generateClassElement(ExpressionClasses::Id _c);
/// @returns the stack position of the element or c_invalidPosition if it does not actually
/// generate a value on the stack.
/// @param _allowSequenced indicates that sequence-constrained operations are allowed
int generateClassElement(ExpressionClasses::Id _c, bool _allowSequenced = false);
/// @returns the position of the representative of the given id on the stack.
/// @note throws an exception if it is not on the stack.
int classElementPosition(ExpressionClasses::Id _id) const;
/// @returns true if @a _element can be removed - in general or, if given, while computing @a _result.
bool canBeRemoved(ExpressionClasses::Id _element, ExpressionClasses::Id _result = ExpressionClasses::Id(-1));
@ -146,7 +186,7 @@ private:
void appendDup(int _fromPosition);
/// Appends a swap instruction to m_generatedItems to retrieve the element at the given stack position.
/// @note this might also remove the last item if it exactly the same swap instruction.
void appendSwapOrRemove(int _fromPosition);
void appendOrRemoveSwap(int _fromPosition);
/// Appends the given assembly item.
void appendItem(AssemblyItem const& _item);
@ -163,7 +203,10 @@ private:
std::map<ExpressionClasses::Id, int> m_classPositions;
/// The actual eqivalence class items and how to compute them.
ExpressionClasses const& m_expressionClasses;
ExpressionClasses& m_expressionClasses;
/// Keeps information about which storage or memory slots were written to by which operations.
/// The operations are sorted ascendingly by sequence number.
std::map<std::pair<StoreOperation::Target, ExpressionClasses::Id>, StoreOperations> m_storeOperations;
/// The set of equivalence classes that should be present on the stack at the end.
std::set<ExpressionClasses::Id> m_finalClasses;
};
@ -174,8 +217,10 @@ _AssemblyItemIterator CommonSubexpressionEliminator::feedItems(
_AssemblyItemIterator _end
)
{
for (; _iterator != _end && !SemanticInformation::breaksBasicBlock(*_iterator); ++_iterator)
for (; _iterator != _end && !SemanticInformation::breaksCSEAnalysisBlock(*_iterator); ++_iterator)
feedItem(*_iterator);
if (_iterator != _end)
m_breakingItem = &(*_iterator++);
return _iterator;
}

100
libevmcore/ExpressionClasses.cpp

@ -39,41 +39,71 @@ bool ExpressionClasses::Expression::operator<(ExpressionClasses::Expression cons
{
auto type = item->type();
auto otherType = _other.item->type();
return std::tie(type, item->data(), arguments) <
std::tie(otherType, _other.item->data(), _other.arguments);
return std::tie(type, item->data(), arguments, sequenceNumber) <
std::tie(otherType, _other.item->data(), _other.arguments, _other.sequenceNumber);
}
ExpressionClasses::Id ExpressionClasses::find(AssemblyItem const& _item, Ids const& _arguments)
ExpressionClasses::Id ExpressionClasses::find(
AssemblyItem const& _item,
Ids const& _arguments,
bool _copyItem,
unsigned _sequenceNumber
)
{
Expression exp;
exp.id = Id(-1);
exp.item = &_item;
exp.arguments = _arguments;
exp.sequenceNumber = _sequenceNumber;
if (SemanticInformation::isCommutativeOperation(_item))
sort(exp.arguments.begin(), exp.arguments.end());
//@todo store all class members (not only the representatives) in an efficient data structure to search here
for (Expression const& e: m_representatives)
if (!(e < exp || exp < e))
return e.id;
auto it = m_expressions.find(exp);
if (it != m_expressions.end())
return it->id;
if (SemanticInformation::isDupInstruction(_item))
if (_copyItem)
{
// Special item that refers to values pre-existing on the stack
m_spareAssemblyItem.push_back(make_shared<AssemblyItem>(_item));
exp.item = m_spareAssemblyItem.back().get();
}
ExpressionClasses::Id id = tryToSimplify(exp);
if (id < m_representatives.size())
return id;
exp.id = m_representatives.size();
m_representatives.push_back(exp);
exp.id = id;
else
{
exp.id = m_representatives.size();
m_representatives.push_back(exp);
}
m_expressions.insert(exp);
return exp.id;
}
bool ExpressionClasses::knownToBeDifferent(ExpressionClasses::Id _a, ExpressionClasses::Id _b)
{
// Try to simplify "_a - _b" and return true iff the value is a non-zero constant.
map<unsigned, Expression const*> matchGroups;
Pattern constant(Push);
constant.setMatchGroup(1, matchGroups);
Id difference = find(Instruction::SUB, {_a, _b});
return constant.matches(representative(difference), *this) && constant.d() != u256(0);
}
bool ExpressionClasses::knownToBeDifferentBy32(ExpressionClasses::Id _a, ExpressionClasses::Id _b)
{
// Try to simplify "_a - _b" and return true iff the value is at least 32 away from zero.
map<unsigned, Expression const*> matchGroups;
Pattern constant(Push);
constant.setMatchGroup(1, matchGroups);
Id difference = find(Instruction::SUB, {_a, _b});
if (!constant.matches(representative(difference), *this))
return false;
// forbidden interval is ["-31", 31]
return constant.d() + 31 > u256(62);
}
string ExpressionClasses::fullDAGToString(ExpressionClasses::Id _id) const
{
Expression const& expr = representative(_id);
@ -189,27 +219,46 @@ Rules::Rules()
// Moving constants to the outside, order matters here!
// we need actions that return expressions (or patterns?) here, and we need also reversed rules
// (X+A)+B -> X+(A+B)
m_rules.push_back({
m_rules += vector<pair<Pattern, function<Pattern()>>>{{
{op, {{op, {X, A}}, B}},
[=]() -> Pattern { return {op, {X, fun(A.d(), B.d())}}; }
});
}, {
// X+(Y+A) -> (X+Y)+A
m_rules.push_back({
{op, {{op, {X, A}}, Y}},
[=]() -> Pattern { return {op, {{op, {X, Y}}, A}}; }
});
}, {
// For now, we still need explicit commutativity for the inner pattern
m_rules.push_back({
{op, {{op, {A, X}}, B}},
[=]() -> Pattern { return {op, {X, fun(A.d(), B.d())}}; }
});
m_rules.push_back({
}, {
{op, {{op, {A, X}}, Y}},
[=]() -> Pattern { return {op, {{op, {X, Y}}, A}}; }
});
}};
}
// move constants across subtractions
m_rules += vector<pair<Pattern, function<Pattern()>>>{
{
// X - A -> X + (-A)
{Instruction::SUB, {X, A}},
[=]() -> Pattern { return {Instruction::ADD, {X, 0 - A.d()}}; }
}, {
// (X + A) - Y -> (X - Y) + A
{Instruction::SUB, {{Instruction::ADD, {X, A}}, Y}},
[=]() -> Pattern { return {Instruction::ADD, {{Instruction::SUB, {X, Y}}, A}}; }
}, {
// (A + X) - Y -> (X - Y) + A
{Instruction::SUB, {{Instruction::ADD, {A, X}}, Y}},
[=]() -> Pattern { return {Instruction::ADD, {{Instruction::SUB, {X, Y}}, A}}; }
}, {
// X - (Y + A) -> (X - Y) + (-A)
{Instruction::SUB, {X, {Instruction::ADD, {Y, A}}}},
[=]() -> Pattern { return {Instruction::ADD, {{Instruction::SUB, {X, Y}}, 0 - A.d()}}; }
}, {
// X - (A + Y) -> (X - Y) + (-A)
{Instruction::SUB, {X, {Instruction::ADD, {A, Y}}}},
[=]() -> Pattern { return {Instruction::ADD, {{Instruction::SUB, {X, Y}}, 0 - A.d()}}; }
}
};
//@todo: (x+8)-3 and other things
}
ExpressionClasses::Id ExpressionClasses::tryToSimplify(Expression const& _expr, bool _secondRun)
@ -231,7 +280,7 @@ ExpressionClasses::Id ExpressionClasses::tryToSimplify(Expression const& _expr,
//cout << ")" << endl;
//cout << "with rule " << rule.first.toString() << endl;
//ExpressionTemplate t(rule.second());
//cout << "to" << rule.second().toString() << endl;
//cout << "to " << rule.second().toString() << endl;
return rebuildExpression(ExpressionTemplate(rule.second()));
}
}
@ -254,8 +303,7 @@ ExpressionClasses::Id ExpressionClasses::rebuildExpression(ExpressionTemplate co
Ids arguments;
for (ExpressionTemplate const& t: _template.arguments)
arguments.push_back(rebuildExpression(t));
m_spareAssemblyItem.push_back(make_shared<AssemblyItem>(_template.item));
return find(*m_spareAssemblyItem.back(), arguments);
return find(_template.item, arguments);
}

20
libevmcore/ExpressionClasses.h

@ -52,17 +52,33 @@ public:
Id id;
AssemblyItem const* item;
Ids arguments;
unsigned sequenceNumber; ///< Storage modification sequence, only used for SLOAD/SSTORE instructions.
/// Behaves as if this was a tuple of (item->type(), item->data(), arguments, sequenceNumber).
bool operator<(Expression const& _other) const;
};
/// Retrieves the id of the expression equivalence class resulting from the given item applied to the
/// given classes, might also create a new one.
Id find(AssemblyItem const& _item, Ids const& _arguments = {});
/// @param _copyItem if true, copies the assembly item to an internal storage instead of just
/// keeping a pointer.
/// The @a _sequenceNumber indicates the current storage or memory access sequence.
Id find(
AssemblyItem const& _item,
Ids const& _arguments = {},
bool _copyItem = true,
unsigned _sequenceNumber = 0
);
/// @returns the canonical representative of an expression class.
Expression const& representative(Id _id) const { return m_representatives.at(_id); }
/// @returns the number of classes.
Id size() const { return m_representatives.size(); }
/// @returns true if the values of the given classes are known to be different (on every input).
/// @note that this function might still return false for some different inputs.
bool knownToBeDifferent(Id _a, Id _b);
/// Similar to @a knownToBeDifferent but require that abs(_a - b) >= 32.
bool knownToBeDifferentBy32(Id _a, Id _b);
std::string fullDAGToString(Id _id) const;
private:
@ -78,6 +94,8 @@ private:
/// Expression equivalence class representatives - we only store one item of an equivalence.
std::vector<Expression> m_representatives;
/// All expression ever encountered.
std::set<Expression> m_expressions;
std::vector<std::shared_ptr<AssemblyItem>> m_spareAssemblyItem;
};

107
libevmcore/SemanticInformation.cpp

@ -0,0 +1,107 @@
/*
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 SemanticInformation.cpp
* @author Christian <c@ethdev.com>
* @date 2015
* Helper to provide semantic information about assembly items.
*/
#include <libevmcore/SemanticInformation.h>
#include <libevmcore/AssemblyItem.h>
using namespace std;
using namespace dev;
using namespace dev::eth;
bool SemanticInformation::breaksCSEAnalysisBlock(AssemblyItem const& _item)
{
switch (_item.type())
{
default:
case UndefinedItem:
case Tag:
return true;
case Push:
case PushString:
case PushTag:
case PushSub:
case PushSubSize:
case PushProgramSize:
case PushData:
return false;
case Operation:
{
if (isSwapInstruction(_item) || isDupInstruction(_item))
return false;
if (_item.instruction() == Instruction::GAS || _item.instruction() == Instruction::PC)
return true; // GAS and PC assume a specific order of opcodes
if (_item.instruction() == Instruction::MSIZE)
return true; // msize is modified already by memory access, avoid that for now
if (_item.instruction() == Instruction::SHA3)
return true; //@todo: we have to compare sha3's not based on their memory addresses but on the memory content.
InstructionInfo info = instructionInfo(_item.instruction());
if (_item.instruction() == Instruction::SSTORE)
return false;
if (_item.instruction() == Instruction::MSTORE)
return false;
//@todo: We do not handle the following memory instructions for now:
// calldatacopy, codecopy, extcodecopy, mstore8,
// msize (note that msize also depends on memory read access)
// the second requirement will be lifted once it is implemented
return info.sideEffects || info.args > 2;
}
}
}
bool SemanticInformation::isCommutativeOperation(AssemblyItem const& _item)
{
if (_item.type() != Operation)
return false;
switch (_item.instruction())
{
case Instruction::ADD:
case Instruction::MUL:
case Instruction::EQ:
case Instruction::AND:
case Instruction::OR:
case Instruction::XOR:
return true;
default:
return false;
}
}
bool SemanticInformation::isDupInstruction(AssemblyItem const& _item)
{
if (_item.type() != Operation)
return false;
return Instruction::DUP1 <= _item.instruction() && _item.instruction() <= Instruction::DUP16;
}
bool SemanticInformation::isSwapInstruction(AssemblyItem const& _item)
{
if (_item.type() != Operation)
return false;
return Instruction::SWAP1 <= _item.instruction() && _item.instruction() <= Instruction::SWAP16;
}
bool SemanticInformation::isJumpInstruction(AssemblyItem const& _item)
{
return _item == AssemblyItem(Instruction::JUMP) || _item == AssemblyItem(Instruction::JUMPI);
}

50
libevmcore/SemanticInformation.h

@ -0,0 +1,50 @@
/*
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 SemanticInformation.h
* @author Christian <c@ethdev.com>
* @date 2015
* Helper to provide semantic information about assembly items.
*/
#pragma once
namespace dev
{
namespace eth
{
class AssemblyItem;
/**
* Helper functions to provide context-independent information about assembly items.
*/
struct SemanticInformation
{
/// @returns true if the given items starts a new block for common subexpression analysis.
static bool breaksCSEAnalysisBlock(AssemblyItem const& _item);
/// @returns true if the item is a two-argument operation whose value does not depend on the
/// order of its arguments.
static bool isCommutativeOperation(AssemblyItem const& _item);
static bool isDupInstruction(AssemblyItem const& _item);
static bool isSwapInstruction(AssemblyItem const& _item);
static bool isJumpInstruction(AssemblyItem const& _item);
};
}
}

69
libp2p/Host.cpp

@ -678,9 +678,6 @@ void Host::disconnectLatePeers()
bytes Host::saveNetwork() const
{
if (!m_nodeTable)
return bytes();
std::list<Peer> peers;
{
RecursiveGuard l(x_sessions);
@ -692,27 +689,22 @@ bytes Host::saveNetwork() const
RLPStream network;
int count = 0;
for (auto const& p: peers)
{
RecursiveGuard l(x_sessions);
for (auto const& p: peers)
// Only save peers which have connected within 2 days, with properly-advertised port and public IP address
// todo: e2e ipv6 support
bi::tcp::endpoint endpoint(p.peerEndpoint());
if (!endpoint.address().is_v4())
continue;
if (chrono::system_clock::now() - p.m_lastConnected < chrono::seconds(3600 * 48) && endpoint.port() > 0 && endpoint.port() < /*49152*/32768 && p.id != id() && !isPrivateAddress(p.endpoint.udp.address()) && !isPrivateAddress(endpoint.address()))
{
// TODO: alpha: Figure out why it ever shares these ports.//p.address.port() >= 30300 && p.address.port() <= 30305 &&
// TODO: alpha: if/how to save private addresses
// Only save peers which have connected within 2 days, with properly-advertised port and public IP address
if (chrono::system_clock::now() - p.m_lastConnected < chrono::seconds(3600 * 48) && p.peerEndpoint().port() > 0 && p.peerEndpoint().port() < /*49152*/32768 && p.id != id() && !isPrivateAddress(p.endpoint.udp.address()) && !isPrivateAddress(p.endpoint.tcp.address()))
{
network.appendList(10);
if (p.peerEndpoint().address().is_v4())
network << p.peerEndpoint().address().to_v4().to_bytes();
else
network << p.peerEndpoint().address().to_v6().to_bytes();
// TODO: alpha: replace 0 with trust-state of node
network << p.peerEndpoint().port() << p.id << 0
<< chrono::duration_cast<chrono::seconds>(p.m_lastConnected.time_since_epoch()).count()
<< chrono::duration_cast<chrono::seconds>(p.m_lastAttempted.time_since_epoch()).count()
<< p.m_failedAttempts << (unsigned)p.m_lastDisconnect << p.m_score << p.m_rating;
count++;
}
network.appendList(10);
network << endpoint.port() << p.id << p.required
<< chrono::duration_cast<chrono::seconds>(p.m_lastConnected.time_since_epoch()).count()
<< chrono::duration_cast<chrono::seconds>(p.m_lastAttempted.time_since_epoch()).count()
<< p.m_failedAttempts << (unsigned)p.m_lastDisconnect << p.m_score << p.m_rating;
count++;
}
}
@ -731,10 +723,13 @@ bytes Host::saveNetwork() const
count++;
}
}
// else: TODO: use previous configuration if available
RLPStream ret(3);
ret << dev::p2p::c_protocolVersion << m_alias.secret();
ret.appendList(count).appendRaw(network.out(), count);
ret.appendList(count);
if (!!count)
ret.appendRaw(network.out(), count);
return ret.out();
}
@ -757,22 +752,14 @@ void Host::restoreNetwork(bytesConstRef _b)
for (auto i: r[2])
{
// todo: e2e ipv6 support
// bi::tcp::endpoint(bi::address_v6(i[0].toArray<byte, 16>()), i[1].toInt<short>());
if (i[0].itemCount() != 4)
continue;
bi::tcp::endpoint tcp;
bi::udp::endpoint udp;
if (i[0].itemCount() == 4)
{
tcp = bi::tcp::endpoint(bi::address_v4(i[0].toArray<byte, 4>()), i[1].toInt<short>());
udp = bi::udp::endpoint(bi::address_v4(i[0].toArray<byte, 4>()), i[1].toInt<short>());
}
else
{
tcp = bi::tcp::endpoint(bi::address_v6(i[0].toArray<byte, 16>()), i[1].toInt<short>());
udp = bi::udp::endpoint(bi::address_v6(i[0].toArray<byte, 16>()), i[1].toInt<short>());
}
// skip private addresses
// todo: to support private addresseses entries must be stored
// and managed externally by host rather than nodetable.
tcp = bi::tcp::endpoint(bi::address_v4(i[0].toArray<byte, 4>()), i[1].toInt<short>());
udp = bi::udp::endpoint(bi::address_v4(i[0].toArray<byte, 4>()), i[1].toInt<short>());
if (isPrivateAddress(tcp.address()) || isPrivateAddress(udp.address()))
continue;
@ -783,6 +770,7 @@ void Host::restoreNetwork(bytesConstRef _b)
{
shared_ptr<Peer> p = make_shared<Peer>();
p->id = id;
p->required = i[3].toInt<bool>();
p->m_lastConnected = chrono::system_clock::time_point(chrono::seconds(i[4].toInt<unsigned>()));
p->m_lastAttempted = chrono::system_clock::time_point(chrono::seconds(i[5].toInt<unsigned>()));
p->m_failedAttempts = i[6].toInt<unsigned>();
@ -792,7 +780,10 @@ void Host::restoreNetwork(bytesConstRef _b)
p->endpoint.tcp = tcp;
p->endpoint.udp = udp;
m_peers[p->id] = p;
m_nodeTable->addNode(*p.get());
if (p->required)
requirePeer(p->id, p->endpoint.udp.address(), p->endpoint.udp.port());
else
m_nodeTable->addNode(*p.get());
}
}
}
@ -801,7 +792,7 @@ void Host::restoreNetwork(bytesConstRef _b)
KeyPair Host::networkAlias(bytesConstRef _b)
{
RLP r(_b);
if (r.itemCount() == 3 && r[0].isInt() && r[0].toInt<int>() == 1)
if (r.itemCount() == 3 && r[0].isInt() && r[0].toInt<unsigned>() == dev::p2p::c_protocolVersion)
return move(KeyPair(move(Secret(r[1].toBytes()))));
else
return move(KeyPair::create());

16
libsolidity/AST.cpp

@ -337,8 +337,14 @@ void FunctionDefinition::checkTypeRequirements()
{
if (!var->getType()->canLiveOutsideStorage())
BOOST_THROW_EXCEPTION(var->createTypeError("Type is required to live outside storage."));
if (!(var->getType()->externalType()) && getVisibility() >= Visibility::Public)
BOOST_THROW_EXCEPTION(var->createTypeError("Internal type is not allowed for function with external visibility"));
if (getVisibility() >= Visibility::Public && !(var->getType()->externalType()))
{
// todo delete when will be implemented arrays as parameter type in internal functions
if (getVisibility() == Visibility::Public && var->getType()->getCategory() == Type::Category::Array)
BOOST_THROW_EXCEPTION(var->createTypeError("Arrays only implemented for external functions."));
else
BOOST_THROW_EXCEPTION(var->createTypeError("Internal type is not allowed for public and external functions."));
}
}
for (ASTPointer<ModifierInvocation> const& modifier: m_functionModifiers)
modifier->checkTypeRequirements(isConstructor() ?
@ -379,7 +385,11 @@ void VariableDeclaration::checkTypeRequirements()
m_value->expectType(*m_type);
if (m_isStateVariable && !m_type->externalType() && getVisibility() >= Visibility::Public)
BOOST_THROW_EXCEPTION(createTypeError("Internal type is not allowed for state variables."));
} else
if (!FunctionType(*this).externalType())
BOOST_THROW_EXCEPTION(createTypeError("Internal type is not allowed for public state variables."));
}
else
{
// no type declared and no previous assignment, infer the type
m_value->checkTypeRequirements();

17
libsolidity/Types.cpp

@ -1103,11 +1103,18 @@ TypePointer FunctionType::externalType() const
TypePointers paramTypes;
TypePointers retParamTypes;
for (auto it = m_parameterTypes.cbegin(); it != m_parameterTypes.cend(); ++it)
paramTypes.push_back((*it)->externalType());
for (auto it = m_returnParameterTypes.cbegin(); it != m_returnParameterTypes.cend(); ++it)
retParamTypes.push_back((*it)->externalType());
for (auto type: m_parameterTypes)
{
if (!type->externalType())
return TypePointer();
paramTypes.push_back(type->externalType());
}
for (auto type: m_returnParameterTypes)
{
if (!type->externalType())
return TypePointer();
retParamTypes.push_back(type->externalType());
}
return make_shared<FunctionType>(paramTypes, retParamTypes, m_location, m_arbitraryParameters);
}

2
libsolidity/Types.h

@ -512,6 +512,8 @@ public:
virtual Category getCategory() const override { return Category::Function; }
/// @returns TypePointer of a new FunctionType object. All input/return parameters are an appropriate external types of input/return parameters of current function.
/// Returns an empty shared pointer if one of the input/return parameters does not have an externaltype.
virtual TypePointer externalType() const override;
explicit FunctionType(FunctionDefinition const& _function, bool _isInternal = true);

2
libweb3jsonrpc/WebThreeStubServerBase.cpp

@ -402,7 +402,7 @@ string WebThreeStubServerBase::eth_getCode(string const& _address, string const&
{
try
{
return toJS(client()->codeAt(jsToAddress(_address), toBlockNumber(_blockNumber)), 1);
return toJS(client()->codeAt(jsToAddress(_address), toBlockNumber(_blockNumber)));
}
catch (...)
{

21
mix/CodeModel.cpp

@ -93,8 +93,23 @@ private:
bool m_functionScope;
uint m_storageSlot;
};
dev::eth::AssemblyItems filterLocations(dev::eth::AssemblyItems const& _locations, dev::solidity::ContractDefinition const& _contract, QHash<LocationPair, QString> _functions)
{
dev::eth::AssemblyItems result;
result.reserve(_locations.size());
for (dev::eth::AssemblyItem item : _locations)
{
dev::SourceLocation const& l = item.getLocation();
if (_contract.getLocation() == l || _functions.contains(LocationPair(l.start, l.end)))
item.setLocation(dev::SourceLocation(-1, -1, l.sourceName));
result.push_back(item);
}
return result;
}
} //namespace
void BackgroundWorker::queueCodeChange(int _jobId)
{
m_model->runCompilationJob(_jobId);
@ -105,13 +120,11 @@ CompiledContract::CompiledContract(const dev::solidity::CompilerStack& _compiler
m_sourceHash(qHash(_source))
{
std::string name = _contractName.toStdString();
auto const& contractDefinition = _compiler.getContractDefinition(name);
ContractDefinition const& contractDefinition = _compiler.getContractDefinition(name);
m_contract.reset(new QContractDefinition(nullptr, &contractDefinition));
QQmlEngine::setObjectOwnership(m_contract.get(), QQmlEngine::CppOwnership);
m_contract->moveToThread(QApplication::instance()->thread());
m_bytes = _compiler.getBytecode(_contractName.toStdString());
m_assemblyItems = _compiler.getRuntimeAssemblyItems(name);
m_constructorAssemblyItems = _compiler.getAssemblyItems(name);
dev::solidity::InterfaceHandler interfaceHandler;
m_contractInterface = QString::fromStdString(*interfaceHandler.getABIInterface(contractDefinition));
@ -122,6 +135,8 @@ CompiledContract::CompiledContract(const dev::solidity::CompilerStack& _compiler
CollectDeclarationsVisitor visitor(&m_functions, &m_locals, &m_storage);
contractDefinition.accept(visitor);
m_assemblyItems = filterLocations(_compiler.getRuntimeAssemblyItems(name), contractDefinition, m_functions);
m_constructorAssemblyItems = filterLocations(_compiler.getAssemblyItems(name), contractDefinition, m_functions);
}
QString CompiledContract::codeHex() const

3
mix/qml.qrc

@ -48,7 +48,8 @@
<file>qml/StatusPaneStyle.qml</file>
<file>qml/StepActionImage.qml</file>
<file>qml/StorageView.qml</file>
<file>qml/StructView.qml</file>
<file>qml/StatesComboBox.qml</file>
<file>qml/StructView.qml</file>
<file>qml/Style.qml</file>
<file>qml/TabStyle.qml</file>
<file>qml/TransactionDialog.qml</file>

1
mix/qml/CodeEditorView.qml

@ -13,6 +13,7 @@ Item {
signal isCleanChanged(var isClean, string documentId)
signal loadComplete
function getDocumentText(documentId) {
for (var i = 0; i < openDocCount; i++) {
if (editorListModel.get(i).documentId === documentId) {

27
mix/qml/Debugger.qml

@ -223,6 +223,33 @@ Rectangle {
anchors.horizontalCenter: parent.horizontalCenter
id: jumpButtons
spacing: 3
StepActionImage
{
id: playAction
enabledStateImg: "qrc:/qml/img/play_button.png"
disableStateImg: "qrc:/qml/img/play_button.png"
onClicked: projectModel.stateListModel.runState(transactionLog.selectedStateIndex)
width: 30
height: 30
buttonShortcut: "Ctrl+Shift+F8"
buttonTooltip: qsTr("Start Debugging")
visible: true
}
StepActionImage
{
id: pauseAction
enabledStateImg: "qrc:/qml/img/stop_button2x.png"
disableStateImg: "qrc:/qml/img/stop_button2x.png"
onClicked: Debugger.init(null);
width: 30
height: 30
buttonShortcut: "Ctrl+Shift+F9"
buttonTooltip: qsTr("Stop Debugging")
visible: true
}
StepActionImage
{
id: runBackAction;

586
mix/qml/LogsPane.qml

@ -6,86 +6,262 @@ import org.ethereum.qml.SortFilterProxyModel 1.0
Rectangle
{
property variant currentStatus;
function push(_level, _type, _content)
{
_content = _content.replace(/\n/g, " ")
logsModel.insert(0, { "type": _type, "date": Qt.formatDateTime(new Date(), "hh:mm:ss dd.MM.yyyy"), "content": _content, "level": _level });
logsModel.insert(0, { "type": _type, "date": Qt.formatDateTime(new Date(), "hh:mm:ss"), "content": _content, "level": _level });
}
onVisibleChanged:
{
if (visible && (logsModel.count === 0 || (logsModel.get(0).date !== currentStatus.date && logsModel.get(0).content !== currentStatus.content)))
logsModel.insert(0, { "type": currentStatus.type, "date": currentStatus.date, "content": currentStatus.content, "level": currentStatus.level });
else if (!visible)
{
for (var k = 0; k < logsModel.count; k++)
{
if (logsModel.get(k).type === "Comp") //do not keep compilation logs.
logsModel.remove(k);
}
}
}
LogsPaneStyle {
id: logStyle
}
id: root
anchors.fill: parent
radius: 5
color: logStyle.generic.layout.backgroundColor
border.color: logStyle.generic.layout.borderColor
border.width: logStyle.generic.layout.borderWidth
ColumnLayout {
radius: 10
color: "transparent"
id: logsPane
Column {
z: 2
height: parent.height
height: parent.height - rowAction.height
width: parent.width
spacing: 0
ListModel {
id: logsModel
}
ScrollView
{
id: scrollView
height: parent.height
width: parent.width
horizontalScrollBarPolicy: Qt.ScrollBarAlwaysOff
Column
{
id: logsRect
spacing: 0
Repeater {
id: logsRepeater
clip: true
property string frontColor: "transparent"
model: SortFilterProxyModel {
id: proxyModel
source: logsModel
property var roles: ["-", "javascript", "run", "state", "comp"]
Component.onCompleted: {
filterType = regEx(proxyModel.roles);
}
function search(_value)
{
filterContent = _value;
}
function toogleFilter(_value)
{
var count = roles.length;
for (var i in roles)
{
if (roles[i] === _value)
{
roles.splice(i, 1);
break;
}
}
if (count === roles.length)
roles.push(_value);
filterType = regEx(proxyModel.roles);
}
function regEx(_value)
{
return "(?:" + roles.join('|') + ")";
}
filterType: "(?:javascript|run|state|comp)"
filterContent: ""
filterSyntax: SortFilterProxyModel.RegExp
filterCaseSensitivity: Qt.CaseInsensitive
}
Rectangle
{
width: logStyle.generic.layout.dateWidth + logStyle.generic.layout.contentWidth + logStyle.generic.layout.typeWidth
height: 30
color:
{
var cl;
if (level === "warning" || level === "error")
cl = logStyle.generic.layout.errorColor;
else
cl = index % 2 === 0 ? "transparent" : logStyle.generic.layout.logAlternateColor;
if (index === 0)
logsRepeater.frontColor = cl;
return cl;
}
MouseArea
{
anchors.fill: parent
onClicked:
{
if (logContent.elide === Text.ElideNone)
{
logContent.elide = Text.ElideRight;
logContent.wrapMode = Text.NoWrap
parent.height = 30;
}
else
{
logContent.elide = Text.ElideNone;
logContent.wrapMode = Text.WordWrap;
parent.height = logContent.lineCount * 30;
}
}
}
DefaultLabel {
text: date;
font.family: logStyle.generic.layout.logLabelFont
width: logStyle.generic.layout.dateWidth
font.pointSize: appStyle.absoluteSize(-1)
anchors.left: parent.left
anchors.leftMargin: 15
anchors.verticalCenter: parent.verticalCenter
color: {
parent.getColor(level);
}
}
DefaultLabel {
text: type;
font.family: logStyle.generic.layout.logLabelFont
width: logStyle.generic.layout.typeWidth
font.pointSize: appStyle.absoluteSize(-1)
anchors.left: parent.left
anchors.leftMargin: 100
anchors.verticalCenter: parent.verticalCenter
color: {
parent.getColor(level);
}
}
Text {
id: logContent
text: content;
font.family: logStyle.generic.layout.logLabelFont
width: logStyle.generic.layout.contentWidth
font.pointSize: appStyle.absoluteSize(-1)
anchors.verticalCenter: parent.verticalCenter
elide: Text.ElideRight
anchors.left: parent.left
anchors.leftMargin: 230
color: {
parent.getColor(level);
}
}
function getColor()
{
if (level === "error")
return "red";
else if (level === "warning")
return "orange";
else
return "#808080";
}
}
}
}
}
Component {
id: itemDelegate
DefaultLabel {
text: styleData.value;
font.family: logStyle.generic.layout.logLabelFont
font.pointSize: appStyle.absoluteSize(-1)
color: {
if (proxyModel.get(styleData.row).level === "error")
return "red";
else if (proxyModel.get(styleData.row).level === "warning")
return "orange";
else
return "#808080";
}
}
}
}
Rectangle
{
gradient: Gradient {
GradientStop { position: 0.0; color: "#f1f1f1" }
GradientStop { position: 1.0; color: "#d9d7da" }
}
Layout.preferredHeight: logStyle.generic.layout.headerHeight
height: logStyle.generic.layout.headerHeight
width: logsPane.width
anchors.bottom: parent.bottom
Row
{
id: rowAction
Layout.preferredHeight: logStyle.generic.layout.headerHeight
height: logStyle.generic.layout.headerHeight
anchors.leftMargin: logStyle.generic.layout.leftMargin
anchors.left: parent.left
spacing: logStyle.generic.layout.headerButtonSpacing
Button
height: parent.height
Rectangle
{
height: logStyle.generic.layout.headerButtonHeight
anchors.verticalCenter: parent.verticalCenter
action: clearAction
iconSource: "qrc:/qml/img/broom.png"
}
Action {
id: clearAction
enabled: logsModel.count > 0
tooltip: qsTr("Clear")
onTriggered: {
logsModel.clear()
color: "transparent"
height: parent.height
width: 40
DefaultLabel
{
anchors.verticalCenter: parent.verticalCenter
color: logStyle.generic.layout.logLabelColor
font.pointSize: appStyle.absoluteSize(-3)
font.family: logStyle.generic.layout.logLabelFont
text: qsTr("Show:")
}
}
Button
{
height: logStyle.generic.layout.headerButtonHeight
Rectangle {
anchors.verticalCenter: parent.verticalCenter
action: copytoClipBoardAction
iconSource: "qrc:/qml/img/copy.png"
}
Action {
id: copytoClipBoardAction
enabled: logsModel.count > 0
tooltip: qsTr("Copy to Clipboard")
onTriggered: {
var content = "";
for (var k = 0; k < logsModel.count; k++)
{
var log = logsModel.get(k);
content += log.type + "\t" + log.level + "\t" + log.date + "\t" + log.content + "\n";
}
clipboard.text = content;
}
width: 1;
height: parent.height
color: logStyle.generic.layout.buttonSeparatorColor1
}
Rectangle {
anchors.verticalCenter: parent.verticalCenter
width: 1;
height: parent.height - 10
color : "#808080"
width: 2;
height: parent.height
color: logStyle.generic.layout.buttonSeparatorColor2
}
ToolButton {
id: javascriptButton
checkable: true
height: logStyle.generic.layout.headerButtonHeight
width: 20
anchors.verticalCenter: parent.verticalCenter
checked: true
onCheckedChanged: {
@ -101,16 +277,35 @@ Rectangle
font.pointSize: appStyle.absoluteSize(-3)
color: logStyle.generic.layout.logLabelColor
anchors.centerIn: parent
text: qsTr("JavaScript")
text: qsTr("JS")
}
}
background:
Rectangle {
color: javascriptButton.checked ? logStyle.generic.layout.buttonSelected : "transparent"
}
}
}
Rectangle {
anchors.verticalCenter: parent.verticalCenter
width: 1;
height: parent.height
color: logStyle.generic.layout.buttonSeparatorColor1
}
Rectangle {
anchors.verticalCenter: parent.verticalCenter
width: 2;
height: parent.height
color: logStyle.generic.layout.buttonSeparatorColor2
}
ToolButton {
id: runButton
checkable: true
height: logStyle.generic.layout.headerButtonHeight
width: 30
anchors.verticalCenter: parent.verticalCenter
checked: true
onCheckedChanged: {
@ -129,14 +324,33 @@ Rectangle
text: qsTr("Run")
}
}
background:
Rectangle {
color: runButton.checked ? logStyle.generic.layout.buttonSelected : "transparent"
}
}
}
Rectangle {
anchors.verticalCenter: parent.verticalCenter
width: 1;
height: parent.height
color: logStyle.generic.layout.buttonSeparatorColor1
}
Rectangle {
anchors.verticalCenter: parent.verticalCenter
width: 2;
height: parent.height
color: logStyle.generic.layout.buttonSeparatorColor2
}
ToolButton {
id: stateButton
checkable: true
height: logStyle.generic.layout.headerButtonHeight
anchors.verticalCenter: parent.verticalCenter
width: 35
checked: true
onCheckedChanged: {
proxyModel.toogleFilter("state")
@ -149,132 +363,226 @@ Rectangle
DefaultLabel {
font.family: logStyle.generic.layout.logLabelFont
font.pointSize: appStyle.absoluteSize(-3)
color: "#5391d8"
color: logStyle.generic.layout.logLabelColor
anchors.centerIn: parent
text: qsTr("State")
}
}
background:
Rectangle {
color: stateButton.checked ? logStyle.generic.layout.buttonSelected : "transparent"
}
}
}
DefaultTextField
{
id: searchBox
height: logStyle.generic.layout.headerButtonHeight
Rectangle {
anchors.verticalCenter: parent.verticalCenter
width: logStyle.generic.layout.headerInputWidth
font.family: logStyle.generic.layout.logLabelFont
font.pointSize: appStyle.absoluteSize(-3)
font.italic: true
onTextChanged: {
proxyModel.search(text);
}
width: 1;
height: parent.height
color: logStyle.generic.layout.buttonSeparatorColor1
}
}
ListModel {
id: logsModel
Rectangle {
anchors.verticalCenter: parent.verticalCenter
width: 2;
height: parent.height
color: logStyle.generic.layout.buttonSeparatorColor2
}
}
TableView {
id: logsTable
clip: true
Layout.fillWidth: true
Layout.preferredHeight: parent.height - rowAction.height
headerVisible : false
onDoubleClicked:
Row
{
height: parent.height
anchors.right: parent.right
anchors.rightMargin: 10
spacing: 10
Rectangle
{
var log = logsModel.get(logsTable.currentRow);
if (log)
clipboard.text = (log.type + "\t" + log.level + "\t" + log.date + "\t" + log.content);
}
height: logStyle.generic.layout.headerButtonHeight
anchors.verticalCenter: parent.verticalCenter
color: "transparent"
width: 20
Button
{
id: clearButton
action: clearAction
anchors.fill: parent
anchors.verticalCenter: parent.verticalCenter
height: 25
style:
ButtonStyle {
background:
Rectangle {
height: logStyle.generic.layout.headerButtonHeight
implicitHeight: logStyle.generic.layout.headerButtonHeight
color: "transparent"
}
}
}
model: SortFilterProxyModel {
id: proxyModel
source: logsModel
property var roles: ["-", "javascript", "run", "state"]
Image {
id: clearImage
source: clearAction.enabled ? "qrc:/qml/img/cleariconactive.png" : "qrc:/qml/img/clearicon.png"
anchors.centerIn: parent
fillMode: Image.PreserveAspectFit
width: 20
height: 25
}
Component.onCompleted: {
filterType = regEx(proxyModel.roles);
Action {
id: clearAction
enabled: logsModel.count > 0
tooltip: qsTr("Clear")
onTriggered: {
logsModel.clear();
}
}
}
function search(_value)
Rectangle
{
height: logStyle.generic.layout.headerButtonHeight
anchors.verticalCenter: parent.verticalCenter
color: "transparent"
width: 20
Button
{
filterContent = _value;
id: copyButton
action: copyAction
anchors.fill: parent
anchors.verticalCenter: parent.verticalCenter
height: 25
style:
ButtonStyle {
background:
Rectangle {
height: logStyle.generic.layout.headerButtonHeight
implicitHeight: logStyle.generic.layout.headerButtonHeight
color: "transparent"
}
}
}
function toogleFilter(_value)
{
var count = roles.length;
for (var i in roles)
{
if (roles[i] === _value)
Image {
id: copyImage
source: copyAction.enabled ? "qrc:/qml/img/copyiconactive.png" : "qrc:/qml/img/copyicon.png"
anchors.centerIn: parent
fillMode: Image.PreserveAspectFit
width: 20
height: 25
}
Action {
id: copyAction
enabled: logsModel.count > 0
tooltip: qsTr("Copy to Clipboard")
onTriggered: {
var content = "";
for (var k = 0; k < logsModel.count; k++)
{
roles.splice(i, 1);
break;
var log = logsModel.get(k);
content += log.type + "\t" + log.level + "\t" + log.date + "\t" + log.content + "\n";
}
clipboard.text = content;
}
if (count === roles.length)
roles.push(_value);
filterType = regEx(proxyModel.roles);
}
}
function regEx(_value)
Rectangle
{
width: 120
radius: 10
height: 25
color: "white"
anchors.verticalCenter: parent.verticalCenter
Image
{
return "(?:" + roles.join('|') + ")";
id: searchImg
source: "qrc:/qml/img/searchicon.png"
fillMode: Image.PreserveAspectFit
width: 20
height: 25
z: 3
}
filterType: "(?:javascript|run|state)"
filterContent: ""
filterSyntax: SortFilterProxyModel.RegExp
filterCaseSensitivity: Qt.CaseInsensitive
}
TableViewColumn
{
role: "date"
title: qsTr("date")
width: logStyle.generic.layout.dateWidth
delegate: itemDelegate
}
TableViewColumn
{
role: "type"
title: qsTr("type")
width: logStyle.generic.layout.typeWidth
delegate: itemDelegate
DefaultTextField
{
id: searchBox
z: 2
width: 100
anchors.left: searchImg.right
anchors.leftMargin: -7
font.family: logStyle.generic.layout.logLabelFont
font.pointSize: appStyle.absoluteSize(-3)
font.italic: true
text: qsTr(" - Search - ")
onFocusChanged:
{
if (!focus && text === "")
text = qsTr(" - Search - ");
else if (focus && text === qsTr(" - Search - "))
text = "";
}
onTextChanged: {
if (text === qsTr(" - Search - "))
proxyModel.search("");
else
proxyModel.search(text);
}
style:
TextFieldStyle {
background: Rectangle {
radius: 10
}
}
}
}
TableViewColumn
Rectangle
{
role: "content"
title: qsTr("content")
width: logStyle.generic.layout.contentWidth
delegate: itemDelegate
}
height: logStyle.generic.layout.headerButtonHeight
anchors.verticalCenter: parent.verticalCenter
color: "transparent"
width: 20
Button
{
id: hideButton
action: hideAction
anchors.fill: parent
anchors.verticalCenter: parent.verticalCenter
height: 25
style:
ButtonStyle {
background:
Rectangle {
height: logStyle.generic.layout.headerButtonHeight
implicitHeight: logStyle.generic.layout.headerButtonHeight
color: "transparent"
}
}
}
rowDelegate: Item {
Rectangle {
width: logsTable.width - 4
height: 17
color: styleData.alternate ? "transparent" : logStyle.generic.layout.logAlternateColor
Image {
id: hideImage
source: "qrc:/qml/img/exit.png"
anchors.centerIn: parent
fillMode: Image.PreserveAspectFit
width: 20
height: 25
}
}
}
Component {
id: itemDelegate
DefaultLabel {
text: styleData.value;
font.family: logStyle.generic.layout.logLabelFont
font.pointSize: appStyle.absoluteSize(-1)
color: {
if (proxyModel.get(styleData.row).level === "error")
return "red";
else if (proxyModel.get(styleData.row).level === "warning")
return "orange";
else
return "#808080";
Action {
id: hideAction
tooltip: qsTr("Exit")
onTriggered: {
logsPane.parent.toggle();
}
}
}
}
}
}

18
mix/qml/LogsPaneStyle.qml

@ -10,19 +10,21 @@ QtObject {
property QtObject generic: QtObject {
property QtObject layout: QtObject {
property string backgroundColor: "#f7f7f7"
property string borderColor: "#5391d8"
property int borderWidth: 1
property int headerHeight: 35
property int headerButtonSpacing: 5
property int headerHeight: 30
property int headerButtonSpacing: 0
property int leftMargin: 10
property int headerButtonHeight: 30
property string logLabelColor: "#5391d8"
property string logLabelColor: "#4a4a4a"
property string logLabelFont: "sans serif"
property int headerInputWidth: 200
property int dateWidth: 150
property int typeWidth: 80
property int contentWidth: 700
property string logAlternateColor: "#f0f0f0"
property int typeWidth: 150
property int contentWidth: 560
property string logAlternateColor: "#f6f5f6"
property string errorColor: "#fffcd5"
property string buttonSeparatorColor1: "#d3d0d0"
property string buttonSeparatorColor2: "#f2f1f2"
property string buttonSelected: "#dcdcdc"
}
}
}

1
mix/qml/ProjectModel.qml

@ -66,6 +66,7 @@ Item {
function renameDocument(documentId, newName) { ProjectModelCode.renameDocument(documentId, newName); }
function removeDocument(documentId) { ProjectModelCode.removeDocument(documentId); }
function getDocument(documentId) { return ProjectModelCode.getDocument(documentId); }
function getDocumentIdByName(documentName) { return ProjectModelCode.getDocumentIdByName(documentName); }
function getDocumentIndex(documentId) { return ProjectModelCode.getDocumentIndex(documentId); }
function addExistingFiles(paths) { ProjectModelCode.doAddExistingFiles(paths); }
function deployProject() { ProjectModelCode.deployProject(false); }

260
mix/qml/StatesComboBox.qml

@ -0,0 +1,260 @@
/*
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 StatesComboBox.qml
* @author Ali Mashatan ali@ethdev.com
* @date 2015
* Ethereum IDE client.
*/
import QtQuick 2.0
import QtQuick.Controls 1.0
import QtQuick.Layouts 1.1
import QtGraphicalEffects 1.0
Rectangle {
id: statesComboBox
width: 200
height: 23
Component.onCompleted: {
var top = dropDownList
while (top.parent) {
top = top.parent
if (top.objectName == "debugPanel")
break
}
var coordinates = dropDownList.mapToItem(top, 0, 0)
//the order is important
dropDownShowdowList.parent = top
dropDownList.parent = top
dropDownShowdowList.x = coordinates.x
dropDownShowdowList.y = coordinates.y
dropDownList.x = coordinates.x
dropDownList.y = coordinates.y
}
signal selectItem(real item)
signal editItem(real item)
signal selectCreate
property int rowHeight: 25
property variant items
property alias selectedItem: chosenItemText.text
property alias selectedIndex: listView.currentRow
function setSelectedIndex(index) {
listView.currentRow = index
chosenItemText.text = statesComboBox.items.get(index).title
}
signal comboClicked
property variant colorItem
property variant colorSelect
SourceSansProRegular
{
id: regularFont
}
SourceSansProBold
{
id: boldFont
}
smooth: true
Rectangle {
id: chosenItem
width: parent.width
height: statesComboBox.height
color: statesComboBox.color
Text {
id: chosenItemText
anchors.left: parent.left
anchors.leftMargin: 10
anchors.verticalCenter: parent.verticalCenter
color: statesComboBox.colorItem
text: ""
font.family: regularFont.name
}
MouseArea {
anchors.fill: parent
onClicked: {
statesComboBox.state = statesComboBox.state === "dropDown" ? "" : "dropDown"
}
}
}
Rectangle {
id: dropDownShowdowList
width: statesComboBox.width
opacity: 0.3
height: 0
clip: true
radius: 4
anchors.top: chosenItem.top
anchors.margins: 2
color: "gray"
}
//ToDo: We need scrollbar for items
Rectangle {
id: dropDownList
width: statesComboBox.width
height: 0
clip: true
radius: 4
anchors.top: chosenItem.top
anchors.topMargin: 23
color: statesComboBox.color
ColumnLayout {
spacing: 2
TableView {
id: listView
height: 20
implicitHeight: 0
width: statesComboBox.width
model: statesComboBox.items
horizontalScrollBarPolicy: Qt.ScrollBarAlwaysOff
currentRow: -1
headerVisible: false
backgroundVisible: false
alternatingRowColors: false
frameVisible: false
TableViewColumn {
role: "title"
title: ""
width: statesComboBox.width
delegate: mainItemDelegate
}
rowDelegate: Rectangle {
width: statesComboBox.width
height: statesComboBox.rowHeight
}
Component {
id: mainItemDelegate
Rectangle {
id: itemDelegate
width: statesComboBox.width
height: statesComboBox.height
Text {
id: textItemid
text: styleData.value
color: statesComboBox.colorItem
anchors.top: parent.top
anchors.left: parent.left
anchors.leftMargin: 10
anchors.topMargin: 5
font.family: regularFont.name
}
Image {
id: imageItemid
height: 20
width: 20
anchors.right: parent.right
anchors.top: parent.top
anchors.margins: 5
visible: false
fillMode: Image.PreserveAspectFit
source: "img/edit_combox.png"
}
MouseArea {
anchors.fill: parent
hoverEnabled: true
onEntered: {
imageItemid.visible = true
textItemid.color = statesComboBox.colorSelect
}
onExited: {
imageItemid.visible = false
textItemid.color = statesComboBox.colorItem
}
onClicked: {
if (mouseX > imageItemid.x
&& mouseX < imageItemid.x + imageItemid.width
&& mouseY > imageItemid.y
&& mouseY < imageItemid.y + imageItemid.height)
statesComboBox.editItem(styleData.row)
else {
statesComboBox.state = ""
var prevSelection = chosenItemText.text
chosenItemText.text = styleData.value
listView.currentRow = styleData.row
statesComboBox.selectItem(styleData.row)
}
}
}
} //Item
} //Component
} //Table View
RowLayout {
anchors.top: listView.bottom
anchors.topMargin: 4
anchors.left: parent.left
anchors.leftMargin: 10
Text {
id: createStateText
width: statesComboBox.width
height: statesComboBox.height
font.family: boldFont.name
color: "#808080"
text: qsTr("Create State ...")
font.weight: Font.DemiBold
MouseArea {
anchors.fill: parent
hoverEnabled: true
onEntered: {
createStateText.color = statesComboBox.colorSelect
}
onExited: {
createStateText.color = statesComboBox.colorItem
}
onClicked: {
statesComboBox.state = ""
statesComboBox.selectCreate()
}
}
}
}
}
}
states: State {
name: "dropDown"
PropertyChanges {
target: dropDownList
height: (statesComboBox.rowHeight * (statesComboBox.items.count + 1))
}
PropertyChanges {
target: dropDownShowdowList
width: statesComboBox.width + 3
height: (statesComboBox.rowHeight * (statesComboBox.items.count + 1)) + 3
}
PropertyChanges {
target: listView
height: 20
implicitHeight: (statesComboBox.rowHeight * (statesComboBox.items.count))
}
}
}

63
mix/qml/StatusPane.qml

@ -9,7 +9,7 @@ Rectangle {
id: statusHeader
objectName: "statusPane"
property variant webPreview
property alias currentStatus: logPane.currentStatus
function updateStatus(message)
{
if (!message)
@ -17,6 +17,7 @@ Rectangle {
status.state = "";
status.text = qsTr("Compile successfully.");
debugImg.state = "active";
currentStatus = { "type": "Comp", "date": Qt.formatDateTime(new Date(), "hh:mm:ss"), "content": status.text, "level": "info" }
}
else
{
@ -24,6 +25,7 @@ Rectangle {
var errorInfo = ErrorLocationFormater.extractErrorInfo(message, true);
status.text = errorInfo.errorLocation + " " + errorInfo.errorDetail;
debugImg.state = "";
currentStatus = { "type": "Comp", "date": Qt.formatDateTime(new Date(), "hh:mm:ss"), "content": status.text, "level": "error" }
}
debugRunActionIcon.enabled = codeModel.hasContract;
}
@ -33,6 +35,7 @@ Rectangle {
status.state = "";
status.text = text
logPane.push("info", type, text);
currentStatus = { "type": type, "date": Qt.formatDateTime(new Date(), "hh:mm:ss"), "content": text, "level": "info" }
}
function warningMessage(text, type)
@ -40,13 +43,15 @@ Rectangle {
status.state = "warning";
status.text = text
logPane.push("warning", type, text);
currentStatus = { "type": type, "date": Qt.formatDateTime(new Date(), "hh:mm:ss"), "content": text, "level": "warning" }
}
function errorMessage(text, type)
{
status.state = "error";
status.text = text
status.text = text;
logPane.push("error", type, text);
currentStatus = { "type": type, "date": Qt.formatDateTime(new Date(), "hh:mm:ss"), "content": text, "level": "error" }
}
StatusPaneStyle {
@ -80,7 +85,7 @@ Rectangle {
function format(_message)
{
var formatted = _message.match(/(?:<dev::eth::)(.+)(?:>)/);
if (formatted)
if (!formatted)
formatted = _message.match(/(?:<dev::)(.+)(?:>)/);
if (formatted && formatted.length > 1)
formatted = formatted[1];
@ -94,6 +99,7 @@ Rectangle {
return formatted;
}
}
Connections {
target:projectModel
onDeploymentStarted: infoMessage(qsTr("Running deployment..."), "Deployment");
@ -115,32 +121,13 @@ Rectangle {
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
radius: 3
width: 500
width: 600
height: 30
color: "#fcfbfc"
states: [
State {
name: "logsOpened"
PropertyChanges {
target: statusContainer
border.color: "#5391d8"
border.width: 1
}
},
State {
name: "logsClosed"
PropertyChanges {
target: statusContainer
border.color: "#5391d8"
border.width: 0
}
}
]
Text {
anchors.verticalCenter: parent.verticalCenter
anchors.horizontalCenter: parent.horizontalCenter
font.pointSize: statusPaneStyle.general.statusFontSize
font.pointSize: appStyle.absoluteSize(-1)
height: 15
font.family: "sans serif"
objectName: "status"
@ -219,40 +206,39 @@ Rectangle {
{
if (logsContainer.state === "opened")
{
statusContainer.state = "logsClosed";
logsContainer.state = "closed"
}
else
{
statusContainer.state = "logsOpened";
logsContainer.state = "opened";
logsContainer.focus = true;
forceActiveFocus();
calCoord();
}
}
id: logsContainer
width: 1000
height: 0
anchors.topMargin: 10
width: 750
anchors.top: statusContainer.bottom
anchors.horizontalCenter: parent.horizontalCenter
anchors.topMargin: 4
visible: false
radius: 5
Component.onCompleted:
radius: 10
function calCoord()
{
var top = logsContainer;
while (top.parent)
top = top.parent
var coordinates = logsContainer.mapToItem(top, 0, 0)
var coordinates = logsContainer.mapToItem(top, 0, 0);
logsContainer.parent = top;
logsContainer.x = coordinates.x
logsContainer.y = coordinates.y
logsContainer.x = status.x + statusContainer.x - LogsPaneStyle.generic.layout.dateWidth - LogsPaneStyle.generic.layout.typeWidth + 70
}
LogsPane
{
id: logPane
id: logPane;
}
states: [
State {
name: "opened";
@ -261,12 +247,13 @@ Rectangle {
State {
name: "closed";
PropertyChanges { target: logsContainer; height: 0; visible: false }
PropertyChanges { target: statusContainer; width: 600; height: 30 }
}
]
transitions: Transition {
NumberAnimation { properties: "height"; easing.type: Easing.InOutQuad; duration: 200 }
NumberAnimation { properties: "visible"; easing.type: Easing.InOutQuad; duration: 200 }
}
NumberAnimation { target: logsContainer; properties: "visible"; easing.type: Easing.InOutQuad; duration: 200 }
}
}
}

4
mix/qml/StorageView.qml

@ -29,7 +29,7 @@ DebugInfoList
anchors.leftMargin: 5
color: "#4a4a4a"
text: styleData.value.split('\t')[0];
font.pointSize: DebuggerPaneStyle.general.basicFontSize
font.pointSize: dbgStyle.general.basicFontSize
width: parent.width - 5
elide: Text.ElideRight
}
@ -52,7 +52,7 @@ DebugInfoList
color: "#4a4a4a"
text: styleData.value.split('\t')[1];
elide: Text.ElideRight
font.pointSize: DebuggerPaneStyle.general.basicFontSize
font.pointSize: dbgStyle.general.basicFontSize
}
}
}

59
mix/qml/TransactionLog.qml

@ -6,30 +6,16 @@ import QtQuick.Layouts 1.1
import org.ethereum.qml.RecordLogEntry 1.0
Item {
property ListModel fullModel: ListModel{}
property ListModel transactionModel: ListModel{}
property ListModel callModel: ListModel{}
Action {
id: addStateAction
text: "Add State"
shortcut: "Ctrl+Alt+T"
enabled: codeModel.hasContract && !clientModel.running;
onTriggered: projectModel.stateListModel.addState();
}
Action {
id: editStateAction
text: "Edit State"
shortcut: "Ctrl+Alt+T"
enabled: codeModel.hasContract && !clientModel.running && statesCombo.currentIndex >= 0 && projectModel.stateListModel.count > 0;
onTriggered: projectModel.stateListModel.editState(statesCombo.currentIndex);
}
property int selectedStateIndex: statesCombo.selectedIndex
ColumnLayout {
anchors.fill: parent
RowLayout {
anchors.right: parent.right
anchors.left: parent.left
Connections
{
id: compilationStatus
@ -61,36 +47,24 @@ Item {
}
}
ComboBox {
StatesComboBox
{
id: statesCombo
model: projectModel.stateListModel
width: 150
editable: false
textRole: "title"
onActivated: {
model.runState(index);
}
items: projectModel.stateListModel
onSelectCreate: projectModel.stateListModel.addState();
onEditItem: projectModel.stateListModel.editState(item)
colorItem: "#808080"
colorSelect: "#4a90e2"
color: "white"
Connections {
target: projectModel.stateListModel
onStateRun: {
if (statesCombo.currentIndex !== index)
statesCombo.currentIndex = index;
if (statesCombo.selectedIndex !== index)
statesCombo.setSelectedIndex( index );
}
}
}
Button
{
anchors.rightMargin: 9
anchors.verticalCenter: parent.verticalCenter
action: editStateAction
}
Button
{
anchors.rightMargin: 9
anchors.verticalCenter: parent.verticalCenter
action: addStateAction
}
Button
{
anchors.rightMargin: 9
anchors.verticalCenter: parent.verticalCenter
@ -163,16 +137,11 @@ Item {
Keys.onPressed: {
if ((event.modifiers & Qt.ControlModifier) && event.key === Qt.Key_C && currentRow >=0 && currentRow < logTable.model.count) {
var item = logTable.model.get(currentRow);
clipboard.text = item.returned;
appContext.toClipboard(item.returned);
}
}
}
Rectangle {
height: 6
color: "transparent"
}
}
Connections {
target: clientModel
onStateCleared: {

7
mix/qml/WebPreview.qml

@ -160,18 +160,19 @@ Item {
//document request
if (urlPath === "/")
urlPath = "/index.html";
var documentId = urlPath.substr(urlPath.lastIndexOf("/") + 1);
var documentName = urlPath.substr(urlPath.lastIndexOf("/") + 1);
var documentId = projectModel.getDocumentIdByName(documentName);
var content = "";
if (projectModel.codeEditor.isDocumentOpen(documentId))
content = projectModel.codeEditor.getDocumentText(documentId);
else
{
var doc = projectModel.getDocument(documentId);
if (doc !== undefined)
if (doc)
content = fileIo.readFile(doc.path);
}
if (documentId === urlInput.text.replace(httpServer.url + "/", "")) {
if (documentName === urlInput.text.replace(httpServer.url + "/", "")) {
//root page, inject deployment script
content = "<script>web3=parent.web3;contracts=parent.contracts;</script>\n" + content;
_request.setResponseContentType("text/html");

41
mix/qml/html/codeeditor.js

@ -22,17 +22,11 @@ editor.on("change", function(eMirror, object) {
});
var mac = /Mac/.test(navigator.platform);
var extraKeys = {};
if (mac === true) {
editor.setOption("extraKeys", {
"Cmd-V": function(cm) {
cm.replaceSelection(clipboard);
},
"Cmd-X": function(cm) {
window.document.execCommand("cut");
},
"Cmd-C": function(cm) {
window.document.execCommand("copy");
}});
extraKeys["Cmd-V"] = function(cm) { cm.replaceSelection(clipboard); };
extraKeys["Cmd-X"] = function(cm) { window.document.execCommand("cut"); };
extraKeys["Cmd-C"] = function(cm) { window.document.execCommand("copy"); };
}
makeMarker = function() {
@ -102,16 +96,14 @@ setMode = function(mode) {
if (mode === "javascript")
{
ternServer = new CodeMirror.TernServer({defs: [ ecma5Spec() ]});
editor.setOption("extraKeys", {
"Ctrl-Space": function(cm) { ternServer.complete(cm); },
"Ctrl-I": function(cm) { ternServer.showType(cm); },
"Ctrl-O": function(cm) { ternServer.showDocs(cm); },
"Alt-.": function(cm) { ternServer.jumpToDef(cm); },
"Alt-,": function(cm) { ternServer.jumpBack(cm); },
"Ctrl-Q": function(cm) { ternServer.rename(cm); },
"Ctrl-.": function(cm) { ternServer.selectName(cm); },
"'.'": function(cm) { setTimeout(function() { ternServer.complete(cm); }, 100); throw CodeMirror.Pass; }
})
extraKeys["Ctrl-Space"] = function(cm) { ternServer.complete(cm); };
extraKeys["Ctrl-I"] = function(cm) { ternServer.showType(cm); };
extraKeys["Ctrl-O"] = function(cm) { ternServer.showDocs(cm); };
extraKeys["Alt-."] = function(cm) { ternServer.jumpToDef(cm); };
extraKeys["Alt-,"] = function(cm) { ternServer.jumpBack(cm); };
extraKeys["Ctrl-Q"] = function(cm) { ternServer.rename(cm); };
extraKeys["Ctrl-."] = function(cm) { ternServer.selectName(cm); };
extraKeys["'.'"] = function(cm) { setTimeout(function() { ternServer.complete(cm); }, 100); throw CodeMirror.Pass; };
editor.on("cursorActivity", function(cm) { ternServer.updateArgHints(cm); });
}
else if (mode === "solidity")
@ -119,10 +111,9 @@ setMode = function(mode) {
CodeMirror.commands.autocomplete = function(cm) {
CodeMirror.showHint(cm, CodeMirror.hint.anyword);
}
editor.setOption("extraKeys", {
"Ctrl-Space": "autocomplete"
})
extraKeys["Ctrl-Space"] = "autocomplete";
}
editor.setOption("extraKeys", extraKeys);
};
setClipboardBase64 = function(text) {
@ -133,8 +124,6 @@ var executionMark;
highlightExecution = function(start, end) {
if (executionMark)
executionMark.clear();
if (start === 0 && end + 1 === editor.getValue().length)
return; // Do not hightlight the whole document.
if (debugWarning)
debugWarning.clear();
executionMark = editor.markText(editor.posFromIndex(start), editor.posFromIndex(end), { className: "CodeMirror-exechighlight" });
@ -199,3 +188,5 @@ compilationComplete = function()
}
compilationCompleteBool = true;
}
editor.setOption("extraKeys", extraKeys);

BIN
mix/qml/img/broom.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 501 B

BIN
mix/qml/img/clearicon.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

BIN
mix/qml/img/cleariconactive.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

BIN
mix/qml/img/copyicon.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 871 B

BIN
mix/qml/img/copyiconactive.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 875 B

BIN
mix/qml/img/edit_combox.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 406 B

BIN
mix/qml/img/pause_button.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 200 B

BIN
mix/qml/img/pause_button2x.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 414 B

BIN
mix/qml/img/play_button.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 531 B

BIN
mix/qml/img/play_button2x.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 904 B

BIN
mix/qml/img/searchicon.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

BIN
mix/qml/img/stop_button2x.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 235 B

10
mix/qml/js/ProjectModel.js

@ -172,7 +172,7 @@ function getDocumentIndex(documentId)
for (var i = 0; i < projectListModel.count; i++)
if (projectListModel.get(i).documentId === documentId)
return i;
console.error("Cant find document " + documentId);
console.error("Can't find document " + documentId);
return -1;
}
@ -291,6 +291,14 @@ function getDocument(documentId) {
return projectListModel.get(i);
}
function getDocumentIdByName(fileName)
{
for (var i = 0; i < projectListModel.count; i++)
if (projectListModel.get(i).fileName === fileName)
return projectListModel.get(i).documentId;
return null;
}
function removeDocument(documentId) {
var i = getDocumentIndex(documentId);
var document = projectListModel.get(i);

12
mix/res.qrc

@ -17,7 +17,6 @@
<file>qml/fonts/SourceSerifPro-Semibold.ttf</file>
<file>qml/img/available_updates.png</file>
<file>qml/img/b64.png</file>
<file>qml/img/broom.png</file>
<file>qml/img/bugiconactive.png</file>
<file>qml/img/bugiconinactive.png</file>
<file>qml/img/closedtriangleindicator.png</file>
@ -49,9 +48,20 @@
<file>qml/img/projecticon.png</file>
<file>qml/img/run.png</file>
<file>qml/img/search_filled.png</file>
<file>qml/img/play_button2x.png</file>
<file>qml/img/play_button.png</file>
<file>qml/img/pause_button2x.png</file>
<file>qml/img/pause_button.png</file>
<file>res/mix_256x256x32.png</file>
<file>stdc/config.sol</file>
<file>stdc/namereg.sol</file>
<file>stdc/std.sol</file>
<file>qml/img/edit_combox.png</file>
<file>qml/img/clearicon.png</file>
<file>qml/img/cleariconactive.png</file>
<file>qml/img/copyicon.png</file>
<file>qml/img/copyiconactive.png</file>
<file>qml/img/searchicon.png</file>
<file>qml/img/stop_button2x.png</file>
</qresource>
</RCC>

19
mix/style.xml

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE QtCreatorCodeStyle>
<!-- Written by QtCreator 3.3.2, 2015-03-31T14:55:07. -->
<qtcreator>
<data>
<variable>CodeStyleData</variable>
<valuemap type="QVariantMap">
<value type="bool" key="AutoSpacesForTabs">false</value>
<value type="int" key="IndentSize">4</value>
<value type="int" key="PaddingMode">2</value>
<value type="bool" key="SpacesForTabs">false</value>
<value type="int" key="TabSize">4</value>
</valuemap>
</data>
<data>
<variable>DisplayName</variable>
<value type="QString">Eth</value>
</data>
</qtcreator>

37
test/ClientBase.cpp

@ -34,34 +34,45 @@ BOOST_AUTO_TEST_CASE(blocks)
{
enumerateClients([](Json::Value const& _json, dev::eth::ClientBase& _client) -> void
{
for (string const& name: _json["postState"].getMemberNames())
auto compareState = [&_client](Json::Value const& _o, string const& _name, BlockNumber _blockNumber) -> void
{
Json::Value o = _json["postState"][name];
Address address(name);
Address address(_name);
// balanceAt
u256 expectedBalance = u256(o["balance"].asString());
u256 balance = _client.balanceAt(address);
u256 expectedBalance = u256(_o["balance"].asString());
u256 balance = _client.balanceAt(address, _blockNumber);
ETH_CHECK_EQUAL(expectedBalance, balance);
// countAt
u256 expectedCount = u256(o["nonce"].asString());
u256 count = _client.countAt(address);
u256 expectedCount = u256(_o["nonce"].asString());
u256 count = _client.countAt(address, _blockNumber);
ETH_CHECK_EQUAL(expectedCount, count);
// stateAt
for (string const& pos: o["storage"].getMemberNames())
for (string const& pos: _o["storage"].getMemberNames())
{
u256 expectedState = u256(o["storage"][pos].asString());
u256 state = _client.stateAt(address, u256(pos));
u256 expectedState = u256(_o["storage"][pos].asString());
u256 state = _client.stateAt(address, u256(pos), _blockNumber);
ETH_CHECK_EQUAL(expectedState, state);
}
// codeAt
bytes expectedCode = fromHex(o["code"].asString());
bytes code = _client.codeAt(address);
bytes expectedCode = fromHex(_o["code"].asString());
bytes code = _client.codeAt(address, _blockNumber);
ETH_CHECK_EQUAL_COLLECTIONS(expectedCode.begin(), expectedCode.end(),
code.begin(), code.end());
code.begin(), code.end());
};
for (string const& name: _json["postState"].getMemberNames())
{
Json::Value o = _json["postState"][name];
compareState(o, name, PendingBlock);
}
for (string const& name: _json["pre"].getMemberNames())
{
Json::Value o = _json["pre"][name];
compareState(o, name, 0);
}
// number

14
test/SolidityNameAndTypeResolution.cpp

@ -460,7 +460,7 @@ BOOST_AUTO_TEST_CASE(function_external_types)
uint a;
}
contract Test {
function boo(uint arg2, bool arg3, bytes8 arg4, bool[2] pairs, uint[] dynamic, C carg) external returns (uint ret) {
function boo(uint arg2, bool arg3, bytes8 arg4, bool[2] pairs, uint[] dynamic, C carg, address[] addresses) external returns (uint ret) {
ret = 5;
}
})";
@ -471,7 +471,7 @@ BOOST_AUTO_TEST_CASE(function_external_types)
auto functions = contract->getDefinedFunctions();
if (functions.empty())
continue;
BOOST_CHECK_EQUAL("boo(uint256,bool,bytes8,bool[2],uint256[],address)", functions[0]->externalSignature());
BOOST_CHECK_EQUAL("boo(uint256,bool,bytes8,bool[2],uint256[],address,address[])", functions[0]->externalSignature());
}
}
@ -503,6 +503,16 @@ BOOST_AUTO_TEST_CASE(function_external_call_not_allowed_conversion)
BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError);
}
// todo delete when implemented
BOOST_AUTO_TEST_CASE(arrays_in_internal_functions)
{
char const* text = R"(
contract Test {
function foo(address[] addresses) {}
})";
BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError);
}
BOOST_AUTO_TEST_CASE(function_internal_allowed_conversion)
{
char const* text = R"(

265
test/SolidityOptimizer.cpp

@ -303,6 +303,271 @@ BOOST_AUTO_TEST_CASE(cse_associativity2)
checkCSE(input, {Instruction::DUP2, Instruction::DUP2, Instruction::ADD, u256(5), Instruction::ADD});
}
BOOST_AUTO_TEST_CASE(cse_storage)
{
AssemblyItems input{
u256(0),
Instruction::SLOAD,
u256(0),
Instruction::SLOAD,
Instruction::ADD,
u256(0),
Instruction::SSTORE
};
checkCSE(input, {
u256(0),
Instruction::DUP1,
Instruction::SLOAD,
Instruction::DUP1,
Instruction::ADD,
Instruction::SWAP1,
Instruction::SSTORE
});
}
BOOST_AUTO_TEST_CASE(cse_noninterleaved_storage)
{
// two stores to the same location should be replaced by only one store, even if we
// read in the meantime
AssemblyItems input{
u256(7),
Instruction::DUP2,
Instruction::SSTORE,
Instruction::DUP1,
Instruction::SLOAD,
u256(8),
Instruction::DUP3,
Instruction::SSTORE
};
checkCSE(input, {
u256(8),
Instruction::DUP2,
Instruction::SSTORE,
u256(7)
});
}
BOOST_AUTO_TEST_CASE(cse_interleaved_storage)
{
// stores and reads to/from two unknown locations, should not optimize away the first store
AssemblyItems input{
u256(7),
Instruction::DUP2,
Instruction::SSTORE, // store to "DUP1"
Instruction::DUP2,
Instruction::SLOAD, // read from "DUP2", might be equal to "DUP1"
u256(0),
Instruction::DUP3,
Instruction::SSTORE // store different value to "DUP1"
};
checkCSE(input, input);
}
BOOST_AUTO_TEST_CASE(cse_interleaved_storage_same_value)
{
// stores and reads to/from two unknown locations, should not optimize away the first store
// but it should optimize away the second, since we already know the value will be the same
AssemblyItems input{
u256(7),
Instruction::DUP2,
Instruction::SSTORE, // store to "DUP1"
Instruction::DUP2,
Instruction::SLOAD, // read from "DUP2", might be equal to "DUP1"
u256(6),
u256(1),
Instruction::ADD,
Instruction::DUP3,
Instruction::SSTORE // store same value to "DUP1"
};
checkCSE(input, {
u256(7),
Instruction::DUP2,
Instruction::SSTORE,
Instruction::DUP2,
Instruction::SLOAD
});
}
BOOST_AUTO_TEST_CASE(cse_interleaved_storage_at_known_location)
{
// stores and reads to/from two known locations, should optimize away the first store,
// because we know that the location is different
AssemblyItems input{
u256(0x70),
u256(1),
Instruction::SSTORE, // store to 1
u256(2),
Instruction::SLOAD, // read from 2, is different from 1
u256(0x90),
u256(1),
Instruction::SSTORE // store different value at 1
};
checkCSE(input, {
u256(2),
Instruction::SLOAD,
u256(0x90),
u256(1),
Instruction::SSTORE
});
}
BOOST_AUTO_TEST_CASE(cse_interleaved_storage_at_known_location_offset)
{
// stores and reads to/from two locations which are known to be different,
// should optimize away the first store, because we know that the location is different
AssemblyItems input{
u256(0x70),
Instruction::DUP2,
u256(1),
Instruction::ADD,
Instruction::SSTORE, // store to "DUP1"+1
Instruction::DUP1,
u256(2),
Instruction::ADD,
Instruction::SLOAD, // read from "DUP1"+2, is different from "DUP1"+1
u256(0x90),
Instruction::DUP3,
u256(1),
Instruction::ADD,
Instruction::SSTORE // store different value at "DUP1"+1
};
checkCSE(input, {
u256(2),
Instruction::DUP2,
Instruction::ADD,
Instruction::SLOAD,
u256(0x90),
u256(1),
Instruction::DUP4,
Instruction::ADD,
Instruction::SSTORE
});
}
BOOST_AUTO_TEST_CASE(cse_interleaved_memory_at_known_location_offset)
{
// stores and reads to/from two locations which are known to be different,
// should not optimize away the first store, because the location overlaps with the load,
// but it should optimize away the second, because we know that the location is different by 32
AssemblyItems input{
u256(0x50),
Instruction::DUP2,
u256(2),
Instruction::ADD,
Instruction::MSTORE, // ["DUP1"+2] = 0x50
u256(0x60),
Instruction::DUP2,
u256(32),
Instruction::ADD,
Instruction::MSTORE, // ["DUP1"+32] = 0x60
Instruction::DUP1,
Instruction::MLOAD, // read from "DUP1"
u256(0x70),
Instruction::DUP3,
u256(32),
Instruction::ADD,
Instruction::MSTORE, // ["DUP1"+32] = 0x70
u256(0x80),
Instruction::DUP3,
u256(2),
Instruction::ADD,
Instruction::MSTORE, // ["DUP1"+2] = 0x80
};
// If the actual code changes too much, we could also simply check that the output contains
// exactly 3 MSTORE and exactly 1 MLOAD instruction.
checkCSE(input, {
u256(0x50),
u256(2),
Instruction::DUP3,
Instruction::ADD,
Instruction::SWAP1,
Instruction::DUP2,
Instruction::MSTORE, // ["DUP1"+2] = 0x50
Instruction::DUP2,
Instruction::MLOAD, // read from "DUP1"
u256(0x70),
u256(32),
Instruction::DUP5,
Instruction::ADD,
Instruction::MSTORE, // ["DUP1"+32] = 0x70
u256(0x80),
Instruction::SWAP1,
Instruction::SWAP2,
Instruction::MSTORE // ["DUP1"+2] = 0x80
});
}
BOOST_AUTO_TEST_CASE(cse_deep_stack)
{
AssemblyItems input{
Instruction::ADD,
Instruction::SWAP1,
Instruction::POP,
Instruction::SWAP8,
Instruction::POP,
Instruction::SWAP8,
Instruction::POP,
Instruction::SWAP8,
Instruction::SWAP5,
Instruction::POP,
Instruction::POP,
Instruction::POP,
Instruction::POP,
Instruction::POP,
};
checkCSE(input, {
Instruction::SWAP4,
Instruction::SWAP12,
Instruction::SWAP3,
Instruction::SWAP11,
Instruction::POP,
Instruction::SWAP1,
Instruction::SWAP3,
Instruction::ADD,
Instruction::SWAP8,
Instruction::POP,
Instruction::SWAP6,
Instruction::POP,
Instruction::POP,
Instruction::POP,
Instruction::POP,
Instruction::POP,
Instruction::POP,
});
}
BOOST_AUTO_TEST_CASE(cse_jumpi_no_jump)
{
AssemblyItems input{
u256(0),
u256(1),
Instruction::DUP2,
AssemblyItem(PushTag, 1),
Instruction::JUMPI
};
checkCSE(input, {
u256(0),
u256(1)
});
}
BOOST_AUTO_TEST_CASE(cse_jumpi_jump)
{
AssemblyItems input{
u256(1),
u256(1),
Instruction::DUP2,
AssemblyItem(PushTag, 1),
Instruction::JUMPI
};
checkCSE(input, {
u256(1),
Instruction::DUP1,
AssemblyItem(PushTag, 1),
Instruction::JUMP
});
}
BOOST_AUTO_TEST_SUITE_END()
}

4
test/dagger.cpp

@ -63,8 +63,8 @@ BOOST_AUTO_TEST_CASE(basic_test)
unsigned cacheSize(o["cache_size"].get_int());
h256 cacheHash(o["cache_hash"].get_str());
BOOST_REQUIRE_EQUAL(Ethasher::get()->cache(header).size(), cacheSize);
BOOST_REQUIRE_EQUAL(sha3(Ethasher::get()->cache(header)), cacheHash);
BOOST_REQUIRE_EQUAL(Ethasher::get()->params(header).cache_size, cacheSize);
BOOST_REQUIRE_EQUAL(sha3(bytesConstRef((byte const*)Ethasher::get()->cache(header), cacheSize)), cacheHash);
#if TEST_FULL
unsigned fullSize(o["full_size"].get_int());

9
test/peer.cpp

@ -57,6 +57,15 @@ BOOST_AUTO_TEST_CASE(host)
g_logVerbosity = oldLogVerbosity;
}
BOOST_AUTO_TEST_CASE(networkConfig)
{
Host save("Test", NetworkPreferences(false));
bytes store(save.saveNetwork());
Host restore("Test", NetworkPreferences(false), bytesConstRef(&store));
BOOST_REQUIRE(save.id() == restore.id());
}
BOOST_AUTO_TEST_CASE(save_nodes)
{
std::list<Host*> hosts;

Loading…
Cancel
Save