Browse Source

Merge remote-tracking branch 'upstream/develop' into develop

cl-refactor
Ali Mashatan 10 years ago
parent
commit
a21831199a
  1. 2
      CodingStandards.txt
  2. 1
      alethzero/MainWin.cpp
  3. 2
      alethzero/Transact.cpp
  4. 5
      libdevcore/CommonJS.h
  5. 2
      libdevcrypto/MemoryDB.h
  6. 7
      libethash/CMakeLists.txt
  7. 4
      libethash/data_sizes.h
  8. 133
      libethash/ethash.h
  9. 44
      libethash/internal.c
  10. 8
      libethash/internal.h
  11. 89
      libethash/io.c
  12. 116
      libethash/io.h
  13. 76
      libethash/io_posix.c
  14. 73
      libethash/io_win32.c
  15. 45
      libethcore/Ethasher.cpp
  16. 27
      libethcore/Ethasher.h
  17. 6
      libethcore/Params.h
  18. 21
      libethereum/BlockChain.cpp
  19. 5
      libethereum/Executive.cpp
  20. 2
      libethereum/Executive.h
  21. 42
      libethereum/State.cpp
  22. 8
      libethereum/Transaction.cpp
  23. 27
      libethereum/Transaction.h
  24. 15
      libevmcore/Assembly.cpp
  25. 303
      libevmcore/CommonSubexpressionEliminator.cpp
  26. 84
      libevmcore/CommonSubexpressionEliminator.h
  27. 100
      libevmcore/ExpressionClasses.cpp
  28. 20
      libevmcore/ExpressionClasses.h
  29. 69
      libp2p/Host.cpp
  30. 2
      libp2p/Host.h
  31. 34
      libsolidity/AST.cpp
  32. 82
      libsolidity/AST.h
  33. 6
      libsolidity/AST_accept.h
  34. 104
      libsolidity/ArrayUtils.cpp
  35. 6
      libsolidity/ArrayUtils.h
  36. 2
      libsolidity/CompilerStack.cpp
  37. 107
      libsolidity/ExpressionCompiler.cpp
  38. 9
      libsolidity/InterfaceHandler.cpp
  39. 24
      libsolidity/Parser.cpp
  40. 53
      libtestutils/BlockChainLoader.cpp
  41. 2
      libweb3jsonrpc/WebThreeStubServerBase.cpp
  42. 4
      libwhisper/Message.cpp
  43. 10
      mix/qml/CodeEditorView.qml
  44. 7
      mix/qml/WebPreview.qml
  45. 39
      mix/qml/html/codeeditor.js
  46. 19
      mix/style.xml
  47. 37
      test/ClientBase.cpp
  48. 57
      test/SolidityNameAndTypeResolution.cpp
  49. 25
      test/SolidityNatspecJSON.cpp
  50. 265
      test/SolidityOptimizer.cpp
  51. 8
      test/SolidityParser.cpp
  52. 4
      test/dagger.cpp
  53. 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. 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. 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. 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. h. No space before ':' but one after it, except in the ternary operator: one on both sides.
i. Space all other operators. i. Space all other operators.
j. Braces, when used, always have their own lines and are at same indentation level as "parent" scope. 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 else
{ {
ui->downloadView->setDownloadMan(nullptr); ui->downloadView->setDownloadMan(nullptr);
writeSettings();
web3()->stopNetwork(); web3()->stopNetwork();
} }
} }

2
alethzero/Transact.cpp

@ -338,7 +338,7 @@ void Transact::rejigData()
} }
if (er.codeDeposit == CodeDeposit::Failed) if (er.codeDeposit == CodeDeposit::Failed)
{ {
bail("<div class=\"error\"><span class=\"icon\">ERROR</span> Code deposit failed due to insufficient gas</div>"); bail("<div class=\"error\"><span class=\"icon\">ERROR</span> Code deposit failed due to insufficient gas; " + QString::fromStdString(toString(er.gasForDeposit)) + " GAS &lt; " + QString::fromStdString(toString(er.depositSize)) + " bytes * " + QString::fromStdString(toString(c_createDataGas)) + "GAS/byte</div>");
return; return;
} }

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) 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) 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 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 dbdebug clog(DBChannel)
#define dbwarn clog(DBWarn)
class MemoryDB class MemoryDB
{ {

7
libethash/CMakeLists.txt

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

4
libethash/data_sizes.h

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

133
libethash/ethash.h

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

44
libethash/internal.c

@ -37,14 +37,14 @@
#include "sha3.h" #include "sha3.h"
#endif // WITH_CRYPTOPP #endif // WITH_CRYPTOPP
size_t ethash_get_datasize(const uint32_t block_number) { uint64_t ethash_get_datasize(const uint32_t block_number) {
assert(block_number / EPOCH_LENGTH < 2048); assert(block_number / ETHASH_EPOCH_LENGTH < 2048);
return dag_sizes[block_number / EPOCH_LENGTH]; return dag_sizes[block_number / ETHASH_EPOCH_LENGTH];
} }
size_t ethash_get_cachesize(const uint32_t block_number) { uint64_t ethash_get_cachesize(const uint32_t block_number) {
assert(block_number / EPOCH_LENGTH < 2048); assert(block_number / ETHASH_EPOCH_LENGTH < 2048);
return cache_sizes[block_number / EPOCH_LENGTH]; return cache_sizes[block_number / ETHASH_EPOCH_LENGTH];
} }
// Follows Sergio's "STRICT MEMORY HARD HASHING FUNCTIONS" (2014) // 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); 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++) { for (unsigned i = 0; i != num_nodes; i++) {
uint32_t const idx = nodes[i].words[0] % num_nodes; uint32_t const idx = nodes[i].words[0] % num_nodes;
node data; node data;
@ -85,10 +85,10 @@ void static ethash_compute_cache_nodes(
} }
void ethash_mkcache( void ethash_mkcache(
ethash_cache *cache, void *cache,
ethash_params const *params, ethash_params const *params,
const uint8_t seed[32]) { const uint8_t seed[32]) {
node *nodes = (node *) cache->mem; node *nodes = (node *) cache;
ethash_compute_cache_nodes(nodes, params, seed); ethash_compute_cache_nodes(nodes, params, seed);
} }
@ -96,10 +96,10 @@ void ethash_calculate_dag_item(
node *const ret, node *const ret,
const unsigned node_index, const unsigned node_index,
const struct ethash_params *params, 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)); 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]; node const *init = &cache_nodes[node_index % num_parent_nodes];
memcpy(ret, init, sizeof(node)); memcpy(ret, init, sizeof(node));
@ -114,7 +114,7 @@ void ethash_calculate_dag_item(
__m128i xmm3 = ret->xmm[3]; __m128i xmm3 = ret->xmm[3];
#endif #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; uint32_t parent_index = ((node_index ^ i) * FNV_PRIME ^ ret->words[i % NODE_WORDS]) % num_parent_nodes;
node const *parent = &cache_nodes[parent_index]; node const *parent = &cache_nodes[parent_index];
@ -150,7 +150,7 @@ void ethash_calculate_dag_item(
void ethash_compute_full_data( void ethash_compute_full_data(
void *mem, void *mem,
ethash_params const *params, 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(uint32_t) * MIX_WORDS)) == 0);
assert((params->full_size % sizeof(node)) == 0); assert((params->full_size % sizeof(node)) == 0);
node *full_nodes = mem; node *full_nodes = mem;
@ -164,7 +164,7 @@ void ethash_compute_full_data(
static void ethash_hash( static void ethash_hash(
ethash_return_value *ret, ethash_return_value *ret,
node const *full_nodes, node const *full_nodes,
ethash_cache const *cache, void const *cache,
ethash_params const *params, ethash_params const *params,
const uint8_t header_hash[32], const uint8_t header_hash[32],
const uint64_t nonce) { const uint64_t nonce) {
@ -201,7 +201,7 @@ static void ethash_hash(
num_full_pages = (unsigned) (params->full_size / page_size); 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; 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) { 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) { void ethash_get_seedhash(uint8_t seedhash[32], const uint32_t block_number) {
memset(seedhash, 0, 32); 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) for (uint32_t i = 0; i < epochs; ++i)
SHA3_256(seedhash, seedhash, 32); SHA3_256(seedhash, seedhash, 32);
} }
int ethash_quick_check_difficulty( int ethash_preliminary_check_boundary(
const uint8_t header_hash[32], const uint8_t header_hash[32],
const uint64_t nonce, const uint64_t nonce,
const uint8_t mix_hash[32], 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); 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) { 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); 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); ethash_hash(ret, NULL, cache, params, previous_hash, nonce);
} }

8
libethash/internal.h

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

45
libethcore/Ethasher.cpp

@ -41,7 +41,23 @@ using namespace eth;
Ethasher* dev::eth::Ethasher::s_this = nullptr; 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); RecursiveGuard l(x_this);
if (_header.number > c_ethashEpochLength * 2048) if (_header.number > c_ethashEpochLength * 2048)
@ -54,8 +70,7 @@ bytes const& Ethasher::cache(BlockInfo const& _header)
if (!m_caches.count(_header.seedHash())) if (!m_caches.count(_header.seedHash()))
{ {
ethash_params p = params((unsigned)_header.number); ethash_params p = params((unsigned)_header.number);
m_caches[_header.seedHash()].resize(p.cache_size); m_caches[_header.seedHash()] = ethash_new_light(&p, _header.seedHash().data());
ethash_prep_light(m_caches[_header.seedHash()].data(), &p, _header.seedHash().data());
} }
return m_caches[_header.seedHash()]; return m_caches[_header.seedHash()];
} }
@ -84,7 +99,7 @@ bytesConstRef Ethasher::full(BlockInfo const& _header)
ethash_params p = params((unsigned)_header.number); ethash_params p = params((unsigned)_header.number);
m_fulls[_header.seedHash()] = bytesRef(new byte[p.full_size], p.full_size); m_fulls[_header.seedHash()] = bytesRef(new byte[p.full_size], p.full_size);
auto c = cache(_header); 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, m_fulls[_header.seedHash()]);
writeFile(memoFile + ".info", info); writeFile(memoFile + ".info", info);
} }
@ -112,17 +127,24 @@ bool Ethasher::verify(BlockInfo const& _header)
h256 boundary = u256((bigint(1) << 256) / _header.difficulty); h256 boundary = u256((bigint(1) << 256) / _header.difficulty);
bool ret = ethash_quick_check_difficulty( bool quick = ethash_quick_check_difficulty(
_header.headerHash(WithoutNonce).data(), _header.headerHash(WithoutNonce).data(),
(uint64_t)(u64)_header.nonce, (uint64_t)(u64)_header.nonce,
_header.mixHash.data(), _header.mixHash.data(),
boundary.data()); boundary.data());
#if ETH_DEBUG #if !ETH_DEBUG
if (!quick)
return false;
#endif
auto result = eval(_header); auto result = eval(_header);
if ((result.value <= boundary && result.mixHash == _header.mixHash) != ret) bool slow = result.value <= boundary && result.mixHash == _header.mixHash;
#if ETH_DEBUG
if (!quick && slow)
{ {
cwarn << "Assertion failure coming: evaluated result gives different outcome to ethash_quick_check_difficulty"; cwarn << "WARNING: evaluated result gives true whereas ethash_quick_check_difficulty gives false.";
cwarn << "headerHash:" << _header.headerHash(WithoutNonce); cwarn << "headerHash:" << _header.headerHash(WithoutNonce);
cwarn << "nonce:" << _header.nonce; cwarn << "nonce:" << _header.nonce;
cwarn << "mixHash:" << _header.mixHash; cwarn << "mixHash:" << _header.mixHash;
@ -131,19 +153,16 @@ bool Ethasher::verify(BlockInfo const& _header)
cwarn << "result.value:" << result.value; cwarn << "result.value:" << result.value;
cwarn << "result.mixHash:" << result.mixHash; cwarn << "result.mixHash:" << result.mixHash;
} }
assert((result.value <= boundary) == ret);
if (result.value <= boundary)
assert(result.mixHash == _header.mixHash);
#endif #endif
return ret; return slow;
} }
Ethasher::Result Ethasher::eval(BlockInfo const& _header, Nonce const& _nonce) Ethasher::Result Ethasher::eval(BlockInfo const& _header, Nonce const& _nonce)
{ {
auto p = Ethasher::params(_header); auto p = Ethasher::params(_header);
ethash_return_value r; 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); // 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)}; 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 <libdevcore/Log.h>
#include <libdevcrypto/SHA3.h> #include <libdevcrypto/SHA3.h>
#include <libethash/ethash.h> // TODO: REMOVE once everything merged into this class and an opaque API can be provided. #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_ethashRevision = ETHASH_REVISION;
static const unsigned c_ethashEpochLength = EPOCH_LENGTH; static const unsigned c_ethashEpochLength = ETHASH_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
#include "Common.h" #include "Common.h"
#include "BlockInfo.h" #include "BlockInfo.h"
@ -57,10 +44,14 @@ class Ethasher
{ {
public: public:
Ethasher() {} Ethasher() {}
~Ethasher();
static Ethasher* get() { if (!s_this) s_this = new Ethasher(); return s_this; } 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); bytesConstRef full(BlockInfo const& _header);
static ethash_params params(BlockInfo const& _header); static ethash_params params(BlockInfo const& _header);
static ethash_params params(unsigned _n); static ethash_params params(unsigned _n);
@ -104,9 +95,11 @@ public:
}; };
private: private:
void killCache(h256 const& _s);
static Ethasher* s_this; static Ethasher* s_this;
RecursiveMutex x_this; RecursiveMutex x_this;
std::map<h256, bytes> m_caches; std::map<h256, LightType> m_caches;
std::map<h256, bytesRef> m_fulls; std::map<h256, bytesRef> m_fulls;
}; };

