diff --git a/CodingStandards.txt b/CodingStandards.txt
index e1a1b3ba9..a98a74c78 100644
--- a/CodingStandards.txt
+++ b/CodingStandards.txt
@@ -13,7 +13,7 @@ c. Don't use braces for condition-body one-liners.
d. Never place condition bodies on same line as condition.
e. Space between first paren and keyword, but *not* following first paren or preceeding final paren.
f. No spaces when fewer than intra-expression three parens together; when three or more, space according to clarity.
-g. No spaces for subscripting.
+g. No spaces for subscripting or unary operators.
h. No space before ':' but one after it, except in the ternary operator: one on both sides.
i. Space all other operators.
j. Braces, when used, always have their own lines and are at same indentation level as "parent" scope.
diff --git a/alethzero/MainWin.cpp b/alethzero/MainWin.cpp
index 0de7e2cb8..610fd032d 100644
--- a/alethzero/MainWin.cpp
+++ b/alethzero/MainWin.cpp
@@ -1774,6 +1774,7 @@ void Main::on_net_triggered()
else
{
ui->downloadView->setDownloadMan(nullptr);
+ writeSettings();
web3()->stopNetwork();
}
}
diff --git a/alethzero/Transact.cpp b/alethzero/Transact.cpp
index 6f52548c7..1ff3427e3 100644
--- a/alethzero/Transact.cpp
+++ b/alethzero/Transact.cpp
@@ -338,7 +338,7 @@ void Transact::rejigData()
}
if (er.codeDeposit == CodeDeposit::Failed)
{
- bail("
ERROR Code deposit failed due to insufficient gas
");
+ bail("ERROR Code deposit failed due to insufficient gas; " + QString::fromStdString(toString(er.gasForDeposit)) + " GAS < " + QString::fromStdString(toString(er.depositSize)) + " bytes * " + QString::fromStdString(toString(c_createDataGas)) + "GAS/byte
");
return;
}
diff --git a/libdevcore/CommonJS.h b/libdevcore/CommonJS.h
index cc487d54c..ade089e16 100644
--- a/libdevcore/CommonJS.h
+++ b/libdevcore/CommonJS.h
@@ -38,7 +38,10 @@ template std::string toJS(FixedHash const& _h)
template std::string toJS(boost::multiprecision::number> 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)
diff --git a/libdevcrypto/MemoryDB.h b/libdevcrypto/MemoryDB.h
index 7d39ba73b..7858126f8 100644
--- a/libdevcrypto/MemoryDB.h
+++ b/libdevcrypto/MemoryDB.h
@@ -32,8 +32,10 @@ namespace dev
{
struct DBChannel: public LogChannel { static const char* name() { return "TDB"; } static const int verbosity = 18; };
+struct DBWarn: public LogChannel { static const char* name() { return "TDB"; } static const int verbosity = 1; };
#define dbdebug clog(DBChannel)
+#define dbwarn clog(DBWarn)
class MemoryDB
{
diff --git a/libethash/CMakeLists.txt b/libethash/CMakeLists.txt
index 38fc821c0..c92240086 100644
--- a/libethash/CMakeLists.txt
+++ b/libethash/CMakeLists.txt
@@ -12,6 +12,7 @@ endif()
set(FILES util.c
util.h
+ io.c
internal.c
ethash.h
endian.h
@@ -19,6 +20,12 @@ set(FILES util.c
fnv.h
data_sizes.h)
+if (MSVC)
+ list(APPEND FILES io_win32.c)
+else()
+ list(APPEND FILES io_posix.c)
+endif()
+
if (NOT CRYPTOPP_FOUND)
find_package(CryptoPP 5.6.2)
endif()
diff --git a/libethash/data_sizes.h b/libethash/data_sizes.h
index 3b747b3ea..cf52ae4f8 100644
--- a/libethash/data_sizes.h
+++ b/libethash/data_sizes.h
@@ -48,7 +48,7 @@ extern "C" {
// Sow[i*HashBytes]; j++]]]][[2]][[1]]
-static const size_t dag_sizes[2048] = {
+static const uint64_t dag_sizes[2048] = {
1073739904U, 1082130304U, 1090514816U, 1098906752U, 1107293056U,
1115684224U, 1124070016U, 1132461952U, 1140849536U, 1149232768U,
1157627776U, 1166013824U, 1174404736U, 1182786944U, 1191180416U,
@@ -477,7 +477,7 @@ static const size_t dag_sizes[2048] = {
// While[! PrimeQ[i], i--];
// Sow[i*HashBytes]; j++]]]][[2]][[1]]
-const size_t cache_sizes[2048] = {
+const uint64_t cache_sizes[2048] = {
16776896U, 16907456U, 17039296U, 17170112U, 17301056U, 17432512U, 17563072U,
17693888U, 17824192U, 17955904U, 18087488U, 18218176U, 18349504U, 18481088U,
18611392U, 18742336U, 18874304U, 19004224U, 19135936U, 19267264U, 19398208U,
diff --git a/libethash/ethash.h b/libethash/ethash.h
index a7159de65..7594fc835 100644
--- a/libethash/ethash.h
+++ b/libethash/ethash.h
@@ -24,92 +24,115 @@
#include
#include
#include
+#include
#include "compiler.h"
-#define REVISION 23
-#define DATASET_BYTES_INIT 1073741824U // 2**30
-#define DATASET_BYTES_GROWTH 8388608U // 2**23
-#define CACHE_BYTES_INIT 1073741824U // 2**24
-#define CACHE_BYTES_GROWTH 131072U // 2**17
-#define EPOCH_LENGTH 30000U
-#define MIX_BYTES 128
-#define HASH_BYTES 64
-#define DATASET_PARENTS 256
-#define CACHE_ROUNDS 3
-#define ACCESSES 64
+#define ETHASH_REVISION 23
+#define ETHASH_DATASET_BYTES_INIT 1073741824U // 2**30
+#define ETHASH_DATASET_BYTES_GROWTH 8388608U // 2**23
+#define ETHASH_CACHE_BYTES_INIT 1073741824U // 2**24
+#define ETHASH_CACHE_BYTES_GROWTH 131072U // 2**17
+#define ETHASH_EPOCH_LENGTH 30000U
+#define ETHASH_MIX_BYTES 128
+#define ETHASH_HASH_BYTES 64
+#define ETHASH_DATASET_PARENTS 256
+#define ETHASH_CACHE_ROUNDS 3
+#define ETHASH_ACCESSES 64
#ifdef __cplusplus
extern "C" {
#endif
typedef struct ethash_params {
- size_t full_size; // Size of full data set (in bytes, multiple of mix size (128)).
- size_t cache_size; // Size of compute cache (in bytes, multiple of node size (64)).
+ uint64_t full_size; // Size of full data set (in bytes, multiple of mix size (128)).
+ uint64_t cache_size; // Size of compute cache (in bytes, multiple of node size (64)).
} ethash_params;
typedef struct ethash_return_value {
- uint8_t result[32];
- uint8_t mix_hash[32];
+ uint8_t result[32];
+ uint8_t mix_hash[32];
} ethash_return_value;
-size_t ethash_get_datasize(const uint32_t block_number);
-size_t ethash_get_cachesize(const uint32_t block_number);
+uint64_t ethash_get_datasize(const uint32_t block_number);
+uint64_t ethash_get_cachesize(const uint32_t block_number);
// initialize the parameters
static inline void ethash_params_init(ethash_params *params, const uint32_t block_number) {
- params->full_size = ethash_get_datasize(block_number);
- params->cache_size = ethash_get_cachesize(block_number);
+ params->full_size = ethash_get_datasize(block_number);
+ params->cache_size = ethash_get_cachesize(block_number);
}
-typedef struct ethash_cache {
- void *mem;
-} ethash_cache;
+/***********************************
+ * OLD API *************************
+ ***********************************
+ ******************** (deprecated) *
+ ***********************************/
-void ethash_mkcache(ethash_cache *cache, ethash_params const *params, const uint8_t seed[32]);
-void ethash_compute_full_data(void *mem, ethash_params const *params, ethash_cache const *cache);
-void ethash_full(ethash_return_value *ret, void const *full_mem, ethash_params const *params, const uint8_t header_hash[32], const uint64_t nonce);
-void ethash_light(ethash_return_value *ret, ethash_cache const *cache, ethash_params const *params, const uint8_t header_hash[32], const uint64_t nonce);
void ethash_get_seedhash(uint8_t seedhash[32], const uint32_t block_number);
+void ethash_mkcache(void *cache, ethash_params const *params, const uint8_t seed[32]);
+void ethash_light(ethash_return_value *ret, void const *cache, ethash_params const *params, const uint8_t header_hash[32], const uint64_t nonce);
+void ethash_compute_full_data(void *mem, ethash_params const *params, void const *cache);
+void ethash_full(ethash_return_value *ret, void const *full_mem, ethash_params const *params, const uint8_t header_hash[32], const uint64_t nonce);
-static inline void ethash_prep_light(void *cache, ethash_params const *params, const uint8_t seed[32]) {
- ethash_cache c;
- c.mem = cache;
- ethash_mkcache(&c, params, seed);
-}
+/***********************************
+ * NEW API *************************
+ ***********************************/
+
+// TODO: compute params and seed in ethash_new_light; it should take only block_number
+// TODO: store params in ethash_light_t/ethash_full_t to avoid having to repass into compute/new_full
+
+typedef uint8_t const ethash_seedhash_t[32];
-static inline void ethash_compute_light(ethash_return_value *ret, void const *cache, ethash_params const *params, const uint8_t header_hash[32], const uint64_t nonce) {
- ethash_cache c;
- c.mem = (void *) cache;
- ethash_light(ret, &c, params, header_hash, nonce);
+typedef void const* ethash_light_t;
+static inline ethash_light_t ethash_new_light(ethash_params const* params, ethash_seedhash_t seed) {
+ void* ret = malloc(params->cache_size);
+ ethash_mkcache(ret, params, seed);
+ return ret;
+}
+static inline void ethash_compute_light(ethash_return_value *ret, ethash_light_t light, ethash_params const *params, const uint8_t header_hash[32], const uint64_t nonce) {
+ ethash_light(ret, light, params, header_hash, nonce);
+}
+static inline void ethash_delete_light(ethash_light_t light) {
+ free((void*)light);
}
+typedef void const* ethash_full_t;
+static inline ethash_full_t ethash_new_full(ethash_params const* params, ethash_light_t light) {
+ void* ret = malloc(params->full_size);
+ ethash_compute_full_data(ret, params, light);
+ return ret;
+}
static inline void ethash_prep_full(void *full, ethash_params const *params, void const *cache) {
- ethash_cache c;
- c.mem = (void *) cache;
- ethash_compute_full_data(full, params, &c);
+ ethash_compute_full_data(full, params, cache);
}
-
static inline void ethash_compute_full(ethash_return_value *ret, void const *full, ethash_params const *params, const uint8_t header_hash[32], const uint64_t nonce) {
- ethash_full(ret, full, params, header_hash, nonce);
+ ethash_full(ret, full, params, header_hash, nonce);
}
-// Returns if hash is less than or equal to difficulty
-static inline int ethash_check_difficulty(
- const uint8_t hash[32],
- const uint8_t difficulty[32]) {
- // Difficulty is big endian
- for (int i = 0; i < 32; i++) {
- if (hash[i] == difficulty[i]) continue;
- return hash[i] < difficulty[i];
- }
- return 1;
+/// @brief Compare two s256-bit big-endian values.
+/// @returns 1 if @a a is less than or equal to @a b, 0 otherwise.
+/// Both parameters are 256-bit big-endian values.
+static inline int ethash_leq_be256(const uint8_t a[32], const uint8_t b[32]) {
+ // Boundary is big endian
+ for (int i = 0; i < 32; i++) {
+ if (a[i] == b[i])
+ continue;
+ return a[i] < b[i];
+ }
+ return 1;
}
-int ethash_quick_check_difficulty(
- const uint8_t header_hash[32],
- const uint64_t nonce,
- const uint8_t mix_hash[32],
- const uint8_t difficulty[32]);
+/// Perofrms a cursory check on the validity of the nonce.
+/// @returns 1 if the nonce may possibly be valid for the given header_hash & boundary.
+/// @p boundary equivalent to 2 ^ 256 / block_difficulty, represented as a 256-bit big-endian.
+int ethash_preliminary_check_boundary(
+ const uint8_t header_hash[32],
+ const uint64_t nonce,
+ const uint8_t mix_hash[32],
+ const uint8_t boundary[32]);
+
+#define ethash_quick_check_difficulty ethash_preliminary_check_boundary
+#define ethash_check_difficulty ethash_leq_be256
#ifdef __cplusplus
}
diff --git a/libethash/internal.c b/libethash/internal.c
index 0a7e767e7..130ca13c3 100644
--- a/libethash/internal.c
+++ b/libethash/internal.c
@@ -37,14 +37,14 @@
#include "sha3.h"
#endif // WITH_CRYPTOPP
-size_t ethash_get_datasize(const uint32_t block_number) {
- assert(block_number / EPOCH_LENGTH < 2048);
- return dag_sizes[block_number / EPOCH_LENGTH];
+uint64_t ethash_get_datasize(const uint32_t block_number) {
+ assert(block_number / ETHASH_EPOCH_LENGTH < 2048);
+ return dag_sizes[block_number / ETHASH_EPOCH_LENGTH];
}
-size_t ethash_get_cachesize(const uint32_t block_number) {
- assert(block_number / EPOCH_LENGTH < 2048);
- return cache_sizes[block_number / EPOCH_LENGTH];
+uint64_t ethash_get_cachesize(const uint32_t block_number) {
+ assert(block_number / ETHASH_EPOCH_LENGTH < 2048);
+ return cache_sizes[block_number / ETHASH_EPOCH_LENGTH];
}
// Follows Sergio's "STRICT MEMORY HARD HASHING FUNCTIONS" (2014)
@@ -63,7 +63,7 @@ void static ethash_compute_cache_nodes(
SHA3_512(nodes[i].bytes, nodes[i - 1].bytes, 64);
}
- for (unsigned j = 0; j != CACHE_ROUNDS; j++) {
+ for (unsigned j = 0; j != ETHASH_CACHE_ROUNDS; j++) {
for (unsigned i = 0; i != num_nodes; i++) {
uint32_t const idx = nodes[i].words[0] % num_nodes;
node data;
@@ -85,10 +85,10 @@ void static ethash_compute_cache_nodes(
}
void ethash_mkcache(
- ethash_cache *cache,
+ void *cache,
ethash_params const *params,
const uint8_t seed[32]) {
- node *nodes = (node *) cache->mem;
+ node *nodes = (node *) cache;
ethash_compute_cache_nodes(nodes, params, seed);
}
@@ -96,10 +96,10 @@ void ethash_calculate_dag_item(
node *const ret,
const unsigned node_index,
const struct ethash_params *params,
- const struct ethash_cache *cache) {
+ const void *cache) {
uint32_t num_parent_nodes = (uint32_t) (params->cache_size / sizeof(node));
- node const *cache_nodes = (node const *) cache->mem;
+ node const *cache_nodes = (node const *) cache;
node const *init = &cache_nodes[node_index % num_parent_nodes];
memcpy(ret, init, sizeof(node));
@@ -114,7 +114,7 @@ void ethash_calculate_dag_item(
__m128i xmm3 = ret->xmm[3];
#endif
- for (unsigned i = 0; i != DATASET_PARENTS; ++i) {
+ for (unsigned i = 0; i != ETHASH_DATASET_PARENTS; ++i) {
uint32_t parent_index = ((node_index ^ i) * FNV_PRIME ^ ret->words[i % NODE_WORDS]) % num_parent_nodes;
node const *parent = &cache_nodes[parent_index];
@@ -150,7 +150,7 @@ void ethash_calculate_dag_item(
void ethash_compute_full_data(
void *mem,
ethash_params const *params,
- ethash_cache const *cache) {
+ void const *cache) {
assert((params->full_size % (sizeof(uint32_t) * MIX_WORDS)) == 0);
assert((params->full_size % sizeof(node)) == 0);
node *full_nodes = mem;
@@ -164,7 +164,7 @@ void ethash_compute_full_data(
static void ethash_hash(
ethash_return_value *ret,
node const *full_nodes,
- ethash_cache const *cache,
+ void const *cache,
ethash_params const *params,
const uint8_t header_hash[32],
const uint64_t nonce) {
@@ -201,7 +201,7 @@ static void ethash_hash(
num_full_pages = (unsigned) (params->full_size / page_size);
- for (unsigned i = 0; i != ACCESSES; ++i) {
+ for (unsigned i = 0; i != ETHASH_ACCESSES; ++i) {
uint32_t const index = ((s_mix->words[0] ^ i) * FNV_PRIME ^ mix->words[i % MIX_WORDS]) % num_full_pages;
for (unsigned n = 0; n != MIX_NODES; ++n) {
@@ -275,26 +275,26 @@ void ethash_quick_hash(
void ethash_get_seedhash(uint8_t seedhash[32], const uint32_t block_number) {
memset(seedhash, 0, 32);
- const uint32_t epochs = block_number / EPOCH_LENGTH;
+ const uint32_t epochs = block_number / ETHASH_EPOCH_LENGTH;
for (uint32_t i = 0; i < epochs; ++i)
SHA3_256(seedhash, seedhash, 32);
}
-int ethash_quick_check_difficulty(
+int ethash_preliminary_check_boundary(
const uint8_t header_hash[32],
const uint64_t nonce,
const uint8_t mix_hash[32],
- const uint8_t difficulty[32]) {
+ const uint8_t difficulty[32]) {
- uint8_t return_hash[32];
+ uint8_t return_hash[32];
ethash_quick_hash(return_hash, header_hash, nonce, mix_hash);
- return ethash_check_difficulty(return_hash, difficulty);
+ return ethash_leq_be256(return_hash, difficulty);
}
void ethash_full(ethash_return_value *ret, void const *full_mem, ethash_params const *params, const uint8_t previous_hash[32], const uint64_t nonce) {
ethash_hash(ret, (node const *) full_mem, NULL, params, previous_hash, nonce);
}
-void ethash_light(ethash_return_value *ret, ethash_cache const *cache, ethash_params const *params, const uint8_t previous_hash[32], const uint64_t nonce) {
+void ethash_light(ethash_return_value *ret, void const *cache, ethash_params const *params, const uint8_t previous_hash[32], const uint64_t nonce) {
ethash_hash(ret, NULL, cache, params, previous_hash, nonce);
-}
\ No newline at end of file
+}
diff --git a/libethash/internal.h b/libethash/internal.h
index bcbacdaa4..dec7e6b13 100644
--- a/libethash/internal.h
+++ b/libethash/internal.h
@@ -3,7 +3,7 @@
#include "endian.h"
#include "ethash.h"
-#define ENABLE_SSE 1
+#define ENABLE_SSE 0
#if defined(_M_X64) && ENABLE_SSE
#include
@@ -15,7 +15,7 @@ extern "C" {
// compile time settings
#define NODE_WORDS (64/4)
-#define MIX_WORDS (MIX_BYTES/4)
+#define MIX_WORDS (ETHASH_MIX_BYTES/4)
#define MIX_NODES (MIX_WORDS / NODE_WORDS)
#include
@@ -34,7 +34,7 @@ void ethash_calculate_dag_item(
node *const ret,
const unsigned node_index,
ethash_params const *params,
- ethash_cache const *cache
+ void const *cache
);
void ethash_quick_hash(
@@ -45,4 +45,4 @@ void ethash_quick_hash(
#ifdef __cplusplus
}
-#endif
\ No newline at end of file
+#endif
diff --git a/libethash/io.c b/libethash/io.c
new file mode 100644
index 000000000..0e935fa59
--- /dev/null
+++ b/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 .
+*/
+/** @file io.c
+ * @author Lefteris Karapetsas
+ * @date 2015
+ */
+#include "io.h"
+#include
+#include
+
+// 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
diff --git a/libethash/io.h b/libethash/io.h
new file mode 100644
index 000000000..0fa292362
--- /dev/null
+++ b/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 .
+*/
+/** @file io.h
+ * @author Lefteris Karapetsas
+ * @date 2015
+ */
+#pragma once
+#include
+#include
+#include
+#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
diff --git a/libethash/io_posix.c b/libethash/io_posix.c
new file mode 100644
index 000000000..9d9ccac69
--- /dev/null
+++ b/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 .
+*/
+/** @file io_posix.c
+ * @author Lefteris Karapetsas
+ * @date 2015
+ */
+
+#include "io.h"
+#include
+#include
+#include
+#include
+#include
+#include
+
+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;
+}
diff --git a/libethash/io_win32.c b/libethash/io_win32.c
new file mode 100644
index 000000000..77367fdd9
--- /dev/null
+++ b/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 .
+*/
+/** @file io_win32.c
+ * @author Lefteris Karapetsas
+ * @date 2015
+ */
+
+#include "io.h"
+#include
+#include
+#include
+
+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;
+}
diff --git a/libethcore/Ethasher.cpp b/libethcore/Ethasher.cpp
index 79a2fdea3..64db69187 100644
--- a/libethcore/Ethasher.cpp
+++ b/libethcore/Ethasher.cpp
@@ -41,7 +41,23 @@ using namespace eth;
Ethasher* dev::eth::Ethasher::s_this = nullptr;
-bytes const& Ethasher::cache(BlockInfo const& _header)
+Ethasher::~Ethasher()
+{
+ while (!m_caches.empty())
+ killCache(m_caches.begin()->first);
+}
+
+void Ethasher::killCache(h256 const& _s)
+{
+ RecursiveGuard l(x_this);
+ if (m_caches.count(_s))
+ {
+ ethash_delete_light(m_caches.at(_s));
+ m_caches.erase(_s);
+ }
+}
+
+void const* Ethasher::cache(BlockInfo const& _header)
{
RecursiveGuard l(x_this);
if (_header.number > c_ethashEpochLength * 2048)
@@ -54,8 +70,7 @@ bytes const& Ethasher::cache(BlockInfo const& _header)
if (!m_caches.count(_header.seedHash()))
{
ethash_params p = params((unsigned)_header.number);
- m_caches[_header.seedHash()].resize(p.cache_size);
- ethash_prep_light(m_caches[_header.seedHash()].data(), &p, _header.seedHash().data());
+ m_caches[_header.seedHash()] = ethash_new_light(&p, _header.seedHash().data());
}
return m_caches[_header.seedHash()];
}
@@ -84,7 +99,7 @@ bytesConstRef Ethasher::full(BlockInfo const& _header)
ethash_params p = params((unsigned)_header.number);
m_fulls[_header.seedHash()] = bytesRef(new byte[p.full_size], p.full_size);
auto c = cache(_header);
- ethash_prep_full(m_fulls[_header.seedHash()].data(), &p, c.data());
+ ethash_prep_full(m_fulls[_header.seedHash()].data(), &p, c);
writeFile(memoFile, m_fulls[_header.seedHash()]);
writeFile(memoFile + ".info", info);
}
@@ -112,17 +127,24 @@ bool Ethasher::verify(BlockInfo const& _header)
h256 boundary = u256((bigint(1) << 256) / _header.difficulty);
- bool ret = ethash_quick_check_difficulty(
+ bool quick = ethash_quick_check_difficulty(
_header.headerHash(WithoutNonce).data(),
(uint64_t)(u64)_header.nonce,
_header.mixHash.data(),
boundary.data());
-#if ETH_DEBUG
+#if !ETH_DEBUG
+ if (!quick)
+ return false;
+#endif
+
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 << "nonce:" << _header.nonce;
cwarn << "mixHash:" << _header.mixHash;
@@ -131,19 +153,16 @@ bool Ethasher::verify(BlockInfo const& _header)
cwarn << "result.value:" << result.value;
cwarn << "result.mixHash:" << result.mixHash;
}
- assert((result.value <= boundary) == ret);
- if (result.value <= boundary)
- assert(result.mixHash == _header.mixHash);
#endif
- return ret;
+ return slow;
}
Ethasher::Result Ethasher::eval(BlockInfo const& _header, Nonce const& _nonce)
{
auto p = Ethasher::params(_header);
ethash_return_value r;
- ethash_compute_light(&r, Ethasher::get()->cache(_header).data(), &p, _header.headerHash(WithoutNonce).data(), (uint64_t)(u64)_nonce);
+ ethash_compute_light(&r, Ethasher::get()->cache(_header), &p, _header.headerHash(WithoutNonce).data(), (uint64_t)(u64)_nonce);
// cdebug << "Ethasher::eval sha3(cache):" << sha3(Ethasher::get()->cache(_header)) << "hh:" << _header.headerHash(WithoutNonce) << "nonce:" << _nonce << " => " << h256(r.result, h256::ConstructFromPointer);
return Result{h256(r.result, h256::ConstructFromPointer), h256(r.mix_hash, h256::ConstructFromPointer)};
}
diff --git a/libethcore/Ethasher.h b/libethcore/Ethasher.h
index c160d38da..a10c206d1 100644
--- a/libethcore/Ethasher.h
+++ b/libethcore/Ethasher.h
@@ -30,21 +30,8 @@
#include
#include
#include // TODO: REMOVE once everything merged into this class and an opaque API can be provided.
-static const unsigned c_ethashRevision = REVISION;
-static const unsigned c_ethashEpochLength = EPOCH_LENGTH;
-#undef REVISION
-#undef DATASET_BYTES_INIT
-#undef DATASET_BYTES_GROWTH
-#undef CACHE_BYTES_INIT
-#undef CACHE_BYTES_GROWTH
-#undef DAGSIZE_BYTES_INIT
-#undef DAG_GROWTH
-#undef EPOCH_LENGTH
-#undef MIX_BYTES
-#undef HASH_BYTES
-#undef DATASET_PARENTS
-#undef CACHE_ROUNDS
-#undef ACCESSES
+static const unsigned c_ethashRevision = ETHASH_REVISION;
+static const unsigned c_ethashEpochLength = ETHASH_EPOCH_LENGTH;
#include "Common.h"
#include "BlockInfo.h"
@@ -57,10 +44,14 @@ class Ethasher
{
public:
Ethasher() {}
+ ~Ethasher();
static Ethasher* get() { if (!s_this) s_this = new Ethasher(); return s_this; }
- bytes const& cache(BlockInfo const& _header);
+ using LightType = void const*;
+ using FullType = void const*;
+
+ LightType cache(BlockInfo const& _header);
bytesConstRef full(BlockInfo const& _header);
static ethash_params params(BlockInfo const& _header);
static ethash_params params(unsigned _n);
@@ -104,9 +95,11 @@ public:
};
private:
+ void killCache(h256 const& _s);
+
static Ethasher* s_this;
RecursiveMutex x_this;
- std::map m_caches;
+ std::map m_caches;
std::map m_fulls;
};
diff --git a/libethcore/Params.h b/libethcore/Params.h
index 7520e49f1..62cf6b2d8 100644
--- a/libethcore/Params.h
+++ b/libethcore/Params.h
@@ -29,15 +29,15 @@ namespace eth
{
//--- 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_minGasLimit;
extern u256 const c_gasLimitBoundDivisor;
+extern u256 const c_genesisDifficulty;
extern u256 const c_minimumDifficulty;
extern u256 const c_difficultyBoundDivisor;
extern u256 const c_durationLimit;
+extern u256 const c_maximumExtraDataSize;
+extern u256 const c_epochDuration;
extern u256 const c_stackLimit;
extern u256 const c_tierStepGas[8]; ///< Once per operation, for a selection of them.
diff --git a/libethereum/BlockChain.cpp b/libethereum/BlockChain.cpp
index 508531f9d..8c0bd2b8b 100644
--- a/libethereum/BlockChain.cpp
+++ b/libethereum/BlockChain.cpp
@@ -339,6 +339,13 @@ h256s BlockChain::import(bytes const& _block, OverlayDB const& _db)
#endif
// All ok - insert into DB
{
+ // ensure parent is cached for later addition.
+ // TODO: this is a bit horrible would be better refactored into an enveloping UpgradableGuard
+ // together with an "ensureCachedWithUpdatableLock(l)" method.
+ // This is safe in practice since the caches don't get flushed nearly often enough to be
+ // done here.
+ details(bi.parentHash);
+
WriteGuard l(x_details);
m_details[newHash] = BlockDetails((unsigned)pd.number + 1, td, bi.parentHash, {});
m_details[bi.parentHash].children.push_back(newHash);
@@ -455,14 +462,14 @@ h256s BlockChain::import(bytes const& _block, OverlayDB const& _db)
h256s BlockChain::treeRoute(h256 const& _from, h256 const& _to, h256* o_common, bool _pre, bool _post) const
{
- // cdebug << "treeRoute" << _from.abridged() << "..." << _to.abridged();
+ cdebug << "treeRoute" << _from.abridged() << "..." << _to.abridged();
if (!_from || !_to)
return h256s();
h256s ret;
h256s back;
unsigned fn = details(_from).number;
unsigned tn = details(_to).number;
- // cdebug << "treeRoute" << fn << "..." << tn;
+ cdebug << "treeRoute" << fn << "..." << tn;
h256 from = _from;
while (fn > tn)
{
@@ -470,7 +477,7 @@ h256s BlockChain::treeRoute(h256 const& _from, h256 const& _to, h256* o_common,
ret.push_back(from);
from = details(from).parent;
fn--;
- // cdebug << "from:" << fn << _from.abridged();
+ cdebug << "from:" << fn << _from.abridged();
}
h256 to = _to;
while (fn < tn)
@@ -479,12 +486,14 @@ h256s BlockChain::treeRoute(h256 const& _from, h256 const& _to, h256* o_common,
back.push_back(to);
to = details(to).parent;
tn--;
- // cdebug << "to:" << tn << _to.abridged();
+ cdebug << "to:" << tn << _to.abridged();
}
while (from != to)
{
- assert(from);
- assert(to);
+ if (!from)
+ assert(from);
+ if (!to)
+ assert(to);
from = details(from).parent;
to = details(to).parent;
if (_pre)
diff --git a/libethereum/Executive.cpp b/libethereum/Executive.cpp
index dfd526bef..0b75a07af 100644
--- a/libethereum/Executive.cpp
+++ b/libethereum/Executive.cpp
@@ -46,7 +46,7 @@ u256 Executive::gasUsed() 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)
@@ -221,6 +221,8 @@ bool Executive::go(OnOpFunc const& _onOp)
if (m_isCreation)
{
+ m_gasForDeposit = m_endGas;
+ m_depositSize = m_out.size();
if (m_out.size() * c_createDataGas <= m_endGas)
{
m_codeDeposit = CodeDeposit::Success;
@@ -228,6 +230,7 @@ bool Executive::go(OnOpFunc const& _onOp)
}
else
{
+
m_codeDeposit = CodeDeposit::Failed;
m_out.reset();
}
diff --git a/libethereum/Executive.h b/libethereum/Executive.h
index 3445ad407..158e86330 100644
--- a/libethereum/Executive.h
+++ b/libethereum/Executive.h
@@ -128,6 +128,8 @@ private:
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.
+ 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.
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.
diff --git a/libethereum/State.cpp b/libethereum/State.cpp
index 378c60fa3..fa457dc41 100644
--- a/libethereum/State.cpp
+++ b/libethereum/State.cpp
@@ -111,22 +111,36 @@ State::State(OverlayDB const& _db, BlockChain const& _bc, h256 _h):
m_state(&m_db),
m_blockReward(c_blockReward)
{
- // TODO THINK: is this necessary?
- m_state.init();
-
auto b = _bc.block(_h);
- BlockInfo bi;
- BlockInfo bip;
- if (_h)
- bi.populate(b);
- if (bi && bi.number)
- bip.populate(_bc.block(bi.parentHash));
- if (!_h || !bip)
+ BlockInfo bi(b);
+
+ if (!bi)
+ {
+ // Might be worth throwing here.
+ cwarn << "Invalid block given for state population: " << _h;
return;
- m_ourAddress = bi.coinbaseAddress;
+ }
- sync(_bc, bi.parentHash, bip);
- enact(&b, _bc);
+ if (bi.number)
+ {
+ // 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):
@@ -711,7 +725,7 @@ void State::commitToMine(BlockChain const& _bc)
uncommitToMine();
// cnote << "Committing to mine on block" << m_previousBlock.hash.abridged();
-#ifdef ETH_PARANOIA
+#if ETH_PARANOIA && 0
commit();
cnote << "Pre-reward stateRoot:" << m_state.root();
#endif
diff --git a/libethereum/Transaction.cpp b/libethereum/Transaction.cpp
index 502f089fb..3228eca70 100644
--- a/libethereum/Transaction.cpp
+++ b/libethereum/Transaction.cpp
@@ -88,12 +88,12 @@ Transaction::Transaction(bytesConstRef _rlpData, CheckSignature _checkSig)
}
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;
}
}
-Address Transaction::safeSender() const noexcept
+Address const& Transaction::safeSender() const noexcept
{
try
{
@@ -102,11 +102,11 @@ Address Transaction::safeSender() const noexcept
catch (...)
{
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)
{
diff --git a/libethereum/Transaction.h b/libethereum/Transaction.h
index bed291868..ab2c12b50 100644
--- a/libethereum/Transaction.h
+++ b/libethereum/Transaction.h
@@ -75,17 +75,30 @@ TransactionException toTransactionException(VMException const& _e);
struct ExecutionResult
{
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;
TransactionException excepted = TransactionException::Unknown;
Address newAddress;
bytes output;
CodeDeposit codeDeposit = CodeDeposit::None;
u256 gasRefunded = 0;
+ unsigned depositSize = 0;
+ u256 gasForDeposit;
};
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.
class Transaction
{
@@ -94,16 +107,16 @@ public:
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.
- 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.
- 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.
- 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.
explicit Transaction(bytesConstRef _rlp, CheckSignature _checkSig);
@@ -118,9 +131,9 @@ public:
bool operator!=(Transaction const& _c) const { return !operator==(_c); }
/// @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.
- Address safeSender() const noexcept;
+ Address const& safeSender() const noexcept;
/// @returns true if transaction is non-null.
explicit operator bool() const { return m_type != NullTransaction; }
diff --git a/libevmcore/Assembly.cpp b/libevmcore/Assembly.cpp
index abe932282..0301c9325 100644
--- a/libevmcore/Assembly.cpp
+++ b/libevmcore/Assembly.cpp
@@ -187,18 +187,7 @@ Assembly& Assembly::optimise(bool _enable)
{
if (!_enable)
return *this;
- std::vector>> rules =
- {
- { { Push, Instruction::POP }, [](AssemblyItemsConstRef) -> AssemblyItems { return {}; } },
- { { PushTag, Instruction::POP }, [](AssemblyItemsConstRef) -> AssemblyItems { return {}; } },
- { { PushString, Instruction::POP }, [](AssemblyItemsConstRef) -> AssemblyItems { return {}; } },
- { { PushSub, Instruction::POP }, [](AssemblyItemsConstRef) -> AssemblyItems { return {}; } },
- { { PushSubSize, Instruction::POP }, [](AssemblyItemsConstRef) -> AssemblyItems { return {}; } },
- { { PushProgramSize, Instruction::POP }, [](AssemblyItemsConstRef) -> AssemblyItems { return {}; } },
- { { Push, PushTag, Instruction::JUMPI }, [](AssemblyItemsConstRef m) -> AssemblyItems { if (m[0].data()) return { m[1], Instruction::JUMP }; else return {}; } },
- { { Instruction::ISZERO, Instruction::ISZERO }, [](AssemblyItemsConstRef) -> AssemblyItems { return {}; } },
- };
-
+ std::vector>> rules;
// jump to next instruction
rules.push_back({ { PushTag, Instruction::JUMP, Tag }, [](AssemblyItemsConstRef m) -> AssemblyItems { if (m[0].m_data == m[2].m_data) return {m[2]}; else return m.toVector(); }});
@@ -235,8 +224,6 @@ Assembly& Assembly::optimise(bool _enable)
*orig = move(*moveIter);
iter = m_items.erase(orig, iter);
}
- if (iter != m_items.end())
- ++iter;
}
for (unsigned i = 0; i < m_items.size(); ++i)
diff --git a/libevmcore/CommonSubexpressionEliminator.cpp b/libevmcore/CommonSubexpressionEliminator.cpp
index 7fed03b4e..47bb5b512 100644
--- a/libevmcore/CommonSubexpressionEliminator.cpp
+++ b/libevmcore/CommonSubexpressionEliminator.cpp
@@ -32,6 +32,8 @@ using namespace dev::eth;
vector CommonSubexpressionEliminator::getOptimizedItems()
{
+ optimizeBreakingItem();
+
map initialStackContents;
map targetStackContents;
int minHeight = m_stackHeight + 1;
@@ -45,19 +47,27 @@ vector CommonSubexpressionEliminator::getOptimizedItems()
// Debug info:
//stream(cout, initialStackContents, targetStackContents);
- return CSECodeGenerator(m_expressionClasses).generateCode(initialStackContents, targetStackContents);
+ AssemblyItems items = CSECodeGenerator(m_expressionClasses, m_storeOperations).generateCode(
+ initialStackContents,
+ targetStackContents
+ );
+ if (m_breakingItem)
+ items.push_back(*m_breakingItem);
+ return items;
}
ostream& CommonSubexpressionEliminator::stream(
ostream& _out,
- map _currentStack,
+ map _initialStack,
map _targetStack
) const
{
auto streamExpressionClass = [this](ostream& _out, ExpressionClasses::Id _id)
{
auto const& expr = m_expressionClasses.representative(_id);
- _out << " " << _id << ": " << *expr.item;
+ _out << " " << dec << _id << ": " << *expr.item;
+ if (expr.sequenceNumber)
+ _out << "@" << dec << expr.sequenceNumber;
_out << "(";
for (ExpressionClasses::Id arg: expr.arguments)
_out << dec << arg << ",";
@@ -66,18 +76,12 @@ ostream& CommonSubexpressionEliminator::stream(
_out << "Optimizer analysis:" << endl;
_out << "Final stack height: " << dec << m_stackHeight << endl;
- _out << "Stack elements: " << endl;
- for (auto const& it: m_stackElements)
- {
- _out << " " << dec << it.first << " = ";
- streamExpressionClass(_out, it.second);
- }
_out << "Equivalence classes: " << endl;
for (ExpressionClasses::Id eqClass = 0; eqClass < m_expressionClasses.size(); ++eqClass)
streamExpressionClass(_out, eqClass);
- _out << "Current stack: " << endl;
- for (auto const& it: _currentStack)
+ _out << "Initial stack: " << endl;
+ for (auto const& it: _initialStack)
{
_out << " " << dec << it.first << ": ";
streamExpressionClass(_out, it.second);
@@ -92,13 +96,12 @@ ostream& CommonSubexpressionEliminator::stream(
return _out;
}
-void CommonSubexpressionEliminator::feedItem(AssemblyItem const& _item)
+void CommonSubexpressionEliminator::feedItem(AssemblyItem const& _item, bool _copyItem)
{
if (_item.type() != Operation)
{
- if (_item.deposit() != 1)
- BOOST_THROW_EXCEPTION(InvalidDeposit());
- setStackElement(++m_stackHeight, m_expressionClasses.find(_item, {}));
+ assertThrow(_item.deposit() == 1, InvalidDeposit, "");
+ setStackElement(++m_stackHeight, m_expressionClasses.find(_item, {}, _copyItem));
}
else
{
@@ -119,12 +122,47 @@ void CommonSubexpressionEliminator::feedItem(AssemblyItem const& _item)
vector arguments(info.args);
for (int i = 0; i < info.args; ++i)
arguments[i] = stackElement(m_stackHeight - i);
- setStackElement(m_stackHeight + _item.deposit(), m_expressionClasses.find(_item, arguments));
+ if (_item.instruction() == Instruction::SSTORE)
+ storeInStorage(arguments[0], arguments[1]);
+ else if (_item.instruction() == Instruction::SLOAD)
+ setStackElement(m_stackHeight + _item.deposit(), loadFromStorage(arguments[0]));
+ else if (_item.instruction() == Instruction::MSTORE)
+ storeInMemory(arguments[0], arguments[1]);
+ else if (_item.instruction() == Instruction::MLOAD)
+ setStackElement(m_stackHeight + _item.deposit(), loadFromMemory(arguments[0]));
+ else
+ setStackElement(m_stackHeight + _item.deposit(), m_expressionClasses.find(_item, arguments, _copyItem));
}
m_stackHeight += _item.deposit();
}
}
+void CommonSubexpressionEliminator::optimizeBreakingItem()
+{
+ if (!m_breakingItem || *m_breakingItem != AssemblyItem(Instruction::JUMPI))
+ return;
+
+ using Id = ExpressionClasses::Id;
+ static AssemblyItem s_jump = Instruction::JUMP;
+
+ Id condition = stackElement(m_stackHeight - 1);
+ Id zero = m_expressionClasses.find(u256(0));
+ if (m_expressionClasses.knownToBeDifferent(condition, zero))
+ {
+ feedItem(Instruction::SWAP1, true);
+ feedItem(Instruction::POP, true);
+ m_breakingItem = &s_jump;
+ return;
+ }
+ Id negatedCondition = m_expressionClasses.find(Instruction::ISZERO, {condition});
+ if (m_expressionClasses.knownToBeDifferent(negatedCondition, zero))
+ {
+ feedItem(Instruction::POP, true);
+ feedItem(Instruction::POP, true);
+ m_breakingItem = nullptr;
+ }
+}
+
void CommonSubexpressionEliminator::setStackElement(int _stackHeight, ExpressionClasses::Id _class)
{
m_stackElements[_stackHeight] = _class;
@@ -132,8 +170,7 @@ void CommonSubexpressionEliminator::setStackElement(int _stackHeight, Expression
void CommonSubexpressionEliminator::swapStackElements(int _stackHeightA, int _stackHeightB)
{
- if (_stackHeightA == _stackHeightB)
- BOOST_THROW_EXCEPTION(OptimizerException() << errinfo_comment("Swap on same stack elements."));
+ assertThrow(_stackHeightA != _stackHeightB, OptimizerException, "Swap on same stack elements.");
// ensure they are created
stackElement(_stackHeightA);
stackElement(_stackHeightB);
@@ -157,6 +194,60 @@ ExpressionClasses::Id CommonSubexpressionEliminator::initialStackElement(int _st
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)
{
switch (_item.type())
@@ -179,7 +270,19 @@ bool SemanticInformation::breaksBasicBlock(AssemblyItem const& _item)
return false;
if (_item.instruction() == Instruction::GAS || _item.instruction() == Instruction::PC)
return true; // GAS and PC assume a specific order of opcodes
+ if (_item.instruction() == Instruction::MSIZE)
+ return true; // msize is modified already by memory access, avoid that for now
+ if (_item.instruction() == Instruction::SHA3)
+ return true; //@todo: we have to compare sha3's not based on their memory addresses but on the memory content.
InstructionInfo info = instructionInfo(_item.instruction());
+ if (_item.instruction() == Instruction::SSTORE)
+ return false;
+ if (_item.instruction() == Instruction::MSTORE)
+ return false;
+ //@todo: We do not handle the following memory instructions for now:
+ // calldatacopy, codecopy, extcodecopy, mstore8,
+ // msize (note that msize also depends on memory read access)
+
// the second requirement will be lifted once it is implemented
return info.sideEffects || info.args > 2;
}
@@ -218,6 +321,16 @@ bool SemanticInformation::isSwapInstruction(AssemblyItem const& _item)
return Instruction::SWAP1 <= _item.instruction() && _item.instruction() <= Instruction::SWAP16;
}
+CSECodeGenerator::CSECodeGenerator(
+ ExpressionClasses& _expressionClasses,
+ vector const& _storeOperations
+):
+ m_expressionClasses(_expressionClasses)
+{
+ for (auto const& store: _storeOperations)
+ m_storeOperations[make_pair(store.target, store.slot)].push_back(store);
+}
+
AssemblyItems CSECodeGenerator::generateCode(
map const& _initialStack,
map const& _targetStackContents
@@ -230,26 +343,40 @@ AssemblyItems CSECodeGenerator::generateCode(
// @todo: provide information about the positions of copies of class elements
- // generate the dependency graph
+ // generate the dependency graph starting from final storage and memory writes and target stack contents
+ for (auto const& p: m_storeOperations)
+ addDependencies(p.second.back().expression);
for (auto const& targetItem: _targetStackContents)
{
m_finalClasses.insert(targetItem.second);
addDependencies(targetItem.second);
}
- // generate the actual elements
+ // store all needed sequenced expressions
+ set> sequencedExpressions;
+ for (auto const& p: m_neededBy)
+ for (auto id: {p.first, p.second})
+ if (unsigned seqNr = m_expressionClasses.representative(id).sequenceNumber)
+ sequencedExpressions.insert(make_pair(seqNr, id));
+
+ // Perform all operations on storage and memory in order, if they are needed.
+ for (auto const& seqAndId: sequencedExpressions)
+ if (!m_classPositions.count(seqAndId.second))
+ generateClassElement(seqAndId.second, true);
+
+ // generate the target stack elements
for (auto const& targetItem: _targetStackContents)
{
- removeStackTopIfPossible();
int position = generateClassElement(targetItem.second);
+ assertThrow(position != c_invalidPosition, OptimizerException, "");
if (position == targetItem.first)
continue;
if (position < targetItem.first)
// it is already at its target, we need another copy
appendDup(position);
else
- appendSwapOrRemove(position);
- appendSwapOrRemove(targetItem.first);
+ appendOrRemoveSwap(position);
+ appendOrRemoveSwap(targetItem.first);
}
// remove surplus elements
@@ -270,23 +397,59 @@ AssemblyItems CSECodeGenerator::generateCode(
// neither initial no target stack, no change in height
finalHeight = 0;
assertThrow(finalHeight == m_stackHeight, OptimizerException, "Incorrect final stack height.");
-
return m_generatedItems;
}
void CSECodeGenerator::addDependencies(ExpressionClasses::Id _c)
{
if (m_neededBy.count(_c))
- return;
- for (ExpressionClasses::Id argument: m_expressionClasses.representative(_c).arguments)
+ return; // we already computed the dependencies for _c
+ ExpressionClasses::Expression expr = m_expressionClasses.representative(_c);
+ for (ExpressionClasses::Id argument: expr.arguments)
{
addDependencies(argument);
m_neededBy.insert(make_pair(argument, _c));
}
+ if (expr.item->type() == Operation && (
+ expr.item->instruction() == Instruction::SLOAD ||
+ expr.item->instruction() == Instruction::MLOAD
+ ))
+ {
+ // this loads an unknown value from storage or memory and thus, in addition to its
+ // arguments, depends on all store operations to addresses where we do not know that
+ // they are different that occur before this load
+ StoreOperation::Target target = expr.item->instruction() == Instruction::SLOAD ?
+ StoreOperation::Storage : StoreOperation::Memory;
+ ExpressionClasses::Id slotToLoadFrom = expr.arguments.at(0);
+ for (auto const& p: m_storeOperations)
+ {
+ if (p.first.first != target)
+ continue;
+ ExpressionClasses::Id slot = p.first.second;
+ StoreOperations const& storeOps = p.second;
+ if (storeOps.front().sequenceNumber > expr.sequenceNumber)
+ continue;
+ if (
+ (target == StoreOperation::Memory && m_expressionClasses.knownToBeDifferentBy32(slot, slotToLoadFrom)) ||
+ (target == StoreOperation::Storage && m_expressionClasses.knownToBeDifferent(slot, slotToLoadFrom))
+ )
+ continue;
+ // note that store and load never have the same sequence number
+ ExpressionClasses::Id latestStore = storeOps.front().expression;
+ for (auto it = ++storeOps.begin(); it != storeOps.end(); ++it)
+ if (it->sequenceNumber < expr.sequenceNumber)
+ latestStore = it->expression;
+ addDependencies(latestStore);
+ m_neededBy.insert(make_pair(latestStore, _c));
+ }
+ }
}
-int CSECodeGenerator::generateClassElement(ExpressionClasses::Id _c)
+int CSECodeGenerator::generateClassElement(ExpressionClasses::Id _c, bool _allowSequenced)
{
+ // do some cleanup
+ removeStackTopIfPossible();
+
if (m_classPositions.count(_c))
{
assertThrow(
@@ -296,7 +459,13 @@ int CSECodeGenerator::generateClassElement(ExpressionClasses::Id _c)
);
return m_classPositions[_c];
}
- ExpressionClasses::Ids const& arguments = m_expressionClasses.representative(_c).arguments;
+ ExpressionClasses::Expression const& expr = m_expressionClasses.representative(_c);
+ assertThrow(
+ _allowSequenced || expr.sequenceNumber == 0,
+ OptimizerException,
+ "Sequence constrained operation requested out of sequence."
+ );
+ ExpressionClasses::Ids const& arguments = expr.arguments;
for (ExpressionClasses::Id arg: boost::adaptors::reverse(arguments))
generateClassElement(arg);
@@ -307,42 +476,42 @@ int CSECodeGenerator::generateClassElement(ExpressionClasses::Id _c)
if (arguments.size() == 1)
{
if (canBeRemoved(arguments[0], _c))
- appendSwapOrRemove(generateClassElement(arguments[0]));
+ appendOrRemoveSwap(classElementPosition(arguments[0]));
else
- appendDup(generateClassElement(arguments[0]));
+ appendDup(classElementPosition(arguments[0]));
}
else if (arguments.size() == 2)
{
if (canBeRemoved(arguments[1], _c))
{
- appendSwapOrRemove(generateClassElement(arguments[1]));
+ appendOrRemoveSwap(classElementPosition(arguments[1]));
if (arguments[0] == arguments[1])
appendDup(m_stackHeight);
else if (canBeRemoved(arguments[0], _c))
{
- appendSwapOrRemove(m_stackHeight - 1);
- appendSwapOrRemove(generateClassElement(arguments[0]));
+ appendOrRemoveSwap(m_stackHeight - 1);
+ appendOrRemoveSwap(classElementPosition(arguments[0]));
}
else
- appendDup(generateClassElement(arguments[0]));
+ appendDup(classElementPosition(arguments[0]));
}
else
{
if (arguments[0] == arguments[1])
{
- appendDup(generateClassElement(arguments[0]));
+ appendDup(classElementPosition(arguments[0]));
appendDup(m_stackHeight);
}
else if (canBeRemoved(arguments[0], _c))
{
- appendSwapOrRemove(generateClassElement(arguments[0]));
- appendDup(generateClassElement(arguments[1]));
- appendSwapOrRemove(m_stackHeight - 1);
+ appendOrRemoveSwap(classElementPosition(arguments[0]));
+ appendDup(classElementPosition(arguments[1]));
+ appendOrRemoveSwap(m_stackHeight - 1);
}
else
{
- appendDup(generateClassElement(arguments[1]));
- appendDup(generateClassElement(arguments[0]));
+ appendDup(classElementPosition(arguments[1]));
+ appendDup(classElementPosition(arguments[0]));
}
}
}
@@ -355,20 +524,41 @@ int CSECodeGenerator::generateClassElement(ExpressionClasses::Id _c)
for (size_t i = 0; i < arguments.size(); ++i)
assertThrow(m_stack[m_stackHeight - i] == arguments[i], OptimizerException, "Expected arguments not present." );
- AssemblyItem const& item = *m_expressionClasses.representative(_c).item;
- while (SemanticInformation::isCommutativeOperation(item) &&
+ while (SemanticInformation::isCommutativeOperation(*expr.item) &&
!m_generatedItems.empty() &&
m_generatedItems.back() == AssemblyItem(Instruction::SWAP1))
// this will not append a swap but remove the one that is already there
- appendSwapOrRemove(m_stackHeight - 1);
+ appendOrRemoveSwap(m_stackHeight - 1);
for (auto arg: arguments)
if (canBeRemoved(arg, _c))
m_classPositions[arg] = c_invalidPosition;
for (size_t i = 0; i < arguments.size(); ++i)
m_stack.erase(m_stackHeight - i);
- appendItem(*m_expressionClasses.representative(_c).item);
- m_stack[m_stackHeight] = _c;
- return m_classPositions[_c] = m_stackHeight;
+ appendItem(*expr.item);
+ if (expr.item->type() != Operation || instructionInfo(expr.item->instruction()).ret == 1)
+ {
+ m_stack[m_stackHeight] = _c;
+ return m_classPositions[_c] = m_stackHeight;
+ }
+ else
+ {
+ assertThrow(
+ instructionInfo(expr.item->instruction()).ret == 0,
+ OptimizerException,
+ "Invalid number of return values."
+ );
+ return m_classPositions[_c] = c_invalidPosition;
+ }
+}
+
+int CSECodeGenerator::classElementPosition(ExpressionClasses::Id _id) const
+{
+ assertThrow(
+ m_classPositions.count(_id) && m_classPositions.at(_id) != c_invalidPosition,
+ OptimizerException,
+ "Element requested but is not present."
+ );
+ return m_classPositions.at(_id);
}
bool CSECodeGenerator::canBeRemoved(ExpressionClasses::Id _element, ExpressionClasses::Id _result)
@@ -401,22 +591,23 @@ bool CSECodeGenerator::removeStackTopIfPossible()
void CSECodeGenerator::appendDup(int _fromPosition)
{
- int nr = 1 + m_stackHeight - _fromPosition;
- assertThrow(nr <= 16, StackTooDeepException, "Stack too deep.");
- assertThrow(1 <= nr, OptimizerException, "Invalid stack access.");
- m_generatedItems.push_back(AssemblyItem(dupInstruction(nr)));
- m_stackHeight++;
+ assertThrow(_fromPosition != c_invalidPosition, OptimizerException, "");
+ int instructionNum = 1 + m_stackHeight - _fromPosition;
+ assertThrow(instructionNum <= 16, StackTooDeepException, "Stack too deep.");
+ assertThrow(1 <= instructionNum, OptimizerException, "Invalid stack access.");
+ appendItem(AssemblyItem(dupInstruction(instructionNum)));
m_stack[m_stackHeight] = m_stack[_fromPosition];
}
-void CSECodeGenerator::appendSwapOrRemove(int _fromPosition)
+void CSECodeGenerator::appendOrRemoveSwap(int _fromPosition)
{
+ assertThrow(_fromPosition != c_invalidPosition, OptimizerException, "");
if (_fromPosition == m_stackHeight)
return;
- int nr = m_stackHeight - _fromPosition;
- assertThrow(nr <= 16, StackTooDeepException, "Stack too deep.");
- assertThrow(1 <= nr, OptimizerException, "Invalid stack access.");
- m_generatedItems.push_back(AssemblyItem(swapInstruction(nr)));
+ int instructionNum = m_stackHeight - _fromPosition;
+ assertThrow(instructionNum <= 16, StackTooDeepException, "Stack too deep.");
+ assertThrow(1 <= instructionNum, OptimizerException, "Invalid stack access.");
+ appendItem(AssemblyItem(swapInstruction(instructionNum)));
// The value of a class can be present in multiple locations on the stack. We only update the
// "canonical" one that is tracked by m_classPositions
if (m_classPositions[m_stack[m_stackHeight]] == m_stackHeight)
diff --git a/libevmcore/CommonSubexpressionEliminator.h b/libevmcore/CommonSubexpressionEliminator.h
index 331a7642d..a9a0c60a4 100644
--- a/libevmcore/CommonSubexpressionEliminator.h
+++ b/libevmcore/CommonSubexpressionEliminator.h
@@ -25,6 +25,8 @@
#include
#include