6
libethcore/Params.h

@ -29,15 +29,15 @@ namespace eth
{ {
//--- BEGIN: AUTOGENERATED FROM /feeStructure.json //--- BEGIN: AUTOGENERATED FROM /feeStructure.json
extern u256 const c_genesisDifficulty;
extern u256 const c_maximumExtraDataSize;
extern u256 const c_epochDuration;
extern u256 const c_genesisGasLimit; extern u256 const c_genesisGasLimit;
extern u256 const c_minGasLimit; extern u256 const c_minGasLimit;
extern u256 const c_gasLimitBoundDivisor; extern u256 const c_gasLimitBoundDivisor;
extern u256 const c_genesisDifficulty;
extern u256 const c_minimumDifficulty; extern u256 const c_minimumDifficulty;
extern u256 const c_difficultyBoundDivisor; extern u256 const c_difficultyBoundDivisor;
extern u256 const c_durationLimit; extern u256 const c_durationLimit;
extern u256 const c_maximumExtraDataSize;
extern u256 const c_epochDuration;
extern u256 const c_stackLimit; extern u256 const c_stackLimit;
extern u256 const c_tierStepGas[8]; ///< Once per operation, for a selection of them. extern u256 const c_tierStepGas[8]; ///< Once per operation, for a selection of them.

21
libethereum/BlockChain.cpp

@ -339,6 +339,13 @@ h256s BlockChain::import(bytes const& _block, OverlayDB const& _db)
#endif #endif
// All ok - insert into DB // 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); WriteGuard l(x_details);
m_details[newHash] = BlockDetails((unsigned)pd.number + 1, td, bi.parentHash, {}); m_details[newHash] = BlockDetails((unsigned)pd.number + 1, td, bi.parentHash, {});
m_details[bi.parentHash].children.push_back(newHash); 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 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) if (!_from || !_to)
return h256s(); return h256s();
h256s ret; h256s ret;
h256s back; h256s back;
unsigned fn = details(_from).number; unsigned fn = details(_from).number;
unsigned tn = details(_to).number; unsigned tn = details(_to).number;
// cdebug << "treeRoute" << fn << "..." << tn; cdebug << "treeRoute" << fn << "..." << tn;
h256 from = _from; h256 from = _from;
while (fn > tn) while (fn > tn)
{ {
@ -470,7 +477,7 @@ h256s BlockChain::treeRoute(h256 const& _from, h256 const& _to, h256* o_common,
ret.push_back(from); ret.push_back(from);
from = details(from).parent; from = details(from).parent;
fn--; fn--;
// cdebug << "from:" << fn << _from.abridged(); cdebug << "from:" << fn << _from.abridged();
} }
h256 to = _to; h256 to = _to;
while (fn < tn) while (fn < tn)
@ -479,12 +486,14 @@ h256s BlockChain::treeRoute(h256 const& _from, h256 const& _to, h256* o_common,
back.push_back(to); back.push_back(to);
to = details(to).parent; to = details(to).parent;
tn--; tn--;
// cdebug << "to:" << tn << _to.abridged(); cdebug << "to:" << tn << _to.abridged();
} }
while (from != to) while (from != to)
{ {
assert(from); if (!from)
assert(to); assert(from);
if (!to)
assert(to);
from = details(from).parent; from = details(from).parent;
to = details(to).parent; to = details(to).parent;
if (_pre) if (_pre)

5
libethereum/Executive.cpp

@ -46,7 +46,7 @@ u256 Executive::gasUsed() const
ExecutionResult Executive::executionResult() const ExecutionResult Executive::executionResult() const
{ {
return ExecutionResult(gasUsed(), m_excepted, m_newAddress, m_out, m_codeDeposit, m_ext ? m_ext->sub.refunds : 0); return ExecutionResult(gasUsed(), m_excepted, m_newAddress, m_out, m_codeDeposit, m_ext ? m_ext->sub.refunds : 0, m_depositSize, m_gasForDeposit);
} }
void Executive::accrueSubState(SubState& _parentContext) void Executive::accrueSubState(SubState& _parentContext)
@ -221,6 +221,8 @@ bool Executive::go(OnOpFunc const& _onOp)
if (m_isCreation) if (m_isCreation)
{ {
m_gasForDeposit = m_endGas;
m_depositSize = m_out.size();
if (m_out.size() * c_createDataGas <= m_endGas) if (m_out.size() * c_createDataGas <= m_endGas)
{ {
m_codeDeposit = CodeDeposit::Success; m_codeDeposit = CodeDeposit::Success;
@ -228,6 +230,7 @@ bool Executive::go(OnOpFunc const& _onOp)
} }
else else
{ {
m_codeDeposit = CodeDeposit::Failed; m_codeDeposit = CodeDeposit::Failed;
m_out.reset(); m_out.reset();
} }

2
libethereum/Executive.h

@ -128,6 +128,8 @@ private:
unsigned m_depth = 0; ///< The context's call-depth. unsigned m_depth = 0; ///< The context's call-depth.
bool m_isCreation = false; ///< True if the transaction creates a contract, or if create() is called. bool m_isCreation = false; ///< True if the transaction creates a contract, or if create() is called.
unsigned m_depositSize = 0; ///< Amount of code of the creation's attempted deposit.
u256 m_gasForDeposit; ///< Amount of gas remaining for the code deposit phase.
CodeDeposit m_codeDeposit = CodeDeposit::None; ///< True if an attempted deposit failed due to lack of gas. CodeDeposit m_codeDeposit = CodeDeposit::None; ///< True if an attempted deposit failed due to lack of gas.
TransactionException m_excepted = TransactionException::None; ///< Details if the VM's execution resulted in an exception. TransactionException m_excepted = TransactionException::None; ///< Details if the VM's execution resulted in an exception.
u256 m_endGas; ///< The final amount of gas for the transaction. u256 m_endGas; ///< The final amount of gas for the transaction.

42
libethereum/State.cpp

@ -111,22 +111,36 @@ State::State(OverlayDB const& _db, BlockChain const& _bc, h256 _h):
m_state(&m_db), m_state(&m_db),
m_blockReward(c_blockReward) m_blockReward(c_blockReward)
{ {
// TODO THINK: is this necessary?
m_state.init();
auto b = _bc.block(_h); auto b = _bc.block(_h);
BlockInfo bi; BlockInfo bi(b);
BlockInfo bip;
if (_h) if (!bi)
bi.populate(b); {
if (bi && bi.number) // Might be worth throwing here.
bip.populate(_bc.block(bi.parentHash)); cwarn << "Invalid block given for state population: " << _h;
if (!_h || !bip)
return; return;
m_ourAddress = bi.coinbaseAddress; }
sync(_bc, bi.parentHash, bip); if (bi.number)
enact(&b, _bc); {
// Non-genesis:
// 1. Start at parent's end state (state root).
BlockInfo bip;
bip.populate(_bc.block(bi.parentHash));
sync(_bc, bi.parentHash, bip);
// 2. Enact the block's transactions onto this state.
m_ourAddress = bi.coinbaseAddress;
enact(&b, _bc);
}
else
{
// Genesis required:
// We know there are no transactions, so just populate directly.
m_state.init();
sync(_bc, _h, bi);
}
} }
State::State(State const& _s): State::State(State const& _s):
@ -711,7 +725,7 @@ void State::commitToMine(BlockChain const& _bc)
uncommitToMine(); uncommitToMine();
// cnote << "Committing to mine on block" << m_previousBlock.hash.abridged(); // cnote << "Committing to mine on block" << m_previousBlock.hash.abridged();
#ifdef ETH_PARANOIA #if ETH_PARANOIA && 0
commit(); commit();
cnote << "Pre-reward stateRoot:" << m_state.root(); cnote << "Pre-reward stateRoot:" << m_state.root();
#endif #endif

8
libethereum/Transaction.cpp

@ -88,12 +88,12 @@ Transaction::Transaction(bytesConstRef _rlpData, CheckSignature _checkSig)
} }
catch (Exception& _e) catch (Exception& _e)
{ {
_e << errinfo_name("invalid transaction format") << BadFieldError(field,toHex(rlp[field].data().toBytes())); _e << errinfo_name("invalid transaction format") << BadFieldError(field, toHex(rlp[field].data().toBytes()));
throw; throw;
} }
} }
Address Transaction::safeSender() const noexcept Address const& Transaction::safeSender() const noexcept
{ {
try try
{ {
@ -102,11 +102,11 @@ Address Transaction::safeSender() const noexcept
catch (...) catch (...)
{ {
cwarn << "safeSender() did throw an exception: " << boost::current_exception_diagnostic_information(); cwarn << "safeSender() did throw an exception: " << boost::current_exception_diagnostic_information();
return Address(); return NullAddress;
} }
} }
Address Transaction::sender() const Address const& Transaction::sender() const
{ {
if (!m_sender) if (!m_sender)
{ {

27
libethereum/Transaction.h

@ -75,17 +75,30 @@ TransactionException toTransactionException(VMException const& _e);
struct ExecutionResult struct ExecutionResult
{ {
ExecutionResult() = default; ExecutionResult() = default;
ExecutionResult(u256 _gasUsed, TransactionException _excepted, Address _newAddress, bytesConstRef _output, CodeDeposit _codeDeposit, u256 _gasRefund): gasUsed(_gasUsed), excepted(_excepted), newAddress(_newAddress), output(_output.toBytes()), codeDeposit(_codeDeposit), gasRefunded(_gasRefund) {} ExecutionResult(u256 const& _gasUsed, TransactionException _excepted, Address const& _newAddress, bytesConstRef _output, CodeDeposit _codeDeposit, u256 const& _gasRefund, unsigned _depositSize, u256 const& _gasForDeposit):
gasUsed(_gasUsed),
excepted(_excepted),
newAddress(_newAddress),
output(_output.toBytes()),
codeDeposit(_codeDeposit),
gasRefunded(_gasRefund),
depositSize(_depositSize),
gasForDeposit(_gasForDeposit)
{}
u256 gasUsed = 0; u256 gasUsed = 0;
TransactionException excepted = TransactionException::Unknown; TransactionException excepted = TransactionException::Unknown;
Address newAddress; Address newAddress;
bytes output; bytes output;
CodeDeposit codeDeposit = CodeDeposit::None; CodeDeposit codeDeposit = CodeDeposit::None;
u256 gasRefunded = 0; u256 gasRefunded = 0;
unsigned depositSize = 0;
u256 gasForDeposit;
}; };
std::ostream& operator<<(std::ostream& _out, ExecutionResult const& _er); std::ostream& operator<<(std::ostream& _out, ExecutionResult const& _er);
static const Address NullAddress;
/// Encodes a transaction, ready to be exported to or freshly imported from RLP. /// Encodes a transaction, ready to be exported to or freshly imported from RLP.
class Transaction class Transaction
{ {
@ -94,16 +107,16 @@ public:
Transaction() {} Transaction() {}
/// Constructs a signed message-call transaction. /// Constructs a signed message-call transaction.
Transaction(u256 _value, u256 _gasPrice, u256 _gas, Address const& _dest, bytes const& _data, u256 _nonce, Secret const& _secret): m_type(MessageCall), m_nonce(_nonce), m_value(_value), m_receiveAddress(_dest), m_gasPrice(_gasPrice), m_gas(_gas), m_data(_data) { sign(_secret); } Transaction(u256 const& _value, u256 const& _gasPrice, u256 const& _gas, Address const& _dest, bytes const& _data, u256 const& _nonce, Secret const& _secret): m_type(MessageCall), m_nonce(_nonce), m_value(_value), m_receiveAddress(_dest), m_gasPrice(_gasPrice), m_gas(_gas), m_data(_data) { sign(_secret); }
/// Constructs a signed contract-creation transaction. /// Constructs a signed contract-creation transaction.
Transaction(u256 _value, u256 _gasPrice, u256 _gas, bytes const& _data, u256 _nonce, Secret const& _secret): m_type(ContractCreation), m_nonce(_nonce), m_value(_value), m_gasPrice(_gasPrice), m_gas(_gas), m_data(_data) { sign(_secret); } Transaction(u256 const& _value, u256 const& _gasPrice, u256 const& _gas, bytes const& _data, u256 const& _nonce, Secret const& _secret): m_type(ContractCreation), m_nonce(_nonce), m_value(_value), m_gasPrice(_gasPrice), m_gas(_gas), m_data(_data) { sign(_secret); }
/// Constructs an unsigned message-call transaction. /// Constructs an unsigned message-call transaction.
Transaction(u256 _value, u256 _gasPrice, u256 _gas, Address const& _dest, bytes const& _data): m_type(MessageCall), m_value(_value), m_receiveAddress(_dest), m_gasPrice(_gasPrice), m_gas(_gas), m_data(_data) {} Transaction(u256 const& _value, u256 const& _gasPrice, u256 const& _gas, Address const& _dest, bytes const& _data): m_type(MessageCall), m_value(_value), m_receiveAddress(_dest), m_gasPrice(_gasPrice), m_gas(_gas), m_data(_data) {}
/// Constructs an unsigned contract-creation transaction. /// Constructs an unsigned contract-creation transaction.
Transaction(u256 _value, u256 _gasPrice, u256 _gas, bytes const& _data): m_type(ContractCreation), m_value(_value), m_gasPrice(_gasPrice), m_gas(_gas), m_data(_data) {} Transaction(u256 const& _value, u256 const& _gasPrice, u256 const& _gas, bytes const& _data): m_type(ContractCreation), m_value(_value), m_gasPrice(_gasPrice), m_gas(_gas), m_data(_data) {}
/// Constructs a transaction from the given RLP. /// Constructs a transaction from the given RLP.
explicit Transaction(bytesConstRef _rlp, CheckSignature _checkSig); explicit Transaction(bytesConstRef _rlp, CheckSignature _checkSig);
@ -118,9 +131,9 @@ public:
bool operator!=(Transaction const& _c) const { return !operator==(_c); } bool operator!=(Transaction const& _c) const { return !operator==(_c); }
/// @returns sender of the transaction from the signature (and hash). /// @returns sender of the transaction from the signature (and hash).
Address sender() const; Address const& sender() const;
/// Like sender() but will never throw. @returns a null Address if the signature is invalid. /// Like sender() but will never throw. @returns a null Address if the signature is invalid.
Address safeSender() const noexcept; Address const& safeSender() const noexcept;
/// @returns true if transaction is non-null. /// @returns true if transaction is non-null.
explicit operator bool() const { return m_type != NullTransaction; } explicit operator bool() const { return m_type != NullTransaction; }

15
libevmcore/Assembly.cpp

@ -187,18 +187,7 @@ Assembly& Assembly::optimise(bool _enable)
{ {
if (!_enable) if (!_enable)
return *this; return *this;
std::vector<pair<AssemblyItems, function<AssemblyItems(AssemblyItemsConstRef)>>> rules = 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 {}; } },
};
// jump to next instruction // 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].m_data == m[2].m_data) return {m[2]}; else return m.toVector(); }});
@ -235,8 +224,6 @@ Assembly& Assembly::optimise(bool _enable)
*orig = move(*moveIter); *orig = move(*moveIter);
iter = m_items.erase(orig, iter); iter = m_items.erase(orig, iter);
} }
if (iter != m_items.end())
++iter;
} }
for (unsigned i = 0; i < m_items.size(); ++i) for (unsigned i = 0; i < m_items.size(); ++i)

303
libevmcore/CommonSubexpressionEliminator.cpp

@ -32,6 +32,8 @@ using namespace dev::eth;
vector<AssemblyItem> CommonSubexpressionEliminator::getOptimizedItems() vector<AssemblyItem> CommonSubexpressionEliminator::getOptimizedItems()
{ {
optimizeBreakingItem();
map<int, ExpressionClasses::Id> initialStackContents; map<int, ExpressionClasses::Id> initialStackContents;
map<int, ExpressionClasses::Id> targetStackContents; map<int, ExpressionClasses::Id> targetStackContents;
int minHeight = m_stackHeight + 1; int minHeight = m_stackHeight + 1;
@ -45,19 +47,27 @@ vector<AssemblyItem> CommonSubexpressionEliminator::getOptimizedItems()
// Debug info: // Debug info:
//stream(cout, initialStackContents, targetStackContents); //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& CommonSubexpressionEliminator::stream(
ostream& _out, ostream& _out,
map<int, ExpressionClasses::Id> _currentStack, map<int, ExpressionClasses::Id> _initialStack,
map<int, ExpressionClasses::Id> _targetStack map<int, ExpressionClasses::Id> _targetStack
) const ) const
{ {
auto streamExpressionClass = [this](ostream& _out, ExpressionClasses::Id _id) auto streamExpressionClass = [this](ostream& _out, ExpressionClasses::Id _id)
{ {
auto const& expr = m_expressionClasses.representative(_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 << "("; _out << "(";
for (ExpressionClasses::Id arg: expr.arguments) for (ExpressionClasses::Id arg: expr.arguments)
_out << dec << arg << ","; _out << dec << arg << ",";
@ -66,18 +76,12 @@ ostream& CommonSubexpressionEliminator::stream(
_out << "Optimizer analysis:" << endl; _out << "Optimizer analysis:" << endl;
_out << "Final stack height: " << dec << m_stackHeight << 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; _out << "Equivalence classes: " << endl;
for (ExpressionClasses::Id eqClass = 0; eqClass < m_expressionClasses.size(); ++eqClass) for (ExpressionClasses::Id eqClass = 0; eqClass < m_expressionClasses.size(); ++eqClass)
streamExpressionClass(_out, eqClass); streamExpressionClass(_out, eqClass);
_out << "Current stack: " << endl; _out << "Initial stack: " << endl;
for (auto const& it: _currentStack) for (auto const& it: _initialStack)
{ {
_out << " " << dec << it.first << ": "; _out << " " << dec << it.first << ": ";
streamExpressionClass(_out, it.second); streamExpressionClass(_out, it.second);
@ -92,13 +96,12 @@ ostream& CommonSubexpressionEliminator::stream(
return _out; return _out;
} }
void CommonSubexpressionEliminator::feedItem(AssemblyItem const& _item) void CommonSubexpressionEliminator::feedItem(AssemblyItem const& _item, bool _copyItem)
{ {
if (_item.type() != Operation) if (_item.type() != Operation)
{ {
if (_item.deposit() != 1) assertThrow(_item.deposit() == 1, InvalidDeposit, "");
BOOST_THROW_EXCEPTION(InvalidDeposit()); setStackElement(++m_stackHeight, m_expressionClasses.find(_item, {}, _copyItem));
setStackElement(++m_stackHeight, m_expressionClasses.find(_item, {}));
} }
else else
{ {
@ -119,12 +122,47 @@ void CommonSubexpressionEliminator::feedItem(AssemblyItem const& _item)
vector<ExpressionClasses::Id> arguments(info.args); vector<ExpressionClasses::Id> arguments(info.args);
for (int i = 0; i < info.args; ++i) for (int i = 0; i < info.args; ++i)
arguments[i] = stackElement(m_stackHeight - 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(); 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) void CommonSubexpressionEliminator::setStackElement(int _stackHeight, ExpressionClasses::Id _class)
{ {
m_stackElements[_stackHeight] = _class; m_stackElements[_stackHeight] = _class;
@ -132,8 +170,7 @@ void CommonSubexpressionEliminator::setStackElement(int _stackHeight, Expression
void CommonSubexpressionEliminator::swapStackElements(int _stackHeightA, int _stackHeightB) void CommonSubexpressionEliminator::swapStackElements(int _stackHeightA, int _stackHeightB)
{ {
if (_stackHeightA == _stackHeightB) assertThrow(_stackHeightA != _stackHeightB, OptimizerException, "Swap on same stack elements.");
BOOST_THROW_EXCEPTION(OptimizerException() << errinfo_comment("Swap on same stack elements."));
// ensure they are created // ensure they are created
stackElement(_stackHeightA); stackElement(_stackHeightA);
stackElement(_stackHeightB); stackElement(_stackHeightB);
@ -157,6 +194,60 @@ ExpressionClasses::Id CommonSubexpressionEliminator::initialStackElement(int _st
return m_expressionClasses.find(AssemblyItem(dupInstruction(1 - _stackHeight))); return m_expressionClasses.find(AssemblyItem(dupInstruction(1 - _stackHeight)));
} }
void CommonSubexpressionEliminator::storeInStorage(ExpressionClasses::Id _slot, ExpressionClasses::Id _value)
{
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++;
}
ExpressionClasses::Id CommonSubexpressionEliminator::loadFromStorage(ExpressionClasses::Id _slot)
{
if (m_storageContent.count(_slot))
return m_storageContent.at(_slot);
else
return m_storageContent[_slot] = m_expressionClasses.find(Instruction::SLOAD, {_slot}, true, m_sequenceNumber);
}
void CommonSubexpressionEliminator::storeInMemory(ExpressionClasses::Id _slot, ExpressionClasses::Id _value)
{
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++;
}
ExpressionClasses::Id CommonSubexpressionEliminator::loadFromMemory(ExpressionClasses::Id _slot)
{
if (m_memoryContent.count(_slot))
return m_memoryContent.at(_slot);
else
return m_memoryContent[_slot] = m_expressionClasses.find(Instruction::MLOAD, {_slot}, true, m_sequenceNumber);
}
bool SemanticInformation::breaksBasicBlock(AssemblyItem const& _item) bool SemanticInformation::breaksBasicBlock(AssemblyItem const& _item)
{ {
switch (_item.type()) switch (_item.type())
@ -179,7 +270,19 @@ bool SemanticInformation::breaksBasicBlock(AssemblyItem const& _item)
return false; return false;
if (_item.instruction() == Instruction::GAS || _item.instruction() == Instruction::PC) if (_item.instruction() == Instruction::GAS || _item.instruction() == Instruction::PC)
return true; // GAS and PC assume a specific order of opcodes 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()); 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 // the second requirement will be lifted once it is implemented
return info.sideEffects || info.args > 2; return info.sideEffects || info.args > 2;
} }
@ -218,6 +321,16 @@ bool SemanticInformation::isSwapInstruction(AssemblyItem const& _item)
return Instruction::SWAP1 <= _item.instruction() && _item.instruction() <= Instruction::SWAP16; return Instruction::SWAP1 <= _item.instruction() && _item.instruction() <= Instruction::SWAP16;
} }
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( AssemblyItems CSECodeGenerator::generateCode(
map<int, ExpressionClasses::Id> const& _initialStack, map<int, ExpressionClasses::Id> const& _initialStack,
map<int, ExpressionClasses::Id> const& _targetStackContents map<int, ExpressionClasses::Id> const& _targetStackContents
@ -230,26 +343,40 @@ AssemblyItems CSECodeGenerator::generateCode(
// @todo: provide information about the positions of copies of class elements // @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) for (auto const& targetItem: _targetStackContents)
{ {
m_finalClasses.insert(targetItem.second); m_finalClasses.insert(targetItem.second);
addDependencies(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) for (auto const& targetItem: _targetStackContents)
{ {
removeStackTopIfPossible();
int position = generateClassElement(targetItem.second); int position = generateClassElement(targetItem.second);
assertThrow(position != c_invalidPosition, OptimizerException, "");
if (position == targetItem.first) if (position == targetItem.first)
continue; continue;
if (position < targetItem.first) if (position < targetItem.first)
// it is already at its target, we need another copy // it is already at its target, we need another copy
appendDup(position); appendDup(position);
else else
appendSwapOrRemove(position); appendOrRemoveSwap(position);
appendSwapOrRemove(targetItem.first); appendOrRemoveSwap(targetItem.first);
} }
// remove surplus elements // remove surplus elements
@ -270,23 +397,59 @@ AssemblyItems CSECodeGenerator::generateCode(
// neither initial no target stack, no change in height // neither initial no target stack, no change in height
finalHeight = 0; finalHeight = 0;
assertThrow(finalHeight == m_stackHeight, OptimizerException, "Incorrect final stack height."); assertThrow(finalHeight == m_stackHeight, OptimizerException, "Incorrect final stack height.");
return m_generatedItems; return m_generatedItems;
} }
void CSECodeGenerator::addDependencies(ExpressionClasses::Id _c) void CSECodeGenerator::addDependencies(ExpressionClasses::Id _c)
{ {
if (m_neededBy.count(_c)) if (m_neededBy.count(_c))
return; return; // we already computed the dependencies for _c
for (ExpressionClasses::Id argument: m_expressionClasses.representative(_c).arguments) ExpressionClasses::Expression expr = m_expressionClasses.representative(_c);
for (ExpressionClasses::Id argument: expr.arguments)
{ {
addDependencies(argument); addDependencies(argument);
m_neededBy.insert(make_pair(argument, _c)); 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)) if (m_classPositions.count(_c))
{ {
assertThrow( assertThrow(
@ -296,7 +459,13 @@ int CSECodeGenerator::generateClassElement(ExpressionClasses::Id _c)
); );
return m_classPositions[_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)) for (ExpressionClasses::Id arg: boost::adaptors::reverse(arguments))
generateClassElement(arg); generateClassElement(arg);
@ -307,42 +476,42 @@ int CSECodeGenerator::generateClassElement(ExpressionClasses::Id _c)
if (arguments.size() == 1) if (arguments.size() == 1)
{ {
if (canBeRemoved(arguments[0], _c)) if (canBeRemoved(arguments[0], _c))
appendSwapOrRemove(generateClassElement(arguments[0])); appendOrRemoveSwap(classElementPosition(arguments[0]));
else else
appendDup(generateClassElement(arguments[0])); appendDup(classElementPosition(arguments[0]));
} }
else if (arguments.size() == 2) else if (arguments.size() == 2)
{ {
if (canBeRemoved(arguments[1], _c)) if (canBeRemoved(arguments[1], _c))
{ {
appendSwapOrRemove(generateClassElement(arguments[1])); appendOrRemoveSwap(classElementPosition(arguments[1]));
if (arguments[0] == arguments[1]) if (arguments[0] == arguments[1])
appendDup(m_stackHeight); appendDup(m_stackHeight);
else if (canBeRemoved(arguments[0], _c)) else if (canBeRemoved(arguments[0], _c))
{ {
appendSwapOrRemove(m_stackHeight - 1); appendOrRemoveSwap(m_stackHeight - 1);
appendSwapOrRemove(generateClassElement(arguments[0])); appendOrRemoveSwap(classElementPosition(arguments[0]));
} }
else else
appendDup(generateClassElement(arguments[0])); appendDup(classElementPosition(arguments[0]));
} }
else else
{ {
if (arguments[0] == arguments[1]) if (arguments[0] == arguments[1])
{ {
appendDup(generateClassElement(arguments[0])); appendDup(classElementPosition(arguments[0]));
appendDup(m_stackHeight); appendDup(m_stackHeight);
} }
else if (canBeRemoved(arguments[0], _c)) else if (canBeRemoved(arguments[0], _c))
{ {
appendSwapOrRemove(generateClassElement(arguments[0])); appendOrRemoveSwap(classElementPosition(arguments[0]));
appendDup(generateClassElement(arguments[1])); appendDup(classElementPosition(arguments[1]));
appendSwapOrRemove(m_stackHeight - 1); appendOrRemoveSwap(m_stackHeight - 1);
} }
else else
{ {
appendDup(generateClassElement(arguments[1])); appendDup(classElementPosition(arguments[1]));
appendDup(generateClassElement(arguments[0])); appendDup(classElementPosition(arguments[0]));
} }
} }
} }
@ -355,20 +524,41 @@ int CSECodeGenerator::generateClassElement(ExpressionClasses::Id _c)
for (size_t i = 0; i < arguments.size(); ++i) for (size_t i = 0; i < arguments.size(); ++i)
assertThrow(m_stack[m_stackHeight - i] == arguments[i], OptimizerException, "Expected arguments not present." ); assertThrow(m_stack[m_stackHeight - i] == arguments[i], OptimizerException, "Expected arguments not present." );
AssemblyItem const& item = *m_expressionClasses.representative(_c).item; while (SemanticInformation::isCommutativeOperation(*expr.item) &&
while (SemanticInformation::isCommutativeOperation(item) &&
!m_generatedItems.empty() && !m_generatedItems.empty() &&
m_generatedItems.back() == AssemblyItem(Instruction::SWAP1)) m_generatedItems.back() == AssemblyItem(Instruction::SWAP1))
// this will not append a swap but remove the one that is already there // 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) for (auto arg: arguments)
if (canBeRemoved(arg, _c)) if (canBeRemoved(arg, _c))
m_classPositions[arg] = c_invalidPosition; m_classPositions[arg] = c_invalidPosition;
for (size_t i = 0; i < arguments.size(); ++i) for (size_t i = 0; i < arguments.size(); ++i)
m_stack.erase(m_stackHeight - i); m_stack.erase(m_stackHeight - i);
appendItem(*m_expressionClasses.representative(_c).item); appendItem(*expr.item);
m_stack[m_stackHeight] = _c; if (expr.item->type() != Operation || instructionInfo(expr.item->instruction()).ret == 1)
return m_classPositions[_c] = m_stackHeight; {
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) bool CSECodeGenerator::canBeRemoved(ExpressionClasses::Id _element, ExpressionClasses::Id _result)
@ -401,22 +591,23 @@ bool CSECodeGenerator::removeStackTopIfPossible()
void CSECodeGenerator::appendDup(int _fromPosition) void CSECodeGenerator::appendDup(int _fromPosition)
{ {
int nr = 1 + m_stackHeight - _fromPosition; assertThrow(_fromPosition != c_invalidPosition, OptimizerException, "");
assertThrow(nr <= 16, StackTooDeepException, "Stack too deep."); int instructionNum = 1 + m_stackHeight - _fromPosition;
assertThrow(1 <= nr, OptimizerException, "Invalid stack access."); assertThrow(instructionNum <= 16, StackTooDeepException, "Stack too deep.");
m_generatedItems.push_back(AssemblyItem(dupInstruction(nr))); assertThrow(1 <= instructionNum, OptimizerException, "Invalid stack access.");
m_stackHeight++; appendItem(AssemblyItem(dupInstruction(instructionNum)));
m_stack[m_stackHeight] = m_stack[_fromPosition]; 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) if (_fromPosition == m_stackHeight)
return; return;
int nr = m_stackHeight - _fromPosition; int instructionNum = m_stackHeight - _fromPosition;
assertThrow(nr <= 16, StackTooDeepException, "Stack too deep."); assertThrow(instructionNum <= 16, StackTooDeepException, "Stack too deep.");
assertThrow(1 <= nr, OptimizerException, "Invalid stack access."); assertThrow(1 <= instructionNum, OptimizerException, "Invalid stack access.");
m_generatedItems.push_back(AssemblyItem(swapInstruction(nr))); appendItem(AssemblyItem(swapInstruction(instructionNum)));
// The value of a class can be present in multiple locations on the stack. We only update the // 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 // "canonical" one that is tracked by m_classPositions
if (m_classPositions[m_stack[m_stackHeight]] == m_stackHeight) if (m_classPositions[m_stack[m_stackHeight]] == m_stackHeight)

84
libevmcore/CommonSubexpressionEliminator.h

@ -25,6 +25,8 @@
#include <vector> #include <vector>
#include <map> #include <map>
#include <set>
#include <tuple>
#include <ostream> #include <ostream>
#include <libdevcore/CommonIO.h> #include <libdevcore/CommonIO.h>
#include <libdevcore/Exceptions.h> #include <libdevcore/Exceptions.h>
@ -44,9 +46,9 @@ using AssemblyItems = std::vector<AssemblyItem>;
* known to be equal only once. * known to be equal only once.
* *
* The general workings are that for each assembly item that is fed into the eliminator, an * 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 * equivalence class is derived from the operation and the equivalence class of its arguments.
* it is assigned to the next sequence number of a stack item. DUPi, SWAPi and some arithmetic * DUPi, SWAPi and some arithmetic instructions are used to infer equivalences while these
* instructions are used to infer equivalences while these classes are determined. * classes are determined.
* *
* When the list of optimized items is requested, they are generated in a bottom-up fashion, * 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. * adding code for equivalence classes that were not yet computed.
@ -54,6 +56,21 @@ using AssemblyItems = std::vector<AssemblyItem>;
class CommonSubexpressionEliminator class CommonSubexpressionEliminator
{ {
public: 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 /// 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. /// item that must be fed into a new instance of the eliminator.
template <class _AssemblyItemIterator> template <class _AssemblyItemIterator>
@ -65,13 +82,16 @@ public:
/// Streams debugging information to @a _out. /// Streams debugging information to @a _out.
std::ostream& stream( std::ostream& stream(
std::ostream& _out, 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>() std::map<int, ExpressionClasses::Id> _targetStack = std::map<int, ExpressionClasses::Id>()
) const; ) const;
private: private:
/// Feeds the item into the system for analysis. /// 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 /// Simplifies the given item using
/// Assigns a new equivalence class to the next sequence number of the given stack element. /// Assigns a new equivalence class to the next sequence number of the given stack element.
@ -85,12 +105,38 @@ private:
/// (must not be positive). /// (must not be positive).
ExpressionClasses::Id initialStackElement(int _stackHeight); 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. /// Current stack height, can be negative.
int m_stackHeight = 0; int m_stackHeight = 0;
/// Current stack layout, mapping stack height -> equivalence class /// Current stack layout, mapping stack height -> equivalence class
std::map<int, ExpressionClasses::Id> m_stackElements; 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. /// Structure containing the classes of equivalent expressions.
ExpressionClasses m_expressionClasses; ExpressionClasses m_expressionClasses;
/// 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 +160,16 @@ struct SemanticInformation
class CSECodeGenerator class CSECodeGenerator
{ {
public: public:
CSECodeGenerator(ExpressionClasses const& _expressionClasses): using StoreOperation = CommonSubexpressionEliminator::StoreOperation;
m_expressionClasses(_expressionClasses) 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 /// @returns the assembly items generated from the given requirements
/// @param _initialStack current contents of the stack (up to stack height of zero) /// @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 _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. /// @note should only be called once on each object.
AssemblyItems generateCode( AssemblyItems generateCode(
std::map<int, ExpressionClasses::Id> const& _initialStack, std::map<int, ExpressionClasses::Id> const& _initialStack,
@ -133,8 +181,13 @@ private:
void addDependencies(ExpressionClasses::Id _c); void addDependencies(ExpressionClasses::Id _c);
/// Produce code that generates the given element if it is not yet present. /// Produce code that generates the given element if it is not yet present.
/// @returns the stack position of the element. /// @returns the stack position of the element or c_invalidPosition if it does not actually
int generateClassElement(ExpressionClasses::Id _c); /// 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. /// @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)); bool canBeRemoved(ExpressionClasses::Id _element, ExpressionClasses::Id _result = ExpressionClasses::Id(-1));
@ -146,7 +199,7 @@ private:
void appendDup(int _fromPosition); void appendDup(int _fromPosition);
/// Appends a swap instruction to m_generatedItems to retrieve the element at the given stack position. /// 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. /// @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. /// Appends the given assembly item.
void appendItem(AssemblyItem const& _item); void appendItem(AssemblyItem const& _item);
@ -163,7 +216,10 @@ private:
std::map<ExpressionClasses::Id, int> m_classPositions; std::map<ExpressionClasses::Id, int> m_classPositions;
/// The actual eqivalence class items and how to compute them. /// 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. /// The set of equivalence classes that should be present on the stack at the end.
std::set<ExpressionClasses::Id> m_finalClasses; std::set<ExpressionClasses::Id> m_finalClasses;
}; };
@ -176,6 +232,8 @@ _AssemblyItemIterator CommonSubexpressionEliminator::feedItems(
{ {
for (; _iterator != _end && !SemanticInformation::breaksBasicBlock(*_iterator); ++_iterator) for (; _iterator != _end && !SemanticInformation::breaksBasicBlock(*_iterator); ++_iterator)
feedItem(*_iterator); feedItem(*_iterator);
if (_iterator != _end)
m_breakingItem = &(*_iterator++);
return _iterator; return _iterator;
} }

100
libevmcore/ExpressionClasses.cpp

@ -39,41 +39,71 @@ bool ExpressionClasses::Expression::operator<(ExpressionClasses::Expression cons
{ {
auto type = item->type(); auto type = item->type();
auto otherType = _other.item->type(); auto otherType = _other.item->type();
return std::tie(type, item->data(), arguments) < return std::tie(type, item->data(), arguments, sequenceNumber) <
std::tie(otherType, _other.item->data(), _other.arguments); 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; Expression exp;
exp.id = Id(-1); exp.id = Id(-1);
exp.item = &_item; exp.item = &_item;
exp.arguments = _arguments; exp.arguments = _arguments;
exp.sequenceNumber = _sequenceNumber;
if (SemanticInformation::isCommutativeOperation(_item)) if (SemanticInformation::isCommutativeOperation(_item))
sort(exp.arguments.begin(), exp.arguments.end()); sort(exp.arguments.begin(), exp.arguments.end());
//@todo store all class members (not only the representatives) in an efficient data structure to search here auto it = m_expressions.find(exp);
for (Expression const& e: m_representatives) if (it != m_expressions.end())
if (!(e < exp || exp < e)) return it->id;
return e.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)); m_spareAssemblyItem.push_back(make_shared<AssemblyItem>(_item));
exp.item = m_spareAssemblyItem.back().get(); exp.item = m_spareAssemblyItem.back().get();
} }
ExpressionClasses::Id id = tryToSimplify(exp); ExpressionClasses::Id id = tryToSimplify(exp);
if (id < m_representatives.size()) if (id < m_representatives.size())
return id; exp.id = id;
else
exp.id = m_representatives.size(); {
m_representatives.push_back(exp); exp.id = m_representatives.size();
m_representatives.push_back(exp);
}
m_expressions.insert(exp);
return exp.id; 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 string ExpressionClasses::fullDAGToString(ExpressionClasses::Id _id) const
{ {
Expression const& expr = representative(_id); Expression const& expr = representative(_id);
@ -189,27 +219,46 @@ Rules::Rules()
// Moving constants to the outside, order matters here! // Moving constants to the outside, order matters here!
// we need actions that return expressions (or patterns?) here, and we need also reversed rules // we need actions that return expressions (or patterns?) here, and we need also reversed rules
// (X+A)+B -> X+(A+B) // (X+A)+B -> X+(A+B)
m_rules.push_back({ m_rules += vector<pair<Pattern, function<Pattern()>>>{{
{op, {{op, {X, A}}, B}}, {op, {{op, {X, A}}, B}},
[=]() -> Pattern { return {op, {X, fun(A.d(), B.d())}}; } [=]() -> Pattern { return {op, {X, fun(A.d(), B.d())}}; }
}); }, {
// X+(Y+A) -> (X+Y)+A // X+(Y+A) -> (X+Y)+A
m_rules.push_back({
{op, {{op, {X, A}}, Y}}, {op, {{op, {X, A}}, Y}},
[=]() -> Pattern { return {op, {{op, {X, Y}}, A}}; } [=]() -> Pattern { return {op, {{op, {X, Y}}, A}}; }
}); }, {
// For now, we still need explicit commutativity for the inner pattern // For now, we still need explicit commutativity for the inner pattern
m_rules.push_back({
{op, {{op, {A, X}}, B}}, {op, {{op, {A, X}}, B}},
[=]() -> Pattern { return {op, {X, fun(A.d(), B.d())}}; } [=]() -> Pattern { return {op, {X, fun(A.d(), B.d())}}; }
}); }, {
m_rules.push_back({
{op, {{op, {A, X}}, Y}}, {op, {{op, {A, X}}, Y}},
[=]() -> Pattern { return {op, {{op, {X, Y}}, A}}; } [=]() -> 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) ExpressionClasses::Id ExpressionClasses::tryToSimplify(Expression const& _expr, bool _secondRun)
@ -231,7 +280,7 @@ ExpressionClasses::Id ExpressionClasses::tryToSimplify(Expression const& _expr,
//cout << ")" << endl; //cout << ")" << endl;
//cout << "with rule " << rule.first.toString() << endl; //cout << "with rule " << rule.first.toString() << endl;
//ExpressionTemplate t(rule.second()); //ExpressionTemplate t(rule.second());
//cout << "to" << rule.second().toString() << endl; //cout << "to " << rule.second().toString() << endl;
return rebuildExpression(ExpressionTemplate(rule.second())); return rebuildExpression(ExpressionTemplate(rule.second()));
} }
} }
@ -254,8 +303,7 @@ ExpressionClasses::Id ExpressionClasses::rebuildExpression(ExpressionTemplate co
Ids arguments; Ids arguments;
for (ExpressionTemplate const& t: _template.arguments) for (ExpressionTemplate const& t: _template.arguments)
arguments.push_back(rebuildExpression(t)); arguments.push_back(rebuildExpression(t));
m_spareAssemblyItem.push_back(make_shared<AssemblyItem>(_template.item)); return find(_template.item, arguments);
return find(*m_spareAssemblyItem.back(), arguments);
} }

20
libevmcore/ExpressionClasses.h

@ -52,17 +52,33 @@ public:
Id id; Id id;
AssemblyItem const* item; AssemblyItem const* item;
Ids arguments; 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; bool operator<(Expression const& _other) const;
}; };
/// Retrieves the id of the expression equivalence class resulting from the given item applied to the /// Retrieves the id of the expression equivalence class resulting from the given item applied to the
/// given classes, might also create a new one. /// 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. /// @returns the canonical representative of an expression class.
Expression const& representative(Id _id) const { return m_representatives.at(_id); } Expression const& representative(Id _id) const { return m_representatives.at(_id); }
/// @returns the number of classes. /// @returns the number of classes.
Id size() const { return m_representatives.size(); } 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; std::string fullDAGToString(Id _id) const;
private: private:
@ -78,6 +94,8 @@ private:
/// Expression equivalence class representatives - we only store one item of an equivalence. /// Expression equivalence class representatives - we only store one item of an equivalence.
std::vector<Expression> m_representatives; std::vector<Expression> m_representatives;
/// All expression ever encountered.
std::set<Expression> m_expressions;
std::vector<std::shared_ptr<AssemblyItem>> m_spareAssemblyItem; std::vector<std::shared_ptr<AssemblyItem>> m_spareAssemblyItem;
}; };

69
libp2p/Host.cpp

@ -678,9 +678,6 @@ void Host::disconnectLatePeers()
bytes Host::saveNetwork() const bytes Host::saveNetwork() const
{ {
if (!m_nodeTable)
return bytes();
std::list<Peer> peers; std::list<Peer> peers;
{ {
RecursiveGuard l(x_sessions); RecursiveGuard l(x_sessions);
@ -692,27 +689,22 @@ bytes Host::saveNetwork() const
RLPStream network; RLPStream network;
int count = 0; int count = 0;
for (auto const& p: peers)
{ {
RecursiveGuard l(x_sessions); // Only save peers which have connected within 2 days, with properly-advertised port and public IP address
for (auto const& p: peers) // 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 && network.appendList(10);
// TODO: alpha: if/how to save private addresses network << endpoint.port() << p.id << p.required
// Only save peers which have connected within 2 days, with properly-advertised port and public IP address << chrono::duration_cast<chrono::seconds>(p.m_lastConnected.time_since_epoch()).count()
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())) << 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;
network.appendList(10); count++;
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++;
}
} }
} }
@ -731,10 +723,13 @@ bytes Host::saveNetwork() const
count++; count++;
} }
} }
// else: TODO: use previous configuration if available
RLPStream ret(3); RLPStream ret(3);
ret << dev::p2p::c_protocolVersion << m_alias.secret(); 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(); return ret.out();
} }
@ -757,22 +752,14 @@ void Host::restoreNetwork(bytesConstRef _b)
for (auto i: r[2]) 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::tcp::endpoint tcp;
bi::udp::endpoint udp; 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>());
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.
if (isPrivateAddress(tcp.address()) || isPrivateAddress(udp.address())) if (isPrivateAddress(tcp.address()) || isPrivateAddress(udp.address()))
continue; continue;
@ -783,6 +770,7 @@ void Host::restoreNetwork(bytesConstRef _b)
{ {
shared_ptr<Peer> p = make_shared<Peer>(); shared_ptr<Peer> p = make_shared<Peer>();
p->id = id; 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_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_lastAttempted = chrono::system_clock::time_point(chrono::seconds(i[5].toInt<unsigned>()));
p->m_failedAttempts = i[6].toInt<unsigned>(); p->m_failedAttempts = i[6].toInt<unsigned>();
@ -792,7 +780,10 @@ void Host::restoreNetwork(bytesConstRef _b)
p->endpoint.tcp = tcp; p->endpoint.tcp = tcp;
p->endpoint.udp = udp; p->endpoint.udp = udp;
m_peers[p->id] = p; 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) KeyPair Host::networkAlias(bytesConstRef _b)
{ {
RLP r(_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())))); return move(KeyPair(move(Secret(r[1].toBytes()))));
else else
return move(KeyPair::create()); return move(KeyPair::create());

2
libp2p/Host.h

@ -96,7 +96,7 @@ public:
static std::string pocHost(); static std::string pocHost();
/// Register a peer-capability; all new peer connections will have this capability. /// Register a peer-capability; all new peer connections will have this capability.
template <class T> std::shared_ptr<T> registerCapability(T* _t) { _t->m_host = this; auto ret = std::shared_ptr<T>(_t); m_capabilities[std::make_pair(T::staticName(), T::staticVersion())] = ret; return ret; } template <class T> std::shared_ptr<T> registerCapability(T* _t) { _t->m_host = this; std::shared_ptr<T> ret(_t); m_capabilities[std::make_pair(T::staticName(), T::staticVersion())] = ret; return ret; }
bool haveCapability(CapDesc const& _name) const { return m_capabilities.count(_name) != 0; } bool haveCapability(CapDesc const& _name) const { return m_capabilities.count(_name) != 0; }
CapDescs caps() const { CapDescs ret; for (auto const& i: m_capabilities) ret.push_back(i.first); return ret; } CapDescs caps() const { CapDescs ret; for (auto const& i: m_capabilities) ret.push_back(i.first); return ret; }

34
libsolidity/AST.cpp

@ -21,6 +21,7 @@
*/ */
#include <algorithm> #include <algorithm>
#include <boost/range/adaptor/reversed.hpp>
#include <libsolidity/Utils.h> #include <libsolidity/Utils.h>
#include <libsolidity/AST.h> #include <libsolidity/AST.h>
#include <libsolidity/ASTVisitor.h> #include <libsolidity/ASTVisitor.h>
@ -52,6 +53,7 @@ void ContractDefinition::checkTypeRequirements()
baseSpecifier->checkTypeRequirements(); baseSpecifier->checkTypeRequirements();
checkIllegalOverrides(); checkIllegalOverrides();
checkAbstractFunctions();
FunctionDefinition const* constructor = getConstructor(); FunctionDefinition const* constructor = getConstructor();
if (constructor && !constructor->getReturnParameters().empty()) if (constructor && !constructor->getReturnParameters().empty())
@ -60,6 +62,7 @@ void ContractDefinition::checkTypeRequirements()
FunctionDefinition const* fallbackFunction = nullptr; FunctionDefinition const* fallbackFunction = nullptr;
for (ASTPointer<FunctionDefinition> const& function: getDefinedFunctions()) for (ASTPointer<FunctionDefinition> const& function: getDefinedFunctions())
{
if (function->getName().empty()) if (function->getName().empty())
{ {
if (fallbackFunction) if (fallbackFunction)
@ -71,6 +74,9 @@ void ContractDefinition::checkTypeRequirements()
BOOST_THROW_EXCEPTION(fallbackFunction->getParameterList().createTypeError("Fallback function cannot take parameters.")); BOOST_THROW_EXCEPTION(fallbackFunction->getParameterList().createTypeError("Fallback function cannot take parameters."));
} }
} }
if (!function->isFullyImplemented())
setFullyImplemented(false);
}
for (ASTPointer<ModifierDefinition> const& modifier: getFunctionModifiers()) for (ASTPointer<ModifierDefinition> const& modifier: getFunctionModifiers())
modifier->checkTypeRequirements(); modifier->checkTypeRequirements();
@ -124,6 +130,28 @@ FunctionDefinition const* ContractDefinition::getFallbackFunction() const
return nullptr; return nullptr;
} }
void ContractDefinition::checkAbstractFunctions()
{
map<string, bool> functions;
// Search from base to derived
for (ContractDefinition const* contract: boost::adaptors::reverse(getLinearizedBaseContracts()))
for (ASTPointer<FunctionDefinition> const& function: contract->getDefinedFunctions())
{
string const& name = function->getName();
if (!function->isFullyImplemented() && functions.count(name) && functions[name])
BOOST_THROW_EXCEPTION(function->createTypeError("Redeclaring an already implemented function as abstract"));
functions[name] = function->isFullyImplemented();
}
for (auto const& it: functions)
if (!it.second)
{
setFullyImplemented(false);
break;
}
}
void ContractDefinition::checkIllegalOverrides() const void ContractDefinition::checkIllegalOverrides() const
{ {
// TODO unify this at a later point. for this we need to put the constness and the access specifier // TODO unify this at a later point. for this we need to put the constness and the access specifier
@ -316,8 +344,8 @@ void FunctionDefinition::checkTypeRequirements()
modifier->checkTypeRequirements(isConstructor() ? modifier->checkTypeRequirements(isConstructor() ?
dynamic_cast<ContractDefinition const&>(*getScope()).getBaseContracts() : dynamic_cast<ContractDefinition const&>(*getScope()).getBaseContracts() :
vector<ASTPointer<InheritanceSpecifier>>()); vector<ASTPointer<InheritanceSpecifier>>());
if (m_body)
m_body->checkTypeRequirements(); m_body->checkTypeRequirements();
} }
string FunctionDefinition::externalSignature() const string FunctionDefinition::externalSignature() const
@ -649,6 +677,8 @@ void NewExpression::checkTypeRequirements()
m_contract = dynamic_cast<ContractDefinition const*>(m_contractName->getReferencedDeclaration()); m_contract = dynamic_cast<ContractDefinition const*>(m_contractName->getReferencedDeclaration());
if (!m_contract) if (!m_contract)
BOOST_THROW_EXCEPTION(createTypeError("Identifier is not a contract.")); BOOST_THROW_EXCEPTION(createTypeError("Identifier is not a contract."));
if (!m_contract->isFullyImplemented())
BOOST_THROW_EXCEPTION(m_contract->createTypeError("Trying to create an instance of an abstract contract."));
shared_ptr<ContractType const> contractType = make_shared<ContractType>(*m_contract); shared_ptr<ContractType const> contractType = make_shared<ContractType>(*m_contract);
TypePointers const& parameterTypes = contractType->getConstructorType()->getParameterTypes(); TypePointers const& parameterTypes = contractType->getConstructorType()->getParameterTypes();
m_type = make_shared<FunctionType>(parameterTypes, TypePointers{contractType}, m_type = make_shared<FunctionType>(parameterTypes, TypePointers{contractType},

82
libsolidity/AST.h

@ -196,6 +196,22 @@ protected:
ASTPointer<ASTString> m_documentation; ASTPointer<ASTString> m_documentation;
}; };
/**
* Abstract class that is added to AST nodes that can be marked as not being fully implemented
*/
class ImplementationOptional
{
public:
explicit ImplementationOptional(bool _implemented): m_implemented(_implemented) {}
/// @return whether this node is fully implemented or not
bool isFullyImplemented() const { return m_implemented; }
void setFullyImplemented(bool _implemented) { m_implemented = _implemented; }
protected:
bool m_implemented;
};
/// @} /// @}
/** /**
@ -203,20 +219,24 @@ protected:
* document order. It first visits all struct declarations, then all variable declarations and * document order. It first visits all struct declarations, then all variable declarations and
* finally all function declarations. * finally all function declarations.
*/ */
class ContractDefinition: public Declaration, public Documented class ContractDefinition: public Declaration, public Documented, public ImplementationOptional
{ {
public: public:
ContractDefinition(SourceLocation const& _location, ContractDefinition(
ASTPointer<ASTString> const& _name, SourceLocation const& _location,
ASTPointer<ASTString> const& _documentation, ASTPointer<ASTString> const& _name,
std::vector<ASTPointer<InheritanceSpecifier>> const& _baseContracts, ASTPointer<ASTString> const& _documentation,
std::vector<ASTPointer<StructDefinition>> const& _definedStructs, std::vector<ASTPointer<InheritanceSpecifier>> const& _baseContracts,
std::vector<ASTPointer<EnumDefinition>> const& _definedEnums, std::vector<ASTPointer<StructDefinition>> const& _definedStructs,
std::vector<ASTPointer<VariableDeclaration>> const& _stateVariables, std::vector<ASTPointer<EnumDefinition>> const& _definedEnums,
std::vector<ASTPointer<FunctionDefinition>> const& _definedFunctions, std::vector<ASTPointer<VariableDeclaration>> const& _stateVariables,
std::vector<ASTPointer<ModifierDefinition>> const& _functionModifiers, std::vector<ASTPointer<FunctionDefinition>> const& _definedFunctions,
std::vector<ASTPointer<EventDefinition>> const& _events): std::vector<ASTPointer<ModifierDefinition>> const& _functionModifiers,
Declaration(_location, _name), Documented(_documentation), std::vector<ASTPointer<EventDefinition>> const& _events
):
Declaration(_location, _name),
Documented(_documentation),
ImplementationOptional(true),
m_baseContracts(_baseContracts), m_baseContracts(_baseContracts),
m_definedStructs(_definedStructs), m_definedStructs(_definedStructs),
m_definedEnums(_definedEnums), m_definedEnums(_definedEnums),
@ -263,6 +283,7 @@ public:
private: private:
void checkIllegalOverrides() const; void checkIllegalOverrides() const;
void checkAbstractFunctions();
std::vector<std::pair<FixedHash<4>, FunctionTypePointer>> const& getInterfaceFunctionList() const; std::vector<std::pair<FixedHash<4>, FunctionTypePointer>> const& getInterfaceFunctionList() const;
@ -378,24 +399,29 @@ private:
std::vector<ASTPointer<VariableDeclaration>> m_parameters; std::vector<ASTPointer<VariableDeclaration>> m_parameters;
}; };
class FunctionDefinition: public Declaration, public VariableScope, public Documented class FunctionDefinition: public Declaration, public VariableScope, public Documented, public ImplementationOptional
{ {
public: public:
FunctionDefinition(SourceLocation const& _location, ASTPointer<ASTString> const& _name, FunctionDefinition(
Declaration::Visibility _visibility, bool _isConstructor, SourceLocation const& _location,
ASTPointer<ASTString> const& _documentation, ASTPointer<ASTString> const& _name,
ASTPointer<ParameterList> const& _parameters, Declaration::Visibility _visibility, bool _isConstructor,
bool _isDeclaredConst, ASTPointer<ASTString> const& _documentation,
std::vector<ASTPointer<ModifierInvocation>> const& _modifiers, ASTPointer<ParameterList> const& _parameters,
ASTPointer<ParameterList> const& _returnParameters, bool _isDeclaredConst,
ASTPointer<Block> const& _body): std::vector<ASTPointer<ModifierInvocation>> const& _modifiers,
Declaration(_location, _name, _visibility), Documented(_documentation), ASTPointer<ParameterList> const& _returnParameters,
m_isConstructor(_isConstructor), ASTPointer<Block> const& _body
m_parameters(_parameters), ):
m_isDeclaredConst(_isDeclaredConst), Declaration(_location, _name, _visibility),
m_functionModifiers(_modifiers), Documented(_documentation),
m_returnParameters(_returnParameters), ImplementationOptional(_body != nullptr),
m_body(_body) m_isConstructor(_isConstructor),
m_parameters(_parameters),
m_isDeclaredConst(_isDeclaredConst),
m_functionModifiers(_modifiers),
m_returnParameters(_returnParameters),
m_body(_body)
{} {}
virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTVisitor& _visitor) override;

6
libsolidity/AST_accept.h

@ -175,7 +175,8 @@ void FunctionDefinition::accept(ASTVisitor& _visitor)
if (m_returnParameters) if (m_returnParameters)
m_returnParameters->accept(_visitor); m_returnParameters->accept(_visitor);
listAccept(m_functionModifiers, _visitor); listAccept(m_functionModifiers, _visitor);
m_body->accept(_visitor); if (m_body)
m_body->accept(_visitor);
} }
_visitor.endVisit(*this); _visitor.endVisit(*this);
} }
@ -188,7 +189,8 @@ void FunctionDefinition::accept(ASTConstVisitor& _visitor) const
if (m_returnParameters) if (m_returnParameters)
m_returnParameters->accept(_visitor); m_returnParameters->accept(_visitor);
listAccept(m_functionModifiers, _visitor); listAccept(m_functionModifiers, _visitor);
m_body->accept(_visitor); if (m_body)
m_body->accept(_visitor);
} }
_visitor.endVisit(*this); _visitor.endVisit(*this);
} }

104
libsolidity/ArrayUtils.cpp

@ -440,6 +440,110 @@ void ArrayUtils::retrieveLength(ArrayType const& _arrayType) const
} }
} }
void ArrayUtils::accessIndex(ArrayType const& _arrayType) const
{
ArrayType::Location location = _arrayType.getLocation();
eth::Instruction load =
location == ArrayType::Location::Storage ? eth::Instruction::SLOAD :
location == ArrayType::Location::Memory ? eth::Instruction::MLOAD :
eth::Instruction::CALLDATALOAD;
// retrieve length
if (!_arrayType.isDynamicallySized())
m_context << _arrayType.getLength();
else if (location == ArrayType::Location::CallData)
// length is stored on the stack
m_context << eth::Instruction::SWAP1;
else
m_context << eth::Instruction::DUP2 << load;
// stack: <base_ref> <index> <length>
// check out-of-bounds access
m_context << eth::Instruction::DUP2 << eth::Instruction::LT;
eth::AssemblyItem legalAccess = m_context.appendConditionalJump();
// out-of-bounds access throws exception (just STOP for now)
m_context << eth::Instruction::STOP;
m_context << legalAccess;
// stack: <base_ref> <index>
if (_arrayType.isByteArray())
switch (location)
{
case ArrayType::Location::Storage:
// byte array index storage lvalue on stack (goal):
// <ref> <byte_number> = <base_ref + index / 32> <index % 32>
m_context << u256(32) << eth::Instruction::SWAP2;
CompilerUtils(m_context).computeHashStatic();
// stack: 32 index data_ref
m_context
<< eth::Instruction::DUP3 << eth::Instruction::DUP3
<< eth::Instruction::DIV << eth::Instruction::ADD
// stack: 32 index (data_ref + index / 32)
<< eth::Instruction::SWAP2 << eth::Instruction::SWAP1
<< eth::Instruction::MOD;
break;
case ArrayType::Location::CallData:
// no lvalue, just retrieve the value
m_context
<< eth::Instruction::ADD << eth::Instruction::CALLDATALOAD
<< ((u256(0xff) << (256 - 8))) << eth::Instruction::AND;
break;
case ArrayType::Location::Memory:
solAssert(false, "Memory lvalues not yet implemented.");
}
else
{
// stack: <base_ref> <index>
m_context << eth::Instruction::SWAP1;
if (_arrayType.isDynamicallySized())
{
if (location == ArrayType::Location::Storage)
CompilerUtils(m_context).computeHashStatic();
else if (location == ArrayType::Location::Memory)
m_context << u256(32) << eth::Instruction::ADD;
}
// stack: <index> <data_ref>
switch (location)
{
case ArrayType::Location::CallData:
m_context
<< eth::Instruction::SWAP1 << _arrayType.getBaseType()->getCalldataEncodedSize()
<< eth::Instruction::MUL << eth::Instruction::ADD;
if (_arrayType.getBaseType()->isValueType())
CompilerUtils(m_context).loadFromMemoryDynamic(*_arrayType.getBaseType(), true, true, false);
break;
case ArrayType::Location::Storage:
m_context << eth::Instruction::SWAP1;
if (_arrayType.getBaseType()->getStorageBytes() <= 16)
{
// stack: <data_ref> <index>
// goal:
// <ref> <byte_number> = <base_ref + index / itemsPerSlot> <(index % itemsPerSlot) * byteSize>
unsigned byteSize = _arrayType.getBaseType()->getStorageBytes();
solAssert(byteSize != 0, "");
unsigned itemsPerSlot = 32 / byteSize;
m_context << u256(itemsPerSlot) << eth::Instruction::SWAP2;
// stack: itemsPerSlot index data_ref
m_context
<< eth::Instruction::DUP3 << eth::Instruction::DUP3
<< eth::Instruction::DIV << eth::Instruction::ADD
// stack: itemsPerSlot index (data_ref + index / itemsPerSlot)
<< eth::Instruction::SWAP2 << eth::Instruction::SWAP1
<< eth::Instruction::MOD
<< u256(byteSize) << eth::Instruction::MUL;
}
else
{
if (_arrayType.getBaseType()->getStorageSize() != 1)
m_context << _arrayType.getBaseType()->getStorageSize() << eth::Instruction::MUL;
m_context << eth::Instruction::ADD << u256(0);
}
break;
case ArrayType::Location::Memory:
solAssert(false, "Memory lvalues not yet implemented.");
}
}
}
void ArrayUtils::incrementByteOffset(unsigned _byteSize, unsigned _byteOffsetPosition, unsigned _storageOffsetPosition) const void ArrayUtils::incrementByteOffset(unsigned _byteSize, unsigned _byteOffsetPosition, unsigned _storageOffsetPosition) const
{ {
solAssert(_byteSize < 32, ""); solAssert(_byteSize < 32, "");

6
libsolidity/ArrayUtils.h

@ -70,6 +70,12 @@ public:
/// Stack pre: reference (excludes byte offset for dynamic storage arrays) /// Stack pre: reference (excludes byte offset for dynamic storage arrays)
/// Stack post: reference length /// Stack post: reference length
void retrieveLength(ArrayType const& _arrayType) const; void retrieveLength(ArrayType const& _arrayType) const;
/// Retrieves the value at a specific index. If the location is storage, only retrieves the
/// position.
/// Stack pre: reference [length] index
/// Stack post for storage: slot byte_offset
/// Stack post for calldata: value
void accessIndex(ArrayType const& _arrayType) const;
private: private:
/// Adds the given number of bytes to a storage byte offset counter and also increments /// Adds the given number of bytes to a storage byte offset counter and also increments

2
libsolidity/CompilerStack.cpp

@ -138,6 +138,8 @@ void CompilerStack::compile(bool _optimize)
for (ASTPointer<ASTNode> const& node: source->ast->getNodes()) for (ASTPointer<ASTNode> const& node: source->ast->getNodes())
if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get())) if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
{ {
if (!contract->isFullyImplemented())
continue;
shared_ptr<Compiler> compiler = make_shared<Compiler>(_optimize); shared_ptr<Compiler> compiler = make_shared<Compiler>(_optimize);
compiler->compileContract(*contract, contractBytecode); compiler->compileContract(*contract, contractBytecode);
Contract& compiledContract = m_contracts[contract->getName()]; Contract& compiledContract = m_contracts[contract->getName()];

107
libsolidity/ExpressionCompiler.cpp

@ -755,113 +755,20 @@ bool ExpressionCompiler::visit(IndexAccess const& _indexAccess)
{ {
ArrayType const& arrayType = dynamic_cast<ArrayType const&>(baseType); ArrayType const& arrayType = dynamic_cast<ArrayType const&>(baseType);
solAssert(_indexAccess.getIndexExpression(), "Index expression expected."); solAssert(_indexAccess.getIndexExpression(), "Index expression expected.");
ArrayType::Location location = arrayType.getLocation();
eth::Instruction load =
location == ArrayType::Location::Storage ? eth::Instruction::SLOAD :
location == ArrayType::Location::Memory ? eth::Instruction::MLOAD :
eth::Instruction::CALLDATALOAD;
// remove storage byte offset // remove storage byte offset
if (location == ArrayType::Location::Storage) if (arrayType.getLocation() == ArrayType::Location::Storage)
m_context << eth::Instruction::POP; m_context << eth::Instruction::POP;
// stack layout: <base_ref> [<length>] <index>
_indexAccess.getIndexExpression()->accept(*this); _indexAccess.getIndexExpression()->accept(*this);
// retrieve length // stack layout: <base_ref> [<length>] <index>
if (!arrayType.isDynamicallySized()) ArrayUtils(m_context).accessIndex(arrayType);
m_context << arrayType.getLength(); if (arrayType.getLocation() == ArrayType::Location::Storage)
else if (location == ArrayType::Location::CallData)
// length is stored on the stack
m_context << eth::Instruction::SWAP1;
else
m_context << eth::Instruction::DUP2 << load;
// stack: <base_ref> <index> <length>
// check out-of-bounds access
m_context << eth::Instruction::DUP2 << eth::Instruction::LT;
eth::AssemblyItem legalAccess = m_context.appendConditionalJump();
// out-of-bounds access throws exception (just STOP for now)
m_context << eth::Instruction::STOP;
m_context << legalAccess;
// stack: <base_ref> <index>
if (arrayType.isByteArray())
switch (location)
{
case ArrayType::Location::Storage:
// byte array index storage lvalue on stack (goal):
// <ref> <byte_number> = <base_ref + index / 32> <index % 32>
m_context << u256(32) << eth::Instruction::SWAP2;
CompilerUtils(m_context).computeHashStatic();
// stack: 32 index data_ref
m_context
<< eth::Instruction::DUP3 << eth::Instruction::DUP3
<< eth::Instruction::DIV << eth::Instruction::ADD
// stack: 32 index (data_ref + index / 32)
<< eth::Instruction::SWAP2 << eth::Instruction::SWAP1
<< eth::Instruction::MOD;
setLValue<StorageByteArrayElement>(_indexAccess);
break;
case ArrayType::Location::CallData:
// no lvalue, just retrieve the value
m_context
<< eth::Instruction::ADD << eth::Instruction::CALLDATALOAD
<< ((u256(0xff) << (256 - 8))) << eth::Instruction::AND;
break;
case ArrayType::Location::Memory:
solAssert(false, "Memory lvalues not yet implemented.");
}
else
{ {
// stack: <base_ref> <index> if (arrayType.isByteArray())
m_context << eth::Instruction::SWAP1; setLValue<StorageByteArrayElement>(_indexAccess);
if (arrayType.isDynamicallySized()) else
{
if (location == ArrayType::Location::Storage)
CompilerUtils(m_context).computeHashStatic();
else if (location == ArrayType::Location::Memory)
m_context << u256(32) << eth::Instruction::ADD;
}
// stack: <index> <data_ref>
switch (location)
{
case ArrayType::Location::CallData:
m_context
<< eth::Instruction::SWAP1 << arrayType.getBaseType()->getCalldataEncodedSize()
<< eth::Instruction::MUL << eth::Instruction::ADD;
if (arrayType.getBaseType()->isValueType())
CompilerUtils(m_context).loadFromMemoryDynamic(*arrayType.getBaseType(), true, true, false);
break;
case ArrayType::Location::Storage:
m_context << eth::Instruction::SWAP1;
if (arrayType.getBaseType()->getStorageBytes() <= 16)
{
// stack: <data_ref> <index>
// goal:
// <ref> <byte_number> = <base_ref + index / itemsPerSlot> <(index % itemsPerSlot) * byteSize>
unsigned byteSize = arrayType.getBaseType()->getStorageBytes();
solAssert(byteSize != 0, "");
unsigned itemsPerSlot = 32 / byteSize;
m_context << u256(itemsPerSlot) << eth::Instruction::SWAP2;
// stack: itemsPerSlot index data_ref
m_context
<< eth::Instruction::DUP3 << eth::Instruction::DUP3
<< eth::Instruction::DIV << eth::Instruction::ADD
// stack: itemsPerSlot index (data_ref + index / itemsPerSlot)
<< eth::Instruction::SWAP2 << eth::Instruction::SWAP1
<< eth::Instruction::MOD
<< u256(byteSize) << eth::Instruction::MUL;
}
else
{
if (arrayType.getBaseType()->getStorageSize() != 1)
m_context << arrayType.getBaseType()->getStorageSize() << eth::Instruction::MUL;
m_context << eth::Instruction::ADD << u256(0);
}
setLValueToStorageItem(_indexAccess); setLValueToStorageItem(_indexAccess);
break;
case ArrayType::Location::Memory:
solAssert(false, "Memory lvalues not yet implemented.");
}
} }
} }
else else

9
libsolidity/InterfaceHandler.cpp

@ -175,8 +175,17 @@ std::unique_ptr<std::string> InterfaceHandler::getDevDocumentation(ContractDefin
method["author"] = m_author; method["author"] = m_author;
Json::Value params(Json::objectValue); Json::Value params(Json::objectValue);
std::vector<std::string> paramNames = it.second->getParameterNames();
for (auto const& pair: m_params) for (auto const& pair: m_params)
{
if (find(paramNames.begin(), paramNames.end(), pair.first) == paramNames.end())
// LTODO: mismatching parameter name, throw some form of warning and not just an exception
BOOST_THROW_EXCEPTION(
DocstringParsingError() <<
errinfo_comment("documented parameter \"" + pair.first + "\" not found found in the function")
);
params[pair.first] = pair.second; params[pair.first] = pair.second;
}
if (!m_params.empty()) if (!m_params.empty())
method["params"] = params; method["params"] = params;

24
libsolidity/Parser.cpp

@ -164,8 +164,17 @@ ASTPointer<ContractDefinition> Parser::parseContractDefinition()
} }
nodeFactory.markEndPosition(); nodeFactory.markEndPosition();
expectToken(Token::RBrace); expectToken(Token::RBrace);
return nodeFactory.createNode<ContractDefinition>(name, docString, baseContracts, structs, enums, return nodeFactory.createNode<ContractDefinition>(
stateVariables, functions, modifiers, events); name,
docString,
baseContracts,
structs,
enums,
stateVariables,
functions,
modifiers,
events
);
} }
ASTPointer<InheritanceSpecifier> Parser::parseInheritanceSpecifier() ASTPointer<InheritanceSpecifier> Parser::parseInheritanceSpecifier()
@ -247,8 +256,15 @@ ASTPointer<FunctionDefinition> Parser::parseFunctionDefinition(ASTString const*
} }
else else
returnParameters = createEmptyParameterList(); returnParameters = createEmptyParameterList();
ASTPointer<Block> block = parseBlock(); ASTPointer<Block> block = ASTPointer<Block>();
nodeFactory.setEndPositionFromNode(block); nodeFactory.markEndPosition();
if (m_scanner->getCurrentToken() != Token::Semicolon)
{
block = parseBlock();
nodeFactory.setEndPositionFromNode(block);
}
else
m_scanner->next(); // just consume the ';'
bool const c_isConstructor = (_contractName && *name == *_contractName); bool const c_isConstructor = (_contractName && *name == *_contractName);
return nodeFactory.createNode<FunctionDefinition>(name, visibility, c_isConstructor, docstring, return nodeFactory.createNode<FunctionDefinition>(name, visibility, c_isConstructor, docstring,
parameters, isDeclaredConst, modifiers, parameters, isDeclaredConst, modifiers,

53
libtestutils/BlockChainLoader.cpp

@ -28,57 +28,6 @@ using namespace dev;
using namespace dev::test; using namespace dev::test;
using namespace dev::eth; using namespace dev::eth;
namespace dev
{
namespace test
{
dev::eth::BlockInfo toBlockInfo(Json::Value const& _json);
bytes toGenesisBlock(Json::Value const& _json);
}
}
dev::eth::BlockInfo dev::test::toBlockInfo(Json::Value const& _json)
{
RLPStream rlpStream;
auto size = _json.getMemberNames().size();
rlpStream.appendList(_json["hash"].empty() ? size : (size - 1));
rlpStream << fromHex(_json["parentHash"].asString());
rlpStream << fromHex(_json["uncleHash"].asString());
rlpStream << fromHex(_json["coinbase"].asString());
rlpStream << fromHex(_json["stateRoot"].asString());
rlpStream << fromHex(_json["transactionsTrie"].asString());
rlpStream << fromHex(_json["receiptTrie"].asString());
rlpStream << fromHex(_json["bloom"].asString());
rlpStream << bigint(_json["difficulty"].asString());
rlpStream << bigint(_json["number"].asString());
rlpStream << bigint(_json["gasLimit"].asString());
rlpStream << bigint(_json["gasUsed"].asString());
rlpStream << bigint(_json["timestamp"].asString());
rlpStream << fromHex(_json["extraData"].asString());
rlpStream << fromHex(_json["mixHash"].asString());
rlpStream << fromHex(_json["nonce"].asString());
BlockInfo result;
RLP rlp(rlpStream.out());
result.populateFromHeader(rlp, IgnoreNonce);
return result;
}
bytes dev::test::toGenesisBlock(Json::Value const& _json)
{
BlockInfo bi = toBlockInfo(_json);
RLPStream rlpStream;
bi.streamRLP(rlpStream, WithNonce);
RLPStream fullStream(3);
fullStream.appendRaw(rlpStream.out());
fullStream.appendRaw(RLPEmptyList);
fullStream.appendRaw(RLPEmptyList);
bi.verifyInternals(&fullStream.out());
return fullStream.out();
}
BlockChainLoader::BlockChainLoader(Json::Value const& _json) BlockChainLoader::BlockChainLoader(Json::Value const& _json)
{ {
// load pre state // load pre state
@ -86,7 +35,7 @@ BlockChainLoader::BlockChainLoader(Json::Value const& _json)
m_state = sl.state(); m_state = sl.state();
// load genesisBlock // load genesisBlock
m_bc.reset(new BlockChain(toGenesisBlock(_json["genesisBlockHeader"]), m_dir.path(), true)); m_bc.reset(new BlockChain(fromHex(_json["genesisRLP"].asString()), m_dir.path(), true));
// load blocks // load blocks
for (auto const& block: _json["blocks"]) for (auto const& block: _json["blocks"])

2
libweb3jsonrpc/WebThreeStubServerBase.cpp

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

4
libwhisper/Message.cpp

@ -58,10 +58,10 @@ Message::Message(Envelope const& _e, FullTopic const& _fk, Secret const& _s)
// get key from decrypted topic key: just xor // get key from decrypted topic key: just xor
h256 tk = h256(bytesConstRef(&(_e.data())).cropped(32 * topicIndex, 32)); h256 tk = h256(bytesConstRef(&(_e.data())).cropped(32 * topicIndex, 32));
bytesConstRef cipherText = bytesConstRef(&(_e.data())).cropped(32 * _e.topic().size()); bytesConstRef cipherText = bytesConstRef(&(_e.data())).cropped(32 * _e.topic().size());
cnote << "Decrypting(" << topicIndex << "): " << topicSecret << tk << (topicSecret ^ tk) << toHex(cipherText); // cdebug << "Decrypting(" << topicIndex << "): " << topicSecret << tk << (topicSecret ^ tk) << toHex(cipherText);
if (!decryptSym(topicSecret ^ tk, cipherText, b)) if (!decryptSym(topicSecret ^ tk, cipherText, b))
return; return;
cnote << "Got: " << toHex(b); // cdebug << "Got: " << toHex(b);
} }
if (populate(b)) if (populate(b))

10
mix/qml/CodeEditorView.qml

@ -11,6 +11,16 @@ Item {
signal breakpointsChanged(string documentId) signal breakpointsChanged(string documentId)
signal isCleanChanged(var isClean, string documentId) signal isCleanChanged(var isClean, string documentId)
function getDocumentIdByName(fileName)
{
for (var i = 0; i < editorListModel.count; i++) {
if (editorListModel.get(i).fileName === fileName) {
return editorListModel.get(i).documentId;
}
}
return null;
}
function getDocumentText(documentId) { function getDocumentText(documentId) {
for (var i = 0; i < editorListModel.count; i++) { for (var i = 0; i < editorListModel.count; i++) {
if (editorListModel.get(i).documentId === documentId) { if (editorListModel.get(i).documentId === documentId) {

7
mix/qml/WebPreview.qml

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

39
mix/qml/html/codeeditor.js

@ -22,17 +22,11 @@ editor.on("change", function(eMirror, object) {
}); });
var mac = /Mac/.test(navigator.platform); var mac = /Mac/.test(navigator.platform);
var extraKeys = {};
if (mac === true) { if (mac === true) {
editor.setOption("extraKeys", { extraKeys["Cmd-V"] = function(cm) { cm.replaceSelection(clipboard); };
"Cmd-V": function(cm) { extraKeys["Cmd-X"] = function(cm) { window.document.execCommand("cut"); };
cm.replaceSelection(clipboard); extraKeys["Cmd-C"] = function(cm) { window.document.execCommand("copy"); };
},
"Cmd-X": function(cm) {
window.document.execCommand("cut");
},
"Cmd-C": function(cm) {
window.document.execCommand("copy");
}});
} }
makeMarker = function() { makeMarker = function() {
@ -102,16 +96,14 @@ setMode = function(mode) {
if (mode === "javascript") if (mode === "javascript")
{ {
ternServer = new CodeMirror.TernServer({defs: [ ecma5Spec() ]}); ternServer = new CodeMirror.TernServer({defs: [ ecma5Spec() ]});
editor.setOption("extraKeys", { extraKeys["Ctrl-Space"] = function(cm) { ternServer.complete(cm); };
"Ctrl-Space": function(cm) { ternServer.complete(cm); }, extraKeys["Ctrl-I"] = function(cm) { ternServer.showType(cm); };
"Ctrl-I": function(cm) { ternServer.showType(cm); }, extraKeys["Ctrl-O"] = function(cm) { ternServer.showDocs(cm); };
"Ctrl-O": function(cm) { ternServer.showDocs(cm); }, extraKeys["Alt-."] = function(cm) { ternServer.jumpToDef(cm); };
"Alt-.": function(cm) { ternServer.jumpToDef(cm); }, extraKeys["Alt-,"] = function(cm) { ternServer.jumpBack(cm); };
"Alt-,": function(cm) { ternServer.jumpBack(cm); }, extraKeys["Ctrl-Q"] = function(cm) { ternServer.rename(cm); };
"Ctrl-Q": function(cm) { ternServer.rename(cm); }, extraKeys["Ctrl-."] = function(cm) { ternServer.selectName(cm); };
"Ctrl-.": function(cm) { ternServer.selectName(cm); }, extraKeys["'.'"] = function(cm) { setTimeout(function() { ternServer.complete(cm); }, 100); throw CodeMirror.Pass; };
"'.'": function(cm) { setTimeout(function() { ternServer.complete(cm); }, 100); throw CodeMirror.Pass; }
})
editor.on("cursorActivity", function(cm) { ternServer.updateArgHints(cm); }); editor.on("cursorActivity", function(cm) { ternServer.updateArgHints(cm); });
} }
else if (mode === "solidity") else if (mode === "solidity")
@ -119,10 +111,9 @@ setMode = function(mode) {
CodeMirror.commands.autocomplete = function(cm) { CodeMirror.commands.autocomplete = function(cm) {
CodeMirror.showHint(cm, CodeMirror.hint.anyword); CodeMirror.showHint(cm, CodeMirror.hint.anyword);
} }
editor.setOption("extraKeys", { extraKeys["Ctrl-Space"] = "autocomplete";
"Ctrl-Space": "autocomplete"
})
} }
editor.setOption("extraKeys", extraKeys);
}; };
setClipboardBase64 = function(text) { setClipboardBase64 = function(text) {
@ -199,3 +190,5 @@ compilationComplete = function()
} }
compilationCompleteBool = true; compilationCompleteBool = true;
} }
editor.setOption("extraKeys", extraKeys);

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 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 // balanceAt
u256 expectedBalance = u256(o["balance"].asString()); u256 expectedBalance = u256(_o["balance"].asString());
u256 balance = _client.balanceAt(address); u256 balance = _client.balanceAt(address, _blockNumber);
ETH_CHECK_EQUAL(expectedBalance, balance); ETH_CHECK_EQUAL(expectedBalance, balance);
// countAt // countAt
u256 expectedCount = u256(o["nonce"].asString()); u256 expectedCount = u256(_o["nonce"].asString());
u256 count = _client.countAt(address); u256 count = _client.countAt(address, _blockNumber);
ETH_CHECK_EQUAL(expectedCount, count); ETH_CHECK_EQUAL(expectedCount, count);
// stateAt // stateAt
for (string const& pos: o["storage"].getMemberNames()) for (string const& pos: _o["storage"].getMemberNames())
{ {
u256 expectedState = u256(o["storage"][pos].asString()); u256 expectedState = u256(_o["storage"][pos].asString());
u256 state = _client.stateAt(address, u256(pos)); u256 state = _client.stateAt(address, u256(pos), _blockNumber);
ETH_CHECK_EQUAL(expectedState, state); ETH_CHECK_EQUAL(expectedState, state);
} }
// codeAt // codeAt
bytes expectedCode = fromHex(o["code"].asString()); bytes expectedCode = fromHex(_o["code"].asString());
bytes code = _client.codeAt(address); bytes code = _client.codeAt(address, _blockNumber);
ETH_CHECK_EQUAL_COLLECTIONS(expectedCode.begin(), expectedCode.end(), 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 // number

57
test/SolidityNameAndTypeResolution.cpp

@ -359,6 +359,63 @@ BOOST_AUTO_TEST_CASE(comparison_bitop_precedence)
ETH_TEST_CHECK_NO_THROW(parseTextAndResolveNames(text), "Parsing and Name Resolving Failed"); ETH_TEST_CHECK_NO_THROW(parseTextAndResolveNames(text), "Parsing and Name Resolving Failed");
} }
BOOST_AUTO_TEST_CASE(function_no_implementation)
{
ASTPointer<SourceUnit> sourceUnit;
char const* text = "contract test {\n"
" function functionName(bytes32 input) returns (bytes32 out);\n"
"}\n";
ETH_TEST_REQUIRE_NO_THROW(sourceUnit = parseTextAndResolveNames(text), "Parsing and name Resolving failed");
std::vector<ASTPointer<ASTNode>> nodes = sourceUnit->getNodes();
ContractDefinition* contract = dynamic_cast<ContractDefinition*>(nodes[0].get());
BOOST_CHECK(contract);
BOOST_CHECK(!contract->isFullyImplemented());
BOOST_CHECK(!contract->getDefinedFunctions()[0]->isFullyImplemented());
}
BOOST_AUTO_TEST_CASE(abstract_contract)
{
ASTPointer<SourceUnit> sourceUnit;
char const* text = R"(
contract base { function foo(); }
contract derived is base { function foo() {} }
)";
ETH_TEST_REQUIRE_NO_THROW(sourceUnit = parseTextAndResolveNames(text), "Parsing and name Resolving failed");
std::vector<ASTPointer<ASTNode>> nodes = sourceUnit->getNodes();
ContractDefinition* base = dynamic_cast<ContractDefinition*>(nodes[0].get());
ContractDefinition* derived = dynamic_cast<ContractDefinition*>(nodes[1].get());
BOOST_CHECK(base);
BOOST_CHECK(!base->isFullyImplemented());
BOOST_CHECK(!base->getDefinedFunctions()[0]->isFullyImplemented());
BOOST_CHECK(derived);
BOOST_CHECK(derived->isFullyImplemented());
BOOST_CHECK(derived->getDefinedFunctions()[0]->isFullyImplemented());
}
BOOST_AUTO_TEST_CASE(create_abstract_contract)
{
ASTPointer<SourceUnit> sourceUnit;
char const* text = R"(
contract base { function foo(); }
contract derived {
base b;
function foo() { b = new base();}
}
)";
BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError);
}
BOOST_AUTO_TEST_CASE(redeclare_implemented_abstract_function_as_abstract)
{
ASTPointer<SourceUnit> sourceUnit;
char const* text = R"(
contract base { function foo(); }
contract derived is base { function foo() {} }
contract wrong is derived { function foo(); }
)";
BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError);
}
BOOST_AUTO_TEST_CASE(function_canonical_signature) BOOST_AUTO_TEST_CASE(function_canonical_signature)
{ {
ASTPointer<SourceUnit> sourceUnit; ASTPointer<SourceUnit> sourceUnit;

25
test/SolidityNatspecJSON.cpp

@ -176,7 +176,6 @@ BOOST_AUTO_TEST_CASE(dev_and_user_no_doc)
"}\n"; "}\n";
char const* devNatspec = "{\"methods\":{}}"; char const* devNatspec = "{\"methods\":{}}";
char const* userNatspec = "{\"methods\":{}}"; char const* userNatspec = "{\"methods\":{}}";
checkNatspec(sourceCode, devNatspec, false); checkNatspec(sourceCode, devNatspec, false);
@ -230,6 +229,18 @@ BOOST_AUTO_TEST_CASE(dev_multiple_params)
checkNatspec(sourceCode, natspec, false); checkNatspec(sourceCode, natspec, false);
} }
BOOST_AUTO_TEST_CASE(dev_documenting_nonexistant_param)
{
char const* sourceCode = "contract test {\n"
" /// @dev Multiplies a number by 7 and adds second parameter\n"
" /// @param a Documentation for the first parameter\n"
" /// @param not_existing Documentation for the second parameter\n"
" function mul(uint a, uint second) returns(uint d) { return a * 7 + second; }\n"
"}\n";
BOOST_CHECK_THROW(checkNatspec(sourceCode, "", false), DocstringParsingError);
}
BOOST_AUTO_TEST_CASE(dev_mutiline_param_description) BOOST_AUTO_TEST_CASE(dev_mutiline_param_description)
{ {
char const* sourceCode = "contract test {\n" char const* sourceCode = "contract test {\n"
@ -487,17 +498,7 @@ BOOST_AUTO_TEST_CASE(dev_title_at_function_error)
" function mul(uint a, uint second) returns(uint d) { return a * 7 + second; }\n" " function mul(uint a, uint second) returns(uint d) { return a * 7 + second; }\n"
"}\n"; "}\n";
char const* natspec = "{" BOOST_CHECK_THROW(checkNatspec(sourceCode, "", false), DocstringParsingError);
" \"author\": \"Lefteris\","
" \"title\": \"Just a test contract\","
" \"methods\":{"
" \"mul(uint256,uint256)\":{ \n"
" \"details\": \"Mul function\"\n"
" }\n"
" }\n"
"}";
BOOST_CHECK_THROW(checkNatspec(sourceCode, natspec, false), DocstringParsingError);
} }
BOOST_AUTO_TEST_CASE(natspec_notice_without_tag) BOOST_AUTO_TEST_CASE(natspec_notice_without_tag)

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}); 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() BOOST_AUTO_TEST_SUITE_END()
} }

8
test/SolidityParser.cpp

@ -108,6 +108,14 @@ BOOST_AUTO_TEST_CASE(single_function_param)
ETH_TEST_CHECK_NO_THROW(parseText(text), "Parsing failed."); ETH_TEST_CHECK_NO_THROW(parseText(text), "Parsing failed.");
} }
BOOST_AUTO_TEST_CASE(function_no_body)
{
char const* text = "contract test {\n"
" function functionName(bytes32 input) returns (bytes32 out);\n"
"}\n";
ETH_TEST_CHECK_NO_THROW(parseText(text), "Parsing failed.");
}
BOOST_AUTO_TEST_CASE(missing_parameter_name_in_named_args) BOOST_AUTO_TEST_CASE(missing_parameter_name_in_named_args)
{ {
char const* text = "contract test {\n" char const* text = "contract test {\n"

4
test/dagger.cpp

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

9
test/peer.cpp

@ -57,6 +57,15 @@ BOOST_AUTO_TEST_CASE(host)
g_logVerbosity = oldLogVerbosity; 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) BOOST_AUTO_TEST_CASE(save_nodes)
{ {
std::list<Host*> hosts; std::list<Host*> hosts;

Loading…
Cancel
Save