Browse Source

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

cl-refactor
CJentzsch 10 years ago
parent
commit
62d6ff9942
  1. 5
      CMakeLists.txt
  2. 1
      alethzero/CMakeLists.txt
  3. 2
      alethzero/DappLoader.cpp
  4. 22
      alethzero/Main.ui
  5. 8
      alethzero/MainWin.cpp
  6. 1
      alethzero/MainWin.h
  7. 48
      eth/main.cpp
  8. 171
      ethkey/KeyAux.h
  9. 4
      ethminer/MinerAux.h
  10. 4
      evmjit/CMakeLists.txt
  11. 18
      evmjit/evmcc/CMakeLists.txt
  12. 210
      evmjit/evmcc/evmcc.cpp
  13. 56
      evmjit/include/evmjit/DataTypes.h
  14. 163
      evmjit/include/evmjit/JIT.h
  15. 17
      evmjit/libevmjit-cpp/Env.cpp
  16. 41
      evmjit/libevmjit-cpp/JitVM.cpp
  17. 6
      evmjit/libevmjit-cpp/JitVM.h
  18. 29
      evmjit/libevmjit-cpp/Utils.h
  19. 674
      evmjit/libevmjit/Arith256.cpp
  20. 27
      evmjit/libevmjit/Arith256.h
  21. 3
      evmjit/libevmjit/Array.cpp
  22. 2
      evmjit/libevmjit/BasicBlock.h
  23. 8
      evmjit/libevmjit/CMakeLists.txt
  24. 27
      evmjit/libevmjit/Cache.cpp
  25. 9
      evmjit/libevmjit/Cache.h
  26. 39
      evmjit/libevmjit/Common.h
  27. 152
      evmjit/libevmjit/Compiler.cpp
  28. 4
      evmjit/libevmjit/Compiler.h
  29. 25
      evmjit/libevmjit/CompilerHelper.h
  30. 5
      evmjit/libevmjit/ExecStats.cpp
  31. 35
      evmjit/libevmjit/ExecStats.h
  32. 192
      evmjit/libevmjit/ExecutionEngine.cpp
  33. 59
      evmjit/libevmjit/ExecutionEngine.h
  34. 24
      evmjit/libevmjit/Ext.cpp
  35. 2
      evmjit/libevmjit/Ext.h
  36. 1
      evmjit/libevmjit/GasMeter.h
  37. 5
      evmjit/libevmjit/Instruction.cpp
  38. 5
      evmjit/libevmjit/Instruction.h
  39. 222
      evmjit/libevmjit/JIT.cpp
  40. 6
      evmjit/libevmjit/Memory.cpp
  41. 97
      evmjit/libevmjit/Optimizer.cpp
  42. 2
      evmjit/libevmjit/Optimizer.h
  43. 43
      evmjit/libevmjit/Runtime.cpp
  44. 30
      evmjit/libevmjit/Runtime.h
  45. 63
      evmjit/libevmjit/RuntimeData.h
  46. 56
      evmjit/libevmjit/RuntimeManager.cpp
  47. 6
      evmjit/libevmjit/RuntimeManager.h
  48. 120
      evmjit/libevmjit/Stack.cpp
  49. 8
      evmjit/libevmjit/Stack.h
  50. 3
      evmjit/libevmjit/Type.cpp
  51. 9
      evmjit/libevmjit/Type.h
  52. 25
      evmjit/libevmjit/interface.cpp
  53. 9
      libdevcore/Common.cpp
  54. 17
      libdevcore/Common.h
  55. 6
      libdevcore/CommonData.h
  56. 2
      libdevcore/RLP.cpp
  57. 2
      libdevcore/RLP.h
  58. 4
      libdevcrypto/CMakeLists.txt
  59. 49
      libdevcrypto/Common.cpp
  60. 2
      libdevcrypto/Common.h
  61. 30
      libdevcrypto/CryptoPP.cpp
  62. 8
      libdevcrypto/CryptoPP.h
  63. 2
      libdevcrypto/ECDHE.cpp
  64. 4
      libethash-cl/ethash_cl_miner.cpp
  65. 1
      libethash-cl/ethash_cl_miner.h
  66. 2
      libethcore/CMakeLists.txt
  67. 2
      libethcore/Ethash.cpp
  68. 118
      libethcore/Transaction.cpp
  69. 177
      libethcore/Transaction.h
  70. 53
      libethereum/BlockChain.cpp
  71. 5
      libethereum/BlockChainSync.cpp
  72. 65
      libethereum/BlockQueue.cpp
  73. 5
      libethereum/BlockQueue.h
  74. 1
      libethereum/CMakeLists.txt
  75. 21
      libethereum/Client.cpp
  76. 5
      libethereum/Client.h
  77. 4
      libethereum/EthereumPeer.cpp
  78. 6
      libethereum/State.cpp
  79. 2
      libethereum/State.h
  80. 87
      libethereum/Transaction.cpp
  81. 126
      libethereum/Transaction.h
  82. 1
      libethereum/TransactionQueue.cpp
  83. 24
      libethereum/VerifiedBlock.h
  84. 1
      libethereumx/CMakeLists.txt
  85. 2
      libevm/SmartVM.cpp
  86. 1
      libjsqrc/ethereumjs/.travis.yml
  87. 2
      libjsqrc/ethereumjs/bower.json
  88. 312
      libjsqrc/ethereumjs/dist/web3-light.js
  89. 4
      libjsqrc/ethereumjs/dist/web3-light.min.js
  90. 314
      libjsqrc/ethereumjs/dist/web3.js
  91. 26
      libjsqrc/ethereumjs/dist/web3.js.map
  92. 6
      libjsqrc/ethereumjs/dist/web3.min.js
  93. 83
      libjsqrc/ethereumjs/example/contract_array.html
  94. 27
      libjsqrc/ethereumjs/gulpfile.js
  95. 95
      libjsqrc/ethereumjs/karma.conf.js
  96. 7
      libjsqrc/ethereumjs/lib/solidity/coder.js
  97. 56
      libjsqrc/ethereumjs/lib/solidity/formatters.js
  98. 9
      libjsqrc/ethereumjs/lib/solidity/param.js
  99. 24
      libjsqrc/ethereumjs/lib/utils/utils.js
  100. 2
      libjsqrc/ethereumjs/lib/version.json

5
CMakeLists.txt

@ -397,7 +397,10 @@ if (JSCONSOLE)
add_subdirectory(ethconsole)
endif ()
add_subdirectory(secp256k1)
if (NOT WIN32)
add_subdirectory(secp256k1)
endif ()
add_subdirectory(libscrypt)
add_subdirectory(libdevcrypto)

1
alethzero/CMakeLists.txt

@ -52,7 +52,6 @@ target_link_libraries(${EXECUTABLE} ethereum)
target_link_libraries(${EXECUTABLE} evm)
target_link_libraries(${EXECUTABLE} ethcore)
target_link_libraries(${EXECUTABLE} devcrypto)
target_link_libraries(${EXECUTABLE} secp256k1)
target_link_libraries(${EXECUTABLE} lll)
if (SOLIDITY)
target_link_libraries(${EXECUTABLE} solidity)

2
alethzero/DappLoader.cpp

@ -129,7 +129,7 @@ void DappLoader::downloadComplete(QNetworkReply* _reply)
h256 expected = m_uriHashes[requestUrl];
bytes package(reinterpret_cast<unsigned char const*>(data.constData()), reinterpret_cast<unsigned char const*>(data.constData() + data.size()));
Secp256k1 dec;
Secp256k1PP dec;
dec.decrypt(expected, package);
h256 got = sha3(package);
if (got != expected)

22
alethzero/Main.ui

@ -44,14 +44,14 @@
<string>0 bytes used</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="syncStatus">
<property name="text">
<string></string>
</property>
</widget>
</item>
</item>
<item>
<widget class="QLabel" name="syncStatus">
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="chainStatus">
<property name="text">
@ -224,6 +224,7 @@
<string>&amp;Config</string>
</property>
<addaction name="gasPrices"/>
<addaction name="sentinel"/>
</widget>
<addaction name="menu_File"/>
<addaction name="menu_View"/>
@ -1782,6 +1783,11 @@ font-size: 14pt</string>
<string>&amp;Gas Prices...</string>
</property>
</action>
<action name="sentinel">
<property name="text">
<string>&amp;Sentinel...</string>
</property>
</action>
</widget>
<layoutdefault spacing="6" margin="11"/>
<customwidgets>

8
alethzero/MainWin.cpp

@ -296,6 +296,14 @@ void Main::on_gasPrices_triggered()
}
}
void Main::on_sentinel_triggered()
{
bool ok;
QString sentinel = QInputDialog::getText(nullptr, "Enter sentinel address", "Enter the sentinel address for bad block reporting (e.g. http://badblockserver.com:8080). Enter nothing to disable.", QLineEdit::Normal, QString::fromStdString(ethereum()->sentinel()), &ok);
if (ok)
ethereum()->setSentinel(sentinel.toStdString());
}
void Main::on_newIdentity_triggered()
{
KeyPair kp = KeyPair::create();

1
alethzero/MainWin.h

@ -196,6 +196,7 @@ private slots:
// Config
void on_gasPrices_triggered();
void on_sentinel_triggered();
void refreshWhisper();
void refreshBlockChain();

48
eth/main.cpp

@ -115,6 +115,7 @@ void interactiveHelp()
<< " reprocess <block> Reprocess a given block." << endl
<< " dumptrace <block> <index> <filename> <format> Dumps a transaction trace" << endl << "to <filename>. <format> should be one of pretty, standard, standard+." << endl
<< " dumpreceipt <block> <index> Dumps a transation receipt." << endl
<< " hashrate Print the current hashrate in hashes per second if the client is mining." << endl
<< " exit Exits the application." << endl;
}
@ -668,9 +669,7 @@ int main(int argc, char** argv)
}
#if ETH_EVMJIT
else if (arg == "-J" || arg == "--jit")
{
jit = true;
}
#endif
else if (arg == "-h" || arg == "--help")
help();
@ -703,12 +702,24 @@ int main(int argc, char** argv)
string logbuf;
std::string additional;
g_logPost = [&](std::string const& a, char const*){
if (g_silence)
logbuf += a + "\n";
else
cout << "\r \r" << a << endl << additional << flush;
};
if (interactive)
g_logPost = [&](std::string const& a, char const*){
static SpinLock s_lock;
SpinGuard l(s_lock);
if (g_silence)
logbuf += a + "\n";
else
cout << "\r \r" << a << endl << additional << flush;
// helpful to use OutputDebugString on windows
#ifdef _WIN32
{
OutputDebugStringA(a.data());
OutputDebugStringA("\n");
}
#endif
};
auto getPassword = [&](string const& prompt){
auto s = g_silence;
@ -790,7 +801,7 @@ int main(int argc, char** argv)
{
bytes block(8);
in.read((char*)block.data(), 8);
block.resize(RLP(block, RLP::LaisezFaire).actualSize());
block.resize(RLP(block, RLP::LaissezFaire).actualSize());
in.read((char*)block.data() + 8, block.size() - 8);
switch (web3.ethereum()->queueBlock(block, safeImport))
@ -815,12 +826,13 @@ int main(int argc, char** argv)
cout << i << " more imported at " << (round(i * 10 / d) / 10) << " blocks/s. " << imported << " imported in " << e << " seconds at " << (round(imported * 10 / e) / 10) << " blocks/s (#" << web3.ethereum()->number() << ")" << endl;
last = (unsigned)e;
lastImported = imported;
// cout << web3.ethereum()->blockQueueStatus() << endl;
}
}
while (web3.ethereum()->blockQueue().items().first + web3.ethereum()->blockQueue().items().second > 0)
{
sleep(1);
this_thread::sleep_for(chrono::seconds(1));
web3.ethereum()->syncQueue(100000);
}
double e = chrono::duration_cast<chrono::milliseconds>(chrono::steady_clock::now() - t).count() / 1000.0;
@ -981,17 +993,11 @@ int main(int argc, char** argv)
web3.addNode(p2p::NodeId(), addrPort);
}
else if (cmd == "netstop")
{
web3.stopNetwork();
}
else if (c && cmd == "minestart")
{
c->startMining();
}
else if (c && cmd == "minestop")
{
c->stopMining();
}
else if (c && cmd == "mineforce")
{
string enable;
@ -1114,13 +1120,11 @@ int main(int argc, char** argv)
}
}
else if (c && cmd == "block")
{
cout << "Current block: " << c->blockChain().details().number << endl;
}
else if (c && cmd == "blockqueue")
{
cout << "Current blockqueue status: " << endl << c->blockQueueStatus() << endl;
}
else if (c && cmd == "hashrate")
cout << "Current hash rate: " << toString(c->hashrate()) << " hashes per second." << endl;
else if (c && cmd == "findblock")
{
if (iss.peek() != -1)
@ -1169,13 +1173,9 @@ int main(int argc, char** argv)
cwarn << "Require parameter: findblock HASH";
}
else if (c && cmd == "firstunknown")
{
cout << "first unknown blockhash: " << c->blockQueue().firstUnknown().hex() << endl;
}
else if (c && cmd == "retryunknown")
{
c->retryUnknown();
}
else if (cmd == "peers")
{
for (auto it: web3.peers())

171
ethkey/KeyAux.h

@ -32,6 +32,7 @@
#include <libdevcore/FileSystem.h>
#include <libethcore/KeyManager.h>
#include <libethcore/ICAP.h>
#include <libethcore/Transaction.h>
#include "BuildInfo.h"
using namespace std;
using namespace dev;
@ -105,7 +106,9 @@ public:
ImportWithAddress,
Export,
Recode,
Kill
Kill,
SignTx,
DecodeTx,
};
KeyCLI(OperationMode _mode = OperationMode::None): m_mode(_mode) {}
@ -131,8 +134,13 @@ public:
auto v = argv[++i];
m_kdfParams[n] = v;
}
else if (arg == "--new-bare")
m_mode = OperationMode::NewBare;
else if (arg == "--sign-tx" && i + 1 < argc)
{
m_mode = OperationMode::SignTx;
m_signKey = argv[++i];
}
else if (arg == "--decode-tx")
m_mode = OperationMode::DecodeTx;
else if (arg == "--import-bare")
m_mode = OperationMode::ImportBare;
else if (arg == "--list-bare")
@ -173,7 +181,7 @@ public:
m_mode = OperationMode::Recode;
else if (arg == "--no-icap")
m_icap = false;
else if (m_mode == OperationMode::ImportBare || m_mode == OperationMode::InspectBare || m_mode == OperationMode::KillBare || m_mode == OperationMode::Recode || m_mode == OperationMode::Export || m_mode == OperationMode::RecodeBare || m_mode == OperationMode::ExportBare)
else if (m_mode == OperationMode::DecodeTx || m_mode == OperationMode::SignTx || m_mode == OperationMode::ImportBare || m_mode == OperationMode::InspectBare || m_mode == OperationMode::KillBare || m_mode == OperationMode::Recode || m_mode == OperationMode::Export || m_mode == OperationMode::RecodeBare || m_mode == OperationMode::ExportBare)
m_inputs.push_back(arg);
else
return false;
@ -209,6 +217,127 @@ public:
}
}
}
else if (m_mode == OperationMode::DecodeTx)
{
string const& i = m_inputs[0];
bytes b = fromHex(i);
if (b.empty())
{
std::string s = contentsString(i);
b = fromHex(s);
if (b.empty())
b = asBytes(s);
}
if (b.empty())
cerr << "Unknown file or bad hex: " << i << endl;
else
try
{
TransactionBase t(b, CheckTransaction::Everything);
cout << "Transaction " << t.sha3().hex() << endl;
if (t.isCreation())
{
cout << " type: creation" << endl;
cout << " code: " << toHex(t.data()) << endl;
}
else
{
cout << " type: message" << endl;
cout << " to: " << t.to().hex() << endl;
cout << " data: " << (t.data().empty() ? "none" : toHex(t.data())) << endl;
}
cout << " from: " << t.from().hex() << endl;
cout << " value: " << formatBalance(t.value()) << " (" << t.value() << " wei)" << endl;
cout << " nonce: " << t.nonce() << endl;
cout << " gas: " << t.gas() << endl;
cout << " gas price: " << formatBalance(t.gasPrice()) << " (" << t.gasPrice() << " wei)" << endl;
cout << " signing hash: " << t.sha3(WithoutSignature).hex() << endl;
cout << " v: " << (int)t.signature().v << endl;
cout << " r: " << t.signature().r << endl;
cout << " s: " << t.signature().s << endl;
}
catch (Exception& ex)
{
cerr << "Invalid transaction: " << ex.what() << endl;
}
}
else if (m_mode == OperationMode::SignTx)
{
Secret s;
string json = contentsString(m_signKey);
if (!json.empty())
{
SecretStore store(m_secretsPath);
s = Secret(store.secret(store.readKeyContent(json), [&](){ return getPassword("Enter password for key: "); }));
}
else
{
if (h128 u = fromUUID(m_signKey))
{
SecretStore store(m_secretsPath);
s = Secret(store.secret(u, [&](){ return getPassword("Enter password for key: "); }));
}
else if (Address a = Address(m_signKey))
{
KeyManager wallet(m_walletPath, m_secretsPath);
if (wallet.exists())
{
openWallet(wallet);
s = wallet.secret(a, [&](){ return getPassword("Enter password for key: "); });
}
else
{
cerr << "Wallet doesn't exist." << endl;
exit(-1);
}
}
else
{
cerr << "Bad file, UUID and address: " << m_signKey << endl;
exit(-1);
}
}
if (!s)
{
cerr << "UUID/address not found: " << m_signKey << endl;
exit(-1);
}
for (string const& i: m_inputs)
{
bytes b = fromHex(i);
bool isFile = false;
if (b.empty())
{
isFile = true;
std::string s = contentsString(i);
b = fromHex(s);
if (b.empty())
b = asBytes(s);
}
if (b.empty())
cerr << "Unknown file or bad hex: " << i << endl;
else
try
{
TransactionBase t(b, CheckTransaction::None);
t.sign(s);
cout << t.sha3() << ": ";
if (isFile)
{
writeFile(i + ".signed", t.data());
cout << i + ".signed" << endl;
}
else
cout << toHex(t.data()) << endl;
}
catch (Exception& ex)
{
cerr << "Invalid transaction: " << ex.what() << endl;
}
}
}
else if (m_mode < OperationMode::CreateWallet)
{
SecretStore store(m_secretsPath);
@ -297,17 +426,7 @@ public:
{
KeyManager wallet(m_walletPath, m_secretsPath);
if (wallet.exists())
while (true)
{
if (wallet.load(m_masterPassword))
break;
if (!m_masterPassword.empty())
{
cout << "Password invalid. Try again." << endl;
m_masterPassword.clear();
}
m_masterPassword = getPassword("Please enter your MASTER password: ");
}
openWallet(wallet);
else
{
cerr << "Couldn't open wallet. Does it exist?" << endl;
@ -419,6 +538,10 @@ public:
<< " --wallet-path <path> Specify Ethereum wallet path (default: " << KeyManager::defaultPath() << ")" << endl
<< " -m, --master <password> Specify wallet (master) password." << endl
<< endl
<< "Transaction operating modes:" << endl
<< " -d,--decode-tx [<hex>|<file>] Decode given transaction." << endl
<< " -s,--sign-tx [ <address>|<uuid>|<file> ] [ <hex>|<file> , ... ] (Re-)Sign given transaction." << endl
<< endl
<< "Encryption configuration:" << endl
<< " --kdf <kdfname> Specify KDF to use when encrypting (default: sc rypt)" << endl
<< " --kdf-param <name> <value> Specify a parameter for the KDF." << endl
@ -445,6 +568,21 @@ public:
}
private:
void openWallet(KeyManager& _w)
{
while (true)
{
if (_w.load(m_masterPassword))
break;
if (!m_masterPassword.empty())
{
cout << "Password invalid. Try again." << endl;
m_masterPassword.clear();
}
m_masterPassword = getPassword("Please enter your MASTER password: ");
}
}
KDF kdf() const { return m_kdf == "pbkdf2" ? KDF::PBKDF2_SHA256 : KDF::Scrypt; }
/// Operating mode.
@ -468,6 +606,9 @@ private:
/// Importing
strings m_inputs;
/// Signing
string m_signKey;
string m_kdf = "scrypt";
map<string, string> m_kdfParams;
// string m_cipher;

4
ethminer/MinerAux.h

@ -217,7 +217,7 @@ public:
auto boundary = bi.boundary();
m = boost::to_lower_copy(string(argv[++i]));
bi.nonce = h64(m);
auto r = EthashAux::eval(bi.seedHash(), powHash, bi.nonce);
auto r = EthashAux::eval(seedHash, powHash, bi.nonce);
bool valid = r.value < boundary;
cout << (valid ? "VALID :-)" : "INVALID :-(") << endl;
cout << r.value << (valid ? " < " : " >= ") << boundary << endl;
@ -265,7 +265,6 @@ public:
ProofOfWork::CPUMiner::setNumInstances(m_miningThreads);
else if (m_minerType == MinerType::GPU)
{
ProofOfWork::GPUMiner::setNumInstances(m_miningThreads);
if (!ProofOfWork::GPUMiner::configureGPU(
m_openclPlatform,
m_openclDevice,
@ -277,6 +276,7 @@ public:
cout << "No GPU device with sufficient memory was found. Can't GPU mine. Remove the -G argument" << endl;
exit(1);
}
ProofOfWork::GPUMiner::setNumInstances(m_miningThreads);
}
if (mode == OperationMode::DAGInit)
doInitDAG(m_initDAG);

4
evmjit/CMakeLists.txt

@ -29,7 +29,3 @@ add_subdirectory(libevmjit)
if(EVMJIT_CPP)
add_subdirectory(libevmjit-cpp)
endif()
if(EVMJIT_TOOLS)
add_subdirectory(evmcc)
endif()

18
evmjit/evmcc/CMakeLists.txt

@ -1,18 +0,0 @@
set(TARGET_NAME evmcc)
set(SOURCES
evmcc.cpp
)
source_group("" FILES ${SOURCES})
add_executable(${TARGET_NAME} ${SOURCES})
set_property(TARGET ${TARGET_NAME} PROPERTY FOLDER "tools")
include_directories(../..)
include_directories(${LLVM_INCLUDE_DIRS})
include_directories(${Boost_INCLUDE_DIRS})
target_link_libraries(${TARGET_NAME} ethereum)
target_link_libraries(${TARGET_NAME} ${Boost_PROGRAM_OPTIONS_LIBRARIES})
install(TARGETS ${TARGET_NAME} DESTINATION bin )

210
evmjit/evmcc/evmcc.cpp

@ -1,210 +0,0 @@
#include <chrono>
#include <iostream>
#include <fstream>
#include <ostream>
#include <string>
#include <vector>
#include <boost/algorithm/string.hpp>
#include <boost/program_options.hpp>
#include <llvm/Bitcode/ReaderWriter.h>
#include <llvm/Support/raw_os_ostream.h>
#include <llvm/Support/Signals.h>
#include <llvm/Support/PrettyStackTrace.h>
#include <libdevcore/Common.h>
#include <libdevcore/CommonIO.h>
#include <libevmcore/Instruction.h>
#include <libevm/ExtVMFace.h>
#include <evmjit/libevmjit/Compiler.h>
#include <evmjit/libevmjit/ExecutionEngine.h>
void parseProgramOptions(int _argc, char** _argv, boost::program_options::variables_map& _varMap)
{
namespace opt = boost::program_options;
opt::options_description explicitOpts("Allowed options");
explicitOpts.add_options()
("help,h", "show usage information")
("compile,c", "compile the code to LLVM IR")
("interpret,i", "compile the code to LLVM IR and execute")
("gas,g", opt::value<size_t>(), "set initial gas for execution")
("disassemble,d", "dissassemble the code")
("dump-cfg", "dump control flow graph to graphviz file")
("dont-optimize", "turn off optimizations")
("optimize-stack", "optimize stack use between basic blocks (default: on)")
("rewrite-switch", "rewrite LLVM switch to branches (default: on)")
("output-ll", opt::value<std::string>(), "dump generated LLVM IR to file")
("output-bc", opt::value<std::string>(), "dump generated LLVM bitcode to file")
("show-logs", "output LOG statements to stderr")
("verbose,V", "enable verbose output");
opt::options_description implicitOpts("Input files");
implicitOpts.add_options()
("input-file", opt::value<std::string>(), "input file");
opt::options_description allOpts("");
allOpts.add(explicitOpts).add(implicitOpts);
opt::positional_options_description inputOpts;
inputOpts.add("input-file", 1);
const char* errorMsg = nullptr;
try
{
auto parser = opt::command_line_parser(_argc, _argv).options(allOpts).positional(inputOpts);
opt::store(parser.run(), _varMap);
opt::notify(_varMap);
}
catch (boost::program_options::error& err)
{
errorMsg = err.what();
}
if (!errorMsg && _varMap.count("input-file") == 0)
errorMsg = "missing input file name";
if (_varMap.count("disassemble") == 0
&& _varMap.count("compile") == 0
&& _varMap.count("interpret") == 0)
{
errorMsg = "at least one of -c, -i, -d is required";
}
if (errorMsg || _varMap.count("help"))
{
if (errorMsg)
std::cerr << "Error: " << errorMsg << std::endl;
std::cout << "Usage: " << _argv[0] << " <options> input-file " << std::endl
<< explicitOpts << std::endl;
std::exit(errorMsg ? 1 : 0);
}
}
int main(int argc, char** argv)
{
llvm::sys::PrintStackTraceOnErrorSignal();
llvm::PrettyStackTraceProgram X(argc, argv);
boost::program_options::variables_map options;
parseProgramOptions(argc, argv, options);
auto inputFile = options["input-file"].as<std::string>();
std::ifstream ifs(inputFile);
if (!ifs.is_open())
{
std::cerr << "cannot open input file " << inputFile << std::endl;
exit(1);
}
std::string src((std::istreambuf_iterator<char>(ifs)),
(std::istreambuf_iterator<char>()));
boost::algorithm::trim(src);
using namespace dev;
bytes bytecode = fromHex(src);
if (options.count("disassemble"))
{
std::string assembly = eth::disassemble(bytecode);
std::cout << assembly << std::endl;
}
if (options.count("compile") || options.count("interpret"))
{
size_t initialGas = 10000;
if (options.count("gas"))
initialGas = options["gas"].as<size_t>();
auto compilationStartTime = std::chrono::high_resolution_clock::now();
eth::jit::Compiler::Options compilerOptions;
compilerOptions.dumpCFG = options.count("dump-cfg") > 0;
bool optimize = options.count("dont-optimize") == 0;
compilerOptions.optimizeStack = optimize || options.count("optimize-stack") > 0;
compilerOptions.rewriteSwitchToBranches = optimize || options.count("rewrite-switch") > 0;
auto compiler = eth::jit::Compiler(compilerOptions);
auto module = compiler.compile(bytecode, "main");
auto compilationEndTime = std::chrono::high_resolution_clock::now();
module->dump();
if (options.count("output-ll"))
{
auto outputFile = options["output-ll"].as<std::string>();
std::ofstream ofs(outputFile);
if (!ofs.is_open())
{
std::cerr << "cannot open output file " << outputFile << std::endl;
exit(1);
}
llvm::raw_os_ostream ros(ofs);
module->print(ros, nullptr);
ofs.close();
}
if (options.count("output-bc"))
{
auto outputFile = options["output-bc"].as<std::string>();
std::ofstream ofs(outputFile);
if (!ofs.is_open())
{
std::cerr << "cannot open output file " << outputFile << std::endl;
exit(1);
}
llvm::raw_os_ostream ros(ofs);
llvm::WriteBitcodeToFile(module.get(), ros);
ros.flush();
ofs.close();
}
if (options.count("verbose"))
{
std::cerr << "*** Compilation time: "
<< std::chrono::duration_cast<std::chrono::microseconds>(compilationEndTime - compilationStartTime).count()
<< std::endl;
}
if (options.count("interpret"))
{
using namespace eth::jit;
ExecutionEngine engine;
eth::jit::u256 gas = initialGas;
// Create random runtime data
RuntimeData data;
data.set(RuntimeData::Gas, gas);
data.set(RuntimeData::Address, (u160)Address(1122334455667788));
data.set(RuntimeData::Caller, (u160)Address(0xfacefacefaceface));
data.set(RuntimeData::Origin, (u160)Address(101010101010101010));
data.set(RuntimeData::CallValue, 0xabcd);
data.set(RuntimeData::CallDataSize, 3);
data.set(RuntimeData::GasPrice, 1003);
data.set(RuntimeData::CoinBase, (u160)Address(101010101010101015));
data.set(RuntimeData::TimeStamp, 1005);
data.set(RuntimeData::Number, 1006);
data.set(RuntimeData::Difficulty, 16);
data.set(RuntimeData::GasLimit, 1008);
data.set(RuntimeData::CodeSize, bytecode.size());
data.callData = (uint8_t*)"abc";
data.code = bytecode.data();
// BROKEN: env_* functions must be implemented & RuntimeData struct created
// TODO: Do not compile module again
auto result = engine.run(bytecode, &data, nullptr);
return static_cast<int>(result);
}
}
return 0;
}

56
evmjit/include/evmjit/DataTypes.h

@ -1,56 +0,0 @@
#pragma once
#include <cstdint>
#include <functional>
namespace dev
{
namespace evmjit
{
struct h256
{
uint64_t words[4];
};
inline bool operator==(h256 _h1, h256 _h2)
{
return _h1.words[0] == _h2.words[0] &&
_h1.words[1] == _h2.words[1] &&
_h1.words[2] == _h2.words[2] &&
_h1.words[3] == _h2.words[3];
}
/// Representation of 256-bit value binary compatible with LLVM i256
struct i256
{
uint64_t a = 0;
uint64_t b = 0;
uint64_t c = 0;
uint64_t d = 0;
i256() = default;
i256(h256 _h)
{
a = _h.words[0];
b = _h.words[1];
c = _h.words[2];
d = _h.words[3];
}
};
}
}
namespace std
{
template<> struct hash<dev::evmjit::h256>
{
size_t operator()(dev::evmjit::h256 const& _h) const
{
/// This implementation expects the argument to be a full 256-bit Keccak hash.
/// It does nothing more than returning a slice of the input hash.
return static_cast<size_t>(_h.words[0]);
};
};
}

163
evmjit/include/evmjit/JIT.h

@ -1,19 +1,149 @@
#pragma once
#include "evmjit/DataTypes.h"
#include <cstdint>
#include <cstring>
#include <functional>
#ifdef _MSC_VER
#define EXPORT __declspec(dllexport)
#define _ALLOW_KEYWORD_MACROS
#define noexcept throw()
#else
#define EXPORT
#endif
namespace dev
{
namespace eth
namespace evmjit
{
namespace jit
using byte = uint8_t;
using bytes_ref = std::tuple<byte const*, size_t>;
/// Representation of 256-bit hash value
struct h256
{
class ExecutionEngine;
}
uint64_t words[4];
};
inline bool operator==(h256 const& _h1, h256 const& _h2)
{
return _h1.words[0] == _h2.words[0] &&
_h1.words[1] == _h2.words[1] &&
_h1.words[2] == _h2.words[2] &&
_h1.words[3] == _h2.words[3];
}
namespace evmjit
/// Representation of 256-bit value binary compatible with LLVM i256
struct i256
{
uint64_t words[4];
i256() = default;
i256(h256 const& _h) { std::memcpy(this, &_h, sizeof(*this)); }
};
// TODO: Merge with ExecutionContext
struct RuntimeData
{
enum Index
{
Gas,
GasPrice,
CallData,
CallDataSize,
Address,
Caller,
Origin,
CallValue,
CoinBase,
Difficulty,
GasLimit,
Number,
Timestamp,
Code,
CodeSize,
SuicideDestAddress = Address, ///< Suicide balance destination address
ReturnData = CallData, ///< Return data pointer (set only in case of RETURN)
ReturnDataSize = CallDataSize, ///< Return data size (set only in case of RETURN)
};
static size_t const numElements = CodeSize + 1;
int64_t gas = 0;
int64_t gasPrice = 0;
byte const* callData = nullptr;
uint64_t callDataSize = 0;
i256 address;
i256 caller;
i256 origin;
i256 callValue;
i256 coinBase;
i256 difficulty;
i256 gasLimit;
uint64_t number = 0;
int64_t timestamp = 0;
byte const* code = nullptr;
uint64_t codeSize = 0;
h256 codeHash;
};
/// VM Environment (ExtVM) opaque type
struct Env;
enum class ReturnCode
{
// Success codes
Stop = 0,
Return = 1,
Suicide = 2,
// Standard error codes
OutOfGas = -1,
StackUnderflow = -2,
BadJumpDestination = -3,
BadInstruction = -4,
Rejected = -5, ///< Input data (code, gas, block info, etc.) does not meet JIT requirement and execution request has been rejected
// Internal error codes
LLVMConfigError = -101,
LLVMCompileError = -102,
LLVMLinkError = -103,
UnexpectedException = -111,
LinkerWorkaround = -299,
};
class ExecutionContext
{
public:
ExecutionContext() = default;
ExecutionContext(RuntimeData& _data, Env* _env) { init(_data, _env); }
ExecutionContext(ExecutionContext const&) = delete;
ExecutionContext& operator=(ExecutionContext const&) = delete;
EXPORT ~ExecutionContext();
void init(RuntimeData& _data, Env* _env) { m_data = &_data; m_env = _env; }
byte const* code() const { return m_data->code; }
uint64_t codeSize() const { return m_data->codeSize; }
h256 const& codeHash() const { return m_data->codeHash; }
bytes_ref getReturnData() const;
protected:
RuntimeData* m_data = nullptr; ///< Pointer to data. Expected by compiled contract.
Env* m_env = nullptr; ///< Pointer to environment proxy. Expected by compiled contract.
byte* m_memData = nullptr;
uint64_t m_memSize = 0;
uint64_t m_memCap = 0;
public:
/// Reference to returned data (RETURN opcode used)
bytes_ref returnData;
};
class JIT
{
@ -23,14 +153,23 @@ public:
/// Returns `true` if the EVM code has been compiled and loaded into memory.
/// In this case the code can be executed without overhead.
/// \param _codeHash The Keccak hash of the EVM code.
static bool isCodeReady(h256 _codeHash);
private:
friend class dev::eth::jit::ExecutionEngine;
EXPORT static bool isCodeReady(h256 const& _codeHash);
static uint64_t getCode(h256 _codeHash);
static void mapCode(h256 _codeHash, uint64_t _funcAddr);
EXPORT static ReturnCode exec(ExecutionContext& _context);
};
}
}
namespace std
{
template<> struct hash<dev::evmjit::h256>
{
size_t operator()(dev::evmjit::h256 const& _h) const
{
/// This implementation expects the argument to be a full 256-bit Keccak hash.
/// It does nothing more than returning a slice of the input hash.
return static_cast<size_t>(_h.words[0]);
};
};
}

17
evmjit/libevmjit-cpp/Env.cpp

@ -3,7 +3,6 @@
#include <libdevcore/SHA3.h>
#include <libevmcore/Params.h>
#include <libevm/ExtVMFace.h>
#include <evmjit/DataTypes.h>
#include "Utils.h"
@ -21,15 +20,15 @@ extern "C"
EXPORT void env_sload(ExtVMFace* _env, i256* _index, i256* o_value)
{
auto index = llvm2eth(*_index);
auto index = jit2eth(*_index);
auto value = _env->store(index); // Interface uses native endianness
*o_value = eth2llvm(value);
*o_value = eth2jit(value);
}
EXPORT void env_sstore(ExtVMFace* _env, i256* _index, i256* _value)
{
auto index = llvm2eth(*_index);
auto value = llvm2eth(*_value);
auto index = jit2eth(*_index);
auto value = jit2eth(*_value);
if (value == 0 && _env->store(index) != 0) // If delete
_env->sub.refunds += c_sstoreRefundGas; // Increase refund counter
@ -40,17 +39,17 @@ extern "C"
EXPORT void env_balance(ExtVMFace* _env, h256* _address, i256* o_value)
{
auto u = _env->balance(right160(*_address));
*o_value = eth2llvm(u);
*o_value = eth2jit(u);
}
EXPORT void env_blockhash(ExtVMFace* _env, i256* _number, h256* o_hash)
{
*o_hash = _env->blockhash(llvm2eth(*_number));
*o_hash = _env->blockhash(jit2eth(*_number));
}
EXPORT void env_create(ExtVMFace* _env, int64_t* io_gas, i256* _endowment, byte* _initBeg, uint64_t _initSize, h256* o_address)
{
auto endowment = llvm2eth(*_endowment);
auto endowment = jit2eth(*_endowment);
if (_env->balance(_env->myAddress) >= endowment && _env->depth < 1024)
{
u256 gas = *io_gas;
@ -65,7 +64,7 @@ extern "C"
EXPORT bool env_call(ExtVMFace* _env, int64_t* io_gas, int64_t _callGas, h256* _receiveAddress, i256* _value, byte* _inBeg, uint64_t _inSize, byte* _outBeg, uint64_t _outSize, h256* _codeAddress)
{
CallParameters params;
params.value = llvm2eth(*_value);
params.value = jit2eth(*_value);
params.senderAddress = _env->myAddress;
params.receiveAddress = right160(*_receiveAddress);
params.codeAddress = right160(*_codeAddress);

41
evmjit/libevmjit-cpp/JitVM.cpp

@ -7,7 +7,6 @@
#include <libdevcore/SHA3.h>
#include <libevm/VM.h>
#include <libevm/VMFactory.h>
#include <evmjit/libevmjit/ExecutionEngine.h>
#include "Utils.h"
@ -20,8 +19,6 @@ extern "C" void env_sload(); // fake declaration for linker symbol stripping wor
bytesConstRef JitVM::execImpl(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp)
{
using namespace jit;
auto rejected = false;
// TODO: Rejecting transactions with gas limit > 2^63 can be used by attacker to take JIT out of scope
rejected |= io_gas > std::numeric_limits<decltype(m_data.gas)>::max(); // Do not accept requests with gas > 2^63 (int64 max)
@ -40,36 +37,38 @@ bytesConstRef JitVM::execImpl(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _on
m_data.gasPrice = static_cast<decltype(m_data.gasPrice)>(_ext.gasPrice);
m_data.callData = _ext.data.data();
m_data.callDataSize = _ext.data.size();
m_data.address = eth2llvm(fromAddress(_ext.myAddress));
m_data.caller = eth2llvm(fromAddress(_ext.caller));
m_data.origin = eth2llvm(fromAddress(_ext.origin));
m_data.callValue = eth2llvm(_ext.value);
m_data.coinBase = eth2llvm(fromAddress(_ext.currentBlock.coinbaseAddress));
m_data.difficulty = eth2llvm(_ext.currentBlock.difficulty);
m_data.gasLimit = eth2llvm(_ext.currentBlock.gasLimit);
m_data.address = eth2jit(fromAddress(_ext.myAddress));
m_data.caller = eth2jit(fromAddress(_ext.caller));
m_data.origin = eth2jit(fromAddress(_ext.origin));
m_data.callValue = eth2jit(_ext.value);
m_data.coinBase = eth2jit(fromAddress(_ext.currentBlock.coinbaseAddress));
m_data.difficulty = eth2jit(_ext.currentBlock.difficulty);
m_data.gasLimit = eth2jit(_ext.currentBlock.gasLimit);
m_data.number = static_cast<decltype(m_data.number)>(_ext.currentBlock.number);
m_data.timestamp = static_cast<decltype(m_data.timestamp)>(_ext.currentBlock.timestamp);
m_data.code = _ext.code.data();
m_data.codeSize = _ext.code.size();
m_data.codeHash = eth2llvm(_ext.codeHash);
m_data.codeHash = eth2jit(_ext.codeHash);
auto env = reinterpret_cast<Env*>(&_ext);
auto exitCode = m_engine.run(&m_data, env);
// Pass pointer to ExtVMFace casted to evmjit::Env* opaque type.
// JIT will do nothing with the pointer, just pass it to Env callback functions implemented in Env.cpp.
m_context.init(m_data, reinterpret_cast<evmjit::Env*>(&_ext));
auto exitCode = evmjit::JIT::exec(m_context);
switch (exitCode)
{
case ReturnCode::Suicide:
_ext.suicide(right160(llvm2eth(m_data.address)));
case evmjit::ReturnCode::Suicide:
_ext.suicide(right160(jit2eth(m_data.address)));
break;
case ReturnCode::BadJumpDestination:
case evmjit::ReturnCode::BadJumpDestination:
BOOST_THROW_EXCEPTION(BadJumpDestination());
case ReturnCode::OutOfGas:
case evmjit::ReturnCode::OutOfGas:
BOOST_THROW_EXCEPTION(OutOfGas());
case ReturnCode::StackUnderflow:
case evmjit::ReturnCode::StackUnderflow: // FIXME: Remove support for detail errors
BOOST_THROW_EXCEPTION(StackUnderflow());
case ReturnCode::BadInstruction:
case evmjit::ReturnCode::BadInstruction:
BOOST_THROW_EXCEPTION(BadInstruction());
case ReturnCode::LinkerWorkaround: // never happens
case evmjit::ReturnCode::LinkerWorkaround: // never happens
env_sload(); // but forces linker to include env_* JIT callback functions
break;
default:
@ -77,7 +76,7 @@ bytesConstRef JitVM::execImpl(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _on
}
io_gas = m_data.gas;
return {std::get<0>(m_engine.returnData), std::get<1>(m_engine.returnData)};
return {std::get<0>(m_context.returnData), std::get<1>(m_context.returnData)};
}
}

6
evmjit/libevmjit-cpp/JitVM.h

@ -1,7 +1,7 @@
#pragma once
#include <libevm/VMFace.h>
#include <evmjit/libevmjit/ExecutionEngine.h>
#include <evmjit/JIT.h>
namespace dev
{
@ -14,8 +14,8 @@ public:
virtual bytesConstRef execImpl(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp) override final;
private:
jit::RuntimeData m_data;
jit::ExecutionEngine m_engine;
evmjit::RuntimeData m_data;
evmjit::ExecutionContext m_context;
std::unique_ptr<VMFace> m_fallbackVM; ///< VM used in case of input data rejected by JIT
};

29
evmjit/libevmjit-cpp/Utils.h

@ -1,40 +1,41 @@
#pragma once
#include <evmjit/DataTypes.h>
#include <evmjit/JIT.h>
namespace dev
{
namespace eth
{
inline u256 llvm2eth(evmjit::i256 _i)
/// Converts EVM JIT representation of 256-bit integer to eth type dev::u256.
inline u256 jit2eth(evmjit::i256 _i)
{
u256 u = 0;
u |= _i.d;
u256 u = _i.words[3];
u <<= 64;
u |= _i.c;
u |= _i.words[2];
u <<= 64;
u |= _i.b;
u |= _i.words[1];
u <<= 64;
u |= _i.a;
u |= _i.words[0];
return u;
}
inline evmjit::i256 eth2llvm(u256 _u)
/// Converts eth type dev::u256 to EVM JIT representation of 256-bit integer.
inline evmjit::i256 eth2jit(u256 _u)
{
evmjit::i256 i;
u256 mask = 0xFFFFFFFFFFFFFFFF;
i.a = static_cast<uint64_t>(_u & mask);
i.words[0] = static_cast<uint64_t>(_u);
_u >>= 64;
i.b = static_cast<uint64_t>(_u & mask);
i.words[1] = static_cast<uint64_t>(_u);
_u >>= 64;
i.c = static_cast<uint64_t>(_u & mask);
i.words[2] = static_cast<uint64_t>(_u);
_u >>= 64;
i.d = static_cast<uint64_t>(_u & mask);
i.words[3] = static_cast<uint64_t>(_u);
return i;
}
inline evmjit::h256 eth2llvm(h256 _u)
/// Converts eth type dev::h256 to EVM JIT representation of 256-bit hash value.
inline evmjit::h256 eth2jit(h256 _u)
{
/// Just directly copies memory
return *(evmjit::h256*)&_u;

674
evmjit/libevmjit/Arith256.cpp

@ -4,6 +4,7 @@
#include <iomanip>
#include "preprocessor/llvm_includes_start.h"
#include <llvm/IR/Module.h>
#include <llvm/IR/IntrinsicInst.h>
#include "preprocessor/llvm_includes_end.h"
@ -32,186 +33,364 @@ void Arith256::debug(llvm::Value* _value, char _c)
createCall(m_debug, {m_builder.CreateZExtOrTrunc(_value, Type::Word), m_builder.getInt8(_c)});
}
llvm::Function* Arith256::getMulFunc()
llvm::Function* Arith256::getMulFunc(llvm::Module& _module)
{
auto& func = m_mul;
if (!func)
{
llvm::Type* argTypes[] = {Type::Word, Type::Word};
func = llvm::Function::Create(llvm::FunctionType::get(Type::Word, argTypes, false), llvm::Function::PrivateLinkage, "mul", getModule());
func->setDoesNotThrow();
func->setDoesNotAccessMemory();
static const auto funcName = "evm.mul.i256";
if (auto func = _module.getFunction(funcName))
return func;
llvm::Type* argTypes[] = {Type::Word, Type::Word};
auto func = llvm::Function::Create(llvm::FunctionType::get(Type::Word, argTypes, false), llvm::Function::PrivateLinkage, funcName, &_module);
func->setDoesNotThrow();
func->setDoesNotAccessMemory();
auto x = &func->getArgumentList().front();
x->setName("x");
auto y = x->getNextNode();
y->setName("y");
auto bb = llvm::BasicBlock::Create(_module.getContext(), {}, func);
auto builder = llvm::IRBuilder<>{bb};
auto i64 = Type::Size;
auto i128 = builder.getIntNTy(128);
auto i256 = Type::Word;
auto c64 = Constant::get(64);
auto c128 = Constant::get(128);
auto c192 = Constant::get(192);
auto x_lo = builder.CreateTrunc(x, i64, "x.lo");
auto y_lo = builder.CreateTrunc(y, i64, "y.lo");
auto x_mi = builder.CreateTrunc(builder.CreateLShr(x, c64), i64);
auto y_mi = builder.CreateTrunc(builder.CreateLShr(y, c64), i64);
auto x_hi = builder.CreateTrunc(builder.CreateLShr(x, c128), i128);
auto y_hi = builder.CreateTrunc(builder.CreateLShr(y, c128), i128);
auto t1 = builder.CreateMul(builder.CreateZExt(x_lo, i128), builder.CreateZExt(y_lo, i128));
auto t2 = builder.CreateMul(builder.CreateZExt(x_lo, i128), builder.CreateZExt(y_mi, i128));
auto t3 = builder.CreateMul(builder.CreateZExt(x_lo, i128), y_hi);
auto t4 = builder.CreateMul(builder.CreateZExt(x_mi, i128), builder.CreateZExt(y_lo, i128));
auto t5 = builder.CreateMul(builder.CreateZExt(x_mi, i128), builder.CreateZExt(y_mi, i128));
auto t6 = builder.CreateMul(builder.CreateZExt(x_mi, i128), y_hi);
auto t7 = builder.CreateMul(x_hi, builder.CreateZExt(y_lo, i128));
auto t8 = builder.CreateMul(x_hi, builder.CreateZExt(y_mi, i128));
auto p = builder.CreateZExt(t1, i256);
p = builder.CreateAdd(p, builder.CreateShl(builder.CreateZExt(t2, i256), c64));
p = builder.CreateAdd(p, builder.CreateShl(builder.CreateZExt(t3, i256), c128));
p = builder.CreateAdd(p, builder.CreateShl(builder.CreateZExt(t4, i256), c64));
p = builder.CreateAdd(p, builder.CreateShl(builder.CreateZExt(t5, i256), c128));
p = builder.CreateAdd(p, builder.CreateShl(builder.CreateZExt(t6, i256), c192));
p = builder.CreateAdd(p, builder.CreateShl(builder.CreateZExt(t7, i256), c128));
p = builder.CreateAdd(p, builder.CreateShl(builder.CreateZExt(t8, i256), c192));
builder.CreateRet(p);
return func;
}
auto x = &func->getArgumentList().front();
x->setName("x");
auto y = x->getNextNode();
y->setName("y");
llvm::Function* Arith256::getMul512Func(llvm::Module& _module)
{
static const auto funcName = "evm.mul.i512";
if (auto func = _module.getFunction(funcName))
return func;
auto i512Ty = llvm::IntegerType::get(_module.getContext(), 512);
auto func = llvm::Function::Create(llvm::FunctionType::get(i512Ty, {Type::Word, Type::Word}, false), llvm::Function::PrivateLinkage, funcName, &_module);
func->setDoesNotThrow();
func->setDoesNotAccessMemory();
auto x = &func->getArgumentList().front();
x->setName("x");
auto y = x->getNextNode();
y->setName("y");
auto bb = llvm::BasicBlock::Create(_module.getContext(), {}, func);
auto builder = llvm::IRBuilder<>{bb};
auto i128 = builder.getIntNTy(128);
auto i256 = Type::Word;
auto x_lo = builder.CreateZExt(builder.CreateTrunc(x, i128, "x.lo"), i256);
auto y_lo = builder.CreateZExt(builder.CreateTrunc(y, i128, "y.lo"), i256);
auto x_hi = builder.CreateZExt(builder.CreateTrunc(builder.CreateLShr(x, Constant::get(128)), i128, "x.hi"), i256);
auto y_hi = builder.CreateZExt(builder.CreateTrunc(builder.CreateLShr(y, Constant::get(128)), i128, "y.hi"), i256);
auto mul256Func = getMulFunc(_module);
auto t1 = builder.CreateCall(mul256Func, {x_lo, y_lo});
auto t2 = builder.CreateCall(mul256Func, {x_lo, y_hi});
auto t3 = builder.CreateCall(mul256Func, {x_hi, y_lo});
auto t4 = builder.CreateCall(mul256Func, {x_hi, y_hi});
auto p = builder.CreateZExt(t1, i512Ty);
p = builder.CreateAdd(p, builder.CreateShl(builder.CreateZExt(t2, i512Ty), builder.getIntN(512, 128)));
p = builder.CreateAdd(p, builder.CreateShl(builder.CreateZExt(t3, i512Ty), builder.getIntN(512, 128)));
p = builder.CreateAdd(p, builder.CreateShl(builder.CreateZExt(t4, i512Ty), builder.getIntN(512, 256)));
builder.CreateRet(p);
InsertPointGuard guard{m_builder};
auto bb = llvm::BasicBlock::Create(m_builder.getContext(), {}, func);
m_builder.SetInsertPoint(bb);
auto i64 = Type::Size;
auto i128 = m_builder.getIntNTy(128);
auto i256 = Type::Word;
auto c64 = Constant::get(64);
auto c128 = Constant::get(128);
auto c192 = Constant::get(192);
auto x_lo = m_builder.CreateTrunc(x, i64, "x.lo");
auto y_lo = m_builder.CreateTrunc(y, i64, "y.lo");
auto x_mi = m_builder.CreateTrunc(m_builder.CreateLShr(x, c64), i64);
auto y_mi = m_builder.CreateTrunc(m_builder.CreateLShr(y, c64), i64);
auto x_hi = m_builder.CreateTrunc(m_builder.CreateLShr(x, c128), i128);
auto y_hi = m_builder.CreateTrunc(m_builder.CreateLShr(y, c128), i128);
auto t1 = m_builder.CreateMul(m_builder.CreateZExt(x_lo, i128), m_builder.CreateZExt(y_lo, i128));
auto t2 = m_builder.CreateMul(m_builder.CreateZExt(x_lo, i128), m_builder.CreateZExt(y_mi, i128));
auto t3 = m_builder.CreateMul(m_builder.CreateZExt(x_lo, i128), y_hi);
auto t4 = m_builder.CreateMul(m_builder.CreateZExt(x_mi, i128), m_builder.CreateZExt(y_lo, i128));
auto t5 = m_builder.CreateMul(m_builder.CreateZExt(x_mi, i128), m_builder.CreateZExt(y_mi, i128));
auto t6 = m_builder.CreateMul(m_builder.CreateZExt(x_mi, i128), y_hi);
auto t7 = m_builder.CreateMul(x_hi, m_builder.CreateZExt(y_lo, i128));
auto t8 = m_builder.CreateMul(x_hi, m_builder.CreateZExt(y_mi, i128));
auto p = m_builder.CreateZExt(t1, i256);
p = m_builder.CreateAdd(p, m_builder.CreateShl(m_builder.CreateZExt(t2, i256), c64));
p = m_builder.CreateAdd(p, m_builder.CreateShl(m_builder.CreateZExt(t3, i256), c128));
p = m_builder.CreateAdd(p, m_builder.CreateShl(m_builder.CreateZExt(t4, i256), c64));
p = m_builder.CreateAdd(p, m_builder.CreateShl(m_builder.CreateZExt(t5, i256), c128));
p = m_builder.CreateAdd(p, m_builder.CreateShl(m_builder.CreateZExt(t6, i256), c192));
p = m_builder.CreateAdd(p, m_builder.CreateShl(m_builder.CreateZExt(t7, i256), c128));
p = m_builder.CreateAdd(p, m_builder.CreateShl(m_builder.CreateZExt(t8, i256), c192));
m_builder.CreateRet(p);
}
return func;
}
llvm::Function* Arith256::getMul512Func()
namespace
{
auto& func = m_mul512;
if (!func)
{
auto i512 = m_builder.getIntNTy(512);
llvm::Type* argTypes[] = {Type::Word, Type::Word};
func = llvm::Function::Create(llvm::FunctionType::get(i512, argTypes, false), llvm::Function::PrivateLinkage, "mul512", getModule());
func->setDoesNotThrow();
func->setDoesNotAccessMemory();
llvm::Function* createUDivRemFunc(llvm::Type* _type, llvm::Module& _module, char const* _funcName)
{
// Based of "Improved shift divisor algorithm" from "Software Integer Division" by Microsoft Research
// The following algorithm also handles divisor of value 0 returning 0 for both quotient and remainder
auto retType = llvm::VectorType::get(_type, 2);
auto func = llvm::Function::Create(llvm::FunctionType::get(retType, {_type, _type}, false), llvm::Function::PrivateLinkage, _funcName, &_module);
func->setDoesNotThrow();
func->setDoesNotAccessMemory();
auto zero = llvm::ConstantInt::get(_type, 0);
auto one = llvm::ConstantInt::get(_type, 1);
auto x = &func->getArgumentList().front();
x->setName("x");
auto yArg = x->getNextNode();
yArg->setName("y");
auto entryBB = llvm::BasicBlock::Create(_module.getContext(), "Entry", func);
auto mainBB = llvm::BasicBlock::Create(_module.getContext(), "Main", func);
auto loopBB = llvm::BasicBlock::Create(_module.getContext(), "Loop", func);
auto continueBB = llvm::BasicBlock::Create(_module.getContext(), "Continue", func);
auto returnBB = llvm::BasicBlock::Create(_module.getContext(), "Return", func);
auto builder = llvm::IRBuilder<>{entryBB};
auto yLEx = builder.CreateICmpULE(yArg, x);
auto r0 = x;
builder.CreateCondBr(yLEx, mainBB, returnBB);
builder.SetInsertPoint(mainBB);
auto ctlzIntr = llvm::Intrinsic::getDeclaration(&_module, llvm::Intrinsic::ctlz, _type);
// both y and r are non-zero
auto yLz = builder.CreateCall(ctlzIntr, {yArg, builder.getInt1(true)}, "y.lz");
auto rLz = builder.CreateCall(ctlzIntr, {r0, builder.getInt1(true)}, "r.lz");
auto i0 = builder.CreateNUWSub(yLz, rLz, "i0");
auto y0 = builder.CreateShl(yArg, i0);
builder.CreateBr(loopBB);
builder.SetInsertPoint(loopBB);
auto yPhi = builder.CreatePHI(_type, 2, "y.phi");
auto rPhi = builder.CreatePHI(_type, 2, "r.phi");
auto iPhi = builder.CreatePHI(_type, 2, "i.phi");
auto qPhi = builder.CreatePHI(_type, 2, "q.phi");
auto rUpdate = builder.CreateNUWSub(rPhi, yPhi);
auto qUpdate = builder.CreateOr(qPhi, one); // q += 1, q lowest bit is 0
auto rGEy = builder.CreateICmpUGE(rPhi, yPhi);
auto r1 = builder.CreateSelect(rGEy, rUpdate, rPhi, "r1");
auto q1 = builder.CreateSelect(rGEy, qUpdate, qPhi, "q");
auto iZero = builder.CreateICmpEQ(iPhi, zero);
builder.CreateCondBr(iZero, returnBB, continueBB);
builder.SetInsertPoint(continueBB);
auto i2 = builder.CreateNUWSub(iPhi, one);
auto q2 = builder.CreateShl(q1, one);
auto y2 = builder.CreateLShr(yPhi, one);
builder.CreateBr(loopBB);
yPhi->addIncoming(y0, mainBB);
yPhi->addIncoming(y2, continueBB);
rPhi->addIncoming(r0, mainBB);
rPhi->addIncoming(r1, continueBB);
iPhi->addIncoming(i0, mainBB);
iPhi->addIncoming(i2, continueBB);
qPhi->addIncoming(zero, mainBB);
qPhi->addIncoming(q2, continueBB);
builder.SetInsertPoint(returnBB);
auto qRet = builder.CreatePHI(_type, 2, "q.ret");
qRet->addIncoming(zero, entryBB);
qRet->addIncoming(q1, loopBB);
auto rRet = builder.CreatePHI(_type, 2, "r.ret");
rRet->addIncoming(r0, entryBB);
rRet->addIncoming(r1, loopBB);
auto ret = builder.CreateInsertElement(llvm::UndefValue::get(retType), qRet, uint64_t(0), "ret0");
ret = builder.CreateInsertElement(ret, rRet, 1, "ret");
builder.CreateRet(ret);
auto x = &func->getArgumentList().front();
x->setName("x");
auto y = x->getNextNode();
y->setName("y");
return func;
}
}
llvm::Function* Arith256::getUDivRem256Func(llvm::Module& _module)
{
static const auto funcName = "evm.udivrem.i256";
if (auto func = _module.getFunction(funcName))
return func;
return createUDivRemFunc(Type::Word, _module, funcName);
}
llvm::Function* Arith256::getUDivRem512Func(llvm::Module& _module)
{
static const auto funcName = "evm.udivrem.i512";
if (auto func = _module.getFunction(funcName))
return func;
return createUDivRemFunc(llvm::IntegerType::get(_module.getContext(), 512), _module, funcName);
}
llvm::Function* Arith256::getUDiv256Func(llvm::Module& _module)
{
static const auto funcName = "evm.udiv.i256";
if (auto func = _module.getFunction(funcName))
return func;
auto udivremFunc = getUDivRem256Func(_module);
auto func = llvm::Function::Create(llvm::FunctionType::get(Type::Word, {Type::Word, Type::Word}, false), llvm::Function::PrivateLinkage, funcName, &_module);
func->setDoesNotThrow();
func->setDoesNotAccessMemory();
auto x = &func->getArgumentList().front();
x->setName("x");
auto y = x->getNextNode();
y->setName("y");
auto bb = llvm::BasicBlock::Create(_module.getContext(), {}, func);
auto builder = llvm::IRBuilder<>{bb};
auto udivrem = builder.CreateCall(udivremFunc, {x, y});
auto udiv = builder.CreateExtractElement(udivrem, uint64_t(0));
builder.CreateRet(udiv);
InsertPointGuard guard{m_builder};
auto bb = llvm::BasicBlock::Create(m_builder.getContext(), {}, func);
m_builder.SetInsertPoint(bb);
auto i128 = m_builder.getIntNTy(128);
auto i256 = Type::Word;
auto x_lo = m_builder.CreateZExt(m_builder.CreateTrunc(x, i128, "x.lo"), i256);
auto y_lo = m_builder.CreateZExt(m_builder.CreateTrunc(y, i128, "y.lo"), i256);
auto x_hi = m_builder.CreateZExt(m_builder.CreateTrunc(m_builder.CreateLShr(x, Constant::get(128)), i128, "x.hi"), i256);
auto y_hi = m_builder.CreateZExt(m_builder.CreateTrunc(m_builder.CreateLShr(y, Constant::get(128)), i128, "y.hi"), i256);
auto t1 = createCall(getMulFunc(), {x_lo, y_lo});
auto t2 = createCall(getMulFunc(), {x_lo, y_hi});
auto t3 = createCall(getMulFunc(), {x_hi, y_lo});
auto t4 = createCall(getMulFunc(), {x_hi, y_hi});
auto p = m_builder.CreateZExt(t1, i512);
p = m_builder.CreateAdd(p, m_builder.CreateShl(m_builder.CreateZExt(t2, i512), m_builder.getIntN(512, 128)));
p = m_builder.CreateAdd(p, m_builder.CreateShl(m_builder.CreateZExt(t3, i512), m_builder.getIntN(512, 128)));
p = m_builder.CreateAdd(p, m_builder.CreateShl(m_builder.CreateZExt(t4, i512), m_builder.getIntN(512, 256)));
m_builder.CreateRet(p);
}
return func;
}
llvm::Function* Arith256::getDivFunc(llvm::Type* _type)
namespace
{
llvm::Function* createURemFunc(llvm::Type* _type, llvm::Module& _module, char const* _funcName)
{
auto& func = _type == Type::Word ? m_div : m_div512;
auto udivremFunc = _type == Type::Word ? Arith256::getUDivRem256Func(_module) : Arith256::getUDivRem512Func(_module);
if (!func)
{
// Based of "Improved shift divisor algorithm" from "Software Integer Division" by Microsoft Research
// The following algorithm also handles divisor of value 0 returning 0 for both quotient and reminder
auto func = llvm::Function::Create(llvm::FunctionType::get(_type, {_type, _type}, false), llvm::Function::PrivateLinkage, _funcName, &_module);
func->setDoesNotThrow();
func->setDoesNotAccessMemory();
llvm::Type* argTypes[] = {_type, _type};
auto retType = llvm::StructType::get(m_builder.getContext(), llvm::ArrayRef<llvm::Type*>{argTypes});
auto funcName = _type == Type::Word ? "div" : "div512";
func = llvm::Function::Create(llvm::FunctionType::get(retType, argTypes, false), llvm::Function::PrivateLinkage, funcName, getModule());
func->setDoesNotThrow();
func->setDoesNotAccessMemory();
auto x = &func->getArgumentList().front();
x->setName("x");
auto y = x->getNextNode();
y->setName("y");
auto zero = llvm::ConstantInt::get(_type, 0);
auto one = llvm::ConstantInt::get(_type, 1);
auto bb = llvm::BasicBlock::Create(_module.getContext(), {}, func);
auto builder = llvm::IRBuilder<>{bb};
auto udivrem = builder.CreateCall(udivremFunc, {x, y});
auto r = builder.CreateExtractElement(udivrem, uint64_t(1));
builder.CreateRet(r);
auto x = &func->getArgumentList().front();
x->setName("x");
auto yArg = x->getNextNode();
yArg->setName("y");
return func;
}
}
InsertPointGuard guard{m_builder};
llvm::Function* Arith256::getURem256Func(llvm::Module& _module)
{
static const auto funcName = "evm.urem.i256";
if (auto func = _module.getFunction(funcName))
return func;
return createURemFunc(Type::Word, _module, funcName);
}
auto entryBB = llvm::BasicBlock::Create(m_builder.getContext(), "Entry", func);
auto mainBB = llvm::BasicBlock::Create(m_builder.getContext(), "Main", func);
auto loopBB = llvm::BasicBlock::Create(m_builder.getContext(), "Loop", func);
auto continueBB = llvm::BasicBlock::Create(m_builder.getContext(), "Continue", func);
auto returnBB = llvm::BasicBlock::Create(m_builder.getContext(), "Return", func);
llvm::Function* Arith256::getURem512Func(llvm::Module& _module)
{
static const auto funcName = "evm.urem.i512";
if (auto func = _module.getFunction(funcName))
return func;
return createURemFunc(llvm::IntegerType::get(_module.getContext(), 512), _module, funcName);
}
m_builder.SetInsertPoint(entryBB);
auto yNonZero = m_builder.CreateICmpNE(yArg, zero);
auto yLEx = m_builder.CreateICmpULE(yArg, x);
auto r0 = m_builder.CreateSelect(yNonZero, x, zero, "r0");
m_builder.CreateCondBr(m_builder.CreateAnd(yLEx, yNonZero), mainBB, returnBB);
m_builder.SetInsertPoint(mainBB);
auto ctlzIntr = llvm::Intrinsic::getDeclaration(getModule(), llvm::Intrinsic::ctlz, _type);
// both y and r are non-zero
auto yLz = m_builder.CreateCall(ctlzIntr, {yArg, m_builder.getInt1(true)}, "y.lz");
auto rLz = m_builder.CreateCall(ctlzIntr, {r0, m_builder.getInt1(true)}, "r.lz");
auto i0 = m_builder.CreateNUWSub(yLz, rLz, "i0");
auto y0 = m_builder.CreateShl(yArg, i0);
m_builder.CreateBr(loopBB);
m_builder.SetInsertPoint(loopBB);
auto yPhi = m_builder.CreatePHI(_type, 2, "y.phi");
auto rPhi = m_builder.CreatePHI(_type, 2, "r.phi");
auto iPhi = m_builder.CreatePHI(_type, 2, "i.phi");
auto qPhi = m_builder.CreatePHI(_type, 2, "q.phi");
auto rUpdate = m_builder.CreateNUWSub(rPhi, yPhi);
auto qUpdate = m_builder.CreateOr(qPhi, one); // q += 1, q lowest bit is 0
auto rGEy = m_builder.CreateICmpUGE(rPhi, yPhi);
auto r1 = m_builder.CreateSelect(rGEy, rUpdate, rPhi, "r1");
auto q1 = m_builder.CreateSelect(rGEy, qUpdate, qPhi, "q");
auto iZero = m_builder.CreateICmpEQ(iPhi, zero);
m_builder.CreateCondBr(iZero, returnBB, continueBB);
llvm::Function* Arith256::getSDivRem256Func(llvm::Module& _module)
{
static const auto funcName = "evm.sdivrem.i256";
if (auto func = _module.getFunction(funcName))
return func;
m_builder.SetInsertPoint(continueBB);
auto i2 = m_builder.CreateNUWSub(iPhi, one);
auto q2 = m_builder.CreateShl(q1, one);
auto y2 = m_builder.CreateLShr(yPhi, one);
m_builder.CreateBr(loopBB);
yPhi->addIncoming(y0, mainBB);
yPhi->addIncoming(y2, continueBB);
rPhi->addIncoming(r0, mainBB);
rPhi->addIncoming(r1, continueBB);
iPhi->addIncoming(i0, mainBB);
iPhi->addIncoming(i2, continueBB);
qPhi->addIncoming(zero, mainBB);
qPhi->addIncoming(q2, continueBB);
auto udivremFunc = getUDivRem256Func(_module);
auto retType = llvm::VectorType::get(Type::Word, 2);
auto func = llvm::Function::Create(llvm::FunctionType::get(retType, {Type::Word, Type::Word}, false), llvm::Function::PrivateLinkage, funcName, &_module);
func->setDoesNotThrow();
func->setDoesNotAccessMemory();
auto x = &func->getArgumentList().front();
x->setName("x");
auto y = x->getNextNode();
y->setName("y");
auto bb = llvm::BasicBlock::Create(_module.getContext(), "", func);
auto builder = llvm::IRBuilder<>{bb};
auto xIsNeg = builder.CreateICmpSLT(x, Constant::get(0));
auto xNeg = builder.CreateSub(Constant::get(0), x);
auto xAbs = builder.CreateSelect(xIsNeg, xNeg, x);
auto yIsNeg = builder.CreateICmpSLT(y, Constant::get(0));
auto yNeg = builder.CreateSub(Constant::get(0), y);
auto yAbs = builder.CreateSelect(yIsNeg, yNeg, y);
auto res = builder.CreateCall(udivremFunc, {xAbs, yAbs});
auto qAbs = builder.CreateExtractElement(res, uint64_t(0));
auto rAbs = builder.CreateExtractElement(res, 1);
// the remainder has the same sign as dividend
auto rNeg = builder.CreateSub(Constant::get(0), rAbs);
auto r = builder.CreateSelect(xIsNeg, rNeg, rAbs);
auto qNeg = builder.CreateSub(Constant::get(0), qAbs);
auto xyOpposite = builder.CreateXor(xIsNeg, yIsNeg);
auto q = builder.CreateSelect(xyOpposite, qNeg, qAbs);
auto ret = builder.CreateInsertElement(llvm::UndefValue::get(retType), q, uint64_t(0));
ret = builder.CreateInsertElement(ret, r, 1);
builder.CreateRet(ret);
return func;
}
llvm::Function* Arith256::getSDiv256Func(llvm::Module& _module)
{
static const auto funcName = "evm.sdiv.i256";
if (auto func = _module.getFunction(funcName))
return func;
auto sdivremFunc = getSDivRem256Func(_module);
auto func = llvm::Function::Create(llvm::FunctionType::get(Type::Word, {Type::Word, Type::Word}, false), llvm::Function::PrivateLinkage, funcName, &_module);
func->setDoesNotThrow();
func->setDoesNotAccessMemory();
auto x = &func->getArgumentList().front();
x->setName("x");
auto y = x->getNextNode();
y->setName("y");
auto bb = llvm::BasicBlock::Create(_module.getContext(), {}, func);
auto builder = llvm::IRBuilder<>{bb};
auto sdivrem = builder.CreateCall(sdivremFunc, {x, y});
auto q = builder.CreateExtractElement(sdivrem, uint64_t(0));
builder.CreateRet(q);
return func;
}
llvm::Function* Arith256::getSRem256Func(llvm::Module& _module)
{
static const auto funcName = "evm.srem.i256";
if (auto func = _module.getFunction(funcName))
return func;
auto sdivremFunc = getSDivRem256Func(_module);
auto func = llvm::Function::Create(llvm::FunctionType::get(Type::Word, {Type::Word, Type::Word}, false), llvm::Function::PrivateLinkage, funcName, &_module);
func->setDoesNotThrow();
func->setDoesNotAccessMemory();
auto x = &func->getArgumentList().front();
x->setName("x");
auto y = x->getNextNode();
y->setName("y");
auto bb = llvm::BasicBlock::Create(_module.getContext(), {}, func);
auto builder = llvm::IRBuilder<>{bb};
auto sdivrem = builder.CreateCall(sdivremFunc, {x, y});
auto r = builder.CreateExtractElement(sdivrem, uint64_t(1));
builder.CreateRet(r);
m_builder.SetInsertPoint(returnBB);
auto qRet = m_builder.CreatePHI(_type, 2, "q.ret");
qRet->addIncoming(zero, entryBB);
qRet->addIncoming(q1, loopBB);
auto rRet = m_builder.CreatePHI(_type, 2, "r.ret");
rRet->addIncoming(r0, entryBB);
rRet->addIncoming(r1, loopBB);
auto ret = m_builder.CreateInsertValue(llvm::UndefValue::get(retType), qRet, 0, "ret0");
ret = m_builder.CreateInsertValue(ret, rRet, 1, "ret");
m_builder.CreateRet(ret);
}
return func;
}
@ -260,14 +439,15 @@ llvm::Function* Arith256::getExpFunc()
m_builder.CreateCondBr(eOdd, updateBB, continueBB);
m_builder.SetInsertPoint(updateBB);
auto r0 = createCall(getMulFunc(), {r, b});
auto mul256Func = getMulFunc(*getModule());
auto r0 = createCall(mul256Func, {r, b});
m_builder.CreateBr(continueBB);
m_builder.SetInsertPoint(continueBB);
auto r1 = m_builder.CreatePHI(Type::Word, 2, "r1");
r1->addIncoming(r, bodyBB);
r1->addIncoming(r0, updateBB);
auto b1 = createCall(getMulFunc(), {b, b});
auto b1 = createCall(mul256Func, {b, b});
auto e1 = m_builder.CreateLShr(e, Constant::get(1), "e1");
m_builder.CreateBr(headerBB);
@ -284,137 +464,6 @@ llvm::Function* Arith256::getExpFunc()
return m_exp;
}
llvm::Function* Arith256::getAddModFunc()
{
if (!m_addmod)
{
auto i512Ty = m_builder.getIntNTy(512);
llvm::Type* argTypes[] = {Type::Word, Type::Word, Type::Word};
m_addmod = llvm::Function::Create(llvm::FunctionType::get(Type::Word, argTypes, false), llvm::Function::PrivateLinkage, "addmod", getModule());
m_addmod->setDoesNotThrow();
m_addmod->setDoesNotAccessMemory();
auto x = &m_addmod->getArgumentList().front();
x->setName("x");
auto y = x->getNextNode();
y->setName("y");
auto mod = y->getNextNode();
mod->setName("m");
InsertPointGuard guard{m_builder};
auto entryBB = llvm::BasicBlock::Create(m_builder.getContext(), {}, m_addmod);
m_builder.SetInsertPoint(entryBB);
auto x512 = m_builder.CreateZExt(x, i512Ty, "x512");
auto y512 = m_builder.CreateZExt(y, i512Ty, "y512");
auto m512 = m_builder.CreateZExt(mod, i512Ty, "m512");
auto s = m_builder.CreateAdd(x512, y512, "s");
auto d = createCall(getDivFunc(i512Ty), {s, m512});
auto r = m_builder.CreateExtractValue(d, 1, "r");
m_builder.CreateRet(m_builder.CreateTrunc(r, Type::Word));
}
return m_addmod;
}
llvm::Function* Arith256::getMulModFunc()
{
if (!m_mulmod)
{
llvm::Type* argTypes[] = {Type::Word, Type::Word, Type::Word};
m_mulmod = llvm::Function::Create(llvm::FunctionType::get(Type::Word, argTypes, false), llvm::Function::PrivateLinkage, "mulmod", getModule());
m_mulmod->setDoesNotThrow();
m_mulmod->setDoesNotAccessMemory();
auto i512Ty = m_builder.getIntNTy(512);
auto x = &m_mulmod->getArgumentList().front();
x->setName("x");
auto y = x->getNextNode();
y->setName("y");
auto mod = y->getNextNode();
mod->setName("mod");
InsertPointGuard guard{m_builder};
auto entryBB = llvm::BasicBlock::Create(m_builder.getContext(), {}, m_mulmod);
m_builder.SetInsertPoint(entryBB);
auto p = createCall(getMul512Func(), {x, y});
auto m = m_builder.CreateZExt(mod, i512Ty, "m");
auto d = createCall(getDivFunc(i512Ty), {p, m});
auto r = m_builder.CreateExtractValue(d, 1, "r");
r = m_builder.CreateTrunc(r, Type::Word);
m_builder.CreateRet(r);
}
return m_mulmod;
}
llvm::Value* Arith256::mul(llvm::Value* _arg1, llvm::Value* _arg2)
{
if (auto c1 = llvm::dyn_cast<llvm::ConstantInt>(_arg1))
{
if (auto c2 = llvm::dyn_cast<llvm::ConstantInt>(_arg2))
return Constant::get(c1->getValue() * c2->getValue());
}
return createCall(getMulFunc(), {_arg1, _arg2});
}
std::pair<llvm::Value*, llvm::Value*> Arith256::div(llvm::Value* _arg1, llvm::Value* _arg2)
{
// FIXME: Disabled because of llvm::APInt::urem bug
// if (auto c1 = llvm::dyn_cast<llvm::ConstantInt>(_arg1))
// {
// if (auto c2 = llvm::dyn_cast<llvm::ConstantInt>(_arg2))
// {
// if (!c2->getValue())
// return std::make_pair(Constant::get(0), Constant::get(0));
// auto div = Constant::get(c1->getValue().udiv(c2->getValue()));
// auto mod = Constant::get(c1->getValue().urem(c2->getValue()));
// return std::make_pair(div, mod);
// }
// }
auto r = createCall(getDivFunc(Type::Word), {_arg1, _arg2});
auto div = m_builder.CreateExtractValue(r, 0, "div");
auto mod = m_builder.CreateExtractValue(r, 1, "mod");
return std::make_pair(div, mod);
}
std::pair<llvm::Value*, llvm::Value*> Arith256::sdiv(llvm::Value* _x, llvm::Value* _y)
{
// FIXME: Disabled because of llvm::APInt::urem bug
// if (auto c1 = llvm::dyn_cast<llvm::ConstantInt>(_x))
// {
// if (auto c2 = llvm::dyn_cast<llvm::ConstantInt>(_y))
// {
// if (!c2->getValue())
// return std::make_pair(Constant::get(0), Constant::get(0));
// auto div = Constant::get(c1->getValue().sdiv(c2->getValue()));
// auto mod = Constant::get(c1->getValue().srem(c2->getValue()));
// return std::make_pair(div, mod);
// }
// }
auto xIsNeg = m_builder.CreateICmpSLT(_x, Constant::get(0));
auto xNeg = m_builder.CreateSub(Constant::get(0), _x);
auto xAbs = m_builder.CreateSelect(xIsNeg, xNeg, _x);
auto yIsNeg = m_builder.CreateICmpSLT(_y, Constant::get(0));
auto yNeg = m_builder.CreateSub(Constant::get(0), _y);
auto yAbs = m_builder.CreateSelect(yIsNeg, yNeg, _y);
auto res = div(xAbs, yAbs);
// the reminder has the same sign as dividend
auto rNeg = m_builder.CreateSub(Constant::get(0), res.second);
res.second = m_builder.CreateSelect(xIsNeg, rNeg, res.second);
auto qNeg = m_builder.CreateSub(Constant::get(0), res.first);
auto xyOpposite = m_builder.CreateXor(xIsNeg, yIsNeg);
res.first = m_builder.CreateSelect(xyOpposite, qNeg, res.first);
return res;
}
llvm::Value* Arith256::exp(llvm::Value* _arg1, llvm::Value* _arg2)
{
// while (e != 0) {
@ -445,49 +494,6 @@ llvm::Value* Arith256::exp(llvm::Value* _arg1, llvm::Value* _arg2)
return createCall(getExpFunc(), {_arg1, _arg2});
}
llvm::Value* Arith256::addmod(llvm::Value* _arg1, llvm::Value* _arg2, llvm::Value* _arg3)
{
// FIXME: Disabled because of llvm::APInt::urem bug
// if (auto c1 = llvm::dyn_cast<llvm::ConstantInt>(_arg1))
// {
// if (auto c2 = llvm::dyn_cast<llvm::ConstantInt>(_arg2))
// {
// if (auto c3 = llvm::dyn_cast<llvm::ConstantInt>(_arg3))
// {
// if (!c3->getValue())
// return Constant::get(0);
// auto s = c1->getValue().zext(256+64) + c2->getValue().zext(256+64);
// auto r = s.urem(c3->getValue().zext(256+64)).trunc(256);
// return Constant::get(r);
// }
// }
// }
return createCall(getAddModFunc(), {_arg1, _arg2, _arg3});
}
llvm::Value* Arith256::mulmod(llvm::Value* _arg1, llvm::Value* _arg2, llvm::Value* _arg3)
{
// FIXME: Disabled because of llvm::APInt::urem bug
// if (auto c1 = llvm::dyn_cast<llvm::ConstantInt>(_arg1))
// {
// if (auto c2 = llvm::dyn_cast<llvm::ConstantInt>(_arg2))
// {
// if (auto c3 = llvm::dyn_cast<llvm::ConstantInt>(_arg3))
// {
// if (!c3->getValue())
// return Constant::get(0);
// auto p = c1->getValue().zext(512) * c2->getValue().zext(512);
// auto r = p.urem(c3->getValue().zext(512)).trunc(256);
// return Constant::get(r);
// }
// }
// }
return createCall(getMulModFunc(), {_arg1, _arg2, _arg3});
}
}
}
}

27
evmjit/libevmjit/Arith256.h

@ -14,30 +14,25 @@ class Arith256 : public CompilerHelper
public:
Arith256(llvm::IRBuilder<>& _builder);
llvm::Value* mul(llvm::Value* _arg1, llvm::Value* _arg2);
std::pair<llvm::Value*, llvm::Value*> div(llvm::Value* _arg1, llvm::Value* _arg2);
std::pair<llvm::Value*, llvm::Value*> sdiv(llvm::Value* _arg1, llvm::Value* _arg2);
llvm::Value* exp(llvm::Value* _arg1, llvm::Value* _arg2);
llvm::Value* mulmod(llvm::Value* _arg1, llvm::Value* _arg2, llvm::Value* _arg3);
llvm::Value* addmod(llvm::Value* _arg1, llvm::Value* _arg2, llvm::Value* _arg3);
void debug(llvm::Value* _value, char _c);
static llvm::Function* getMulFunc(llvm::Module& _module);
static llvm::Function* getMul512Func(llvm::Module& _module);
static llvm::Function* getUDiv256Func(llvm::Module& _module);
static llvm::Function* getURem256Func(llvm::Module& _module);
static llvm::Function* getURem512Func(llvm::Module& _module);
static llvm::Function* getUDivRem256Func(llvm::Module& _module);
static llvm::Function* getSDiv256Func(llvm::Module& _module);
static llvm::Function* getSRem256Func(llvm::Module& _module);
static llvm::Function* getSDivRem256Func(llvm::Module& _module);
static llvm::Function* getUDivRem512Func(llvm::Module& _module);
private:
llvm::Function* getMulFunc();
llvm::Function* getMul512Func();
llvm::Function* getDivFunc(llvm::Type* _type);
llvm::Function* getExpFunc();
llvm::Function* getAddModFunc();
llvm::Function* getMulModFunc();
llvm::Function* m_mul = nullptr;
llvm::Function* m_mul512 = nullptr;
llvm::Function* m_div = nullptr;
llvm::Function* m_div512 = nullptr;
llvm::Function* m_exp = nullptr;
llvm::Function* m_addmod = nullptr;
llvm::Function* m_mulmod = nullptr;
llvm::Function* m_debug = nullptr;
};

3
evmjit/libevmjit/Array.cpp

@ -6,7 +6,6 @@
#include "preprocessor/llvm_includes_end.h"
#include "RuntimeManager.h"
#include "Runtime.h"
#include "Utils.h"
namespace dev
@ -17,7 +16,6 @@ namespace jit
{
static const auto c_reallocStep = 1;
static const auto c_reallocMultipier = 2;
llvm::Value* LazyFunction::call(llvm::IRBuilder<>& _builder, std::initializer_list<llvm::Value*> const& _args, llvm::Twine const& _name)
{
@ -56,7 +54,6 @@ llvm::Function* Array::createArrayPushFunc()
m_builder.SetInsertPoint(reallocBB);
auto newCap = m_builder.CreateNUWAdd(cap, m_builder.getInt64(c_reallocStep), "newCap");
//newCap = m_builder.CreateNUWMul(newCap, m_builder.getInt64(c_reallocMultipier));
auto reallocSize = m_builder.CreateShl(newCap, 5, "reallocSize"); // size in bytes: newCap * 32
auto bytes = m_builder.CreateBitCast(data, Type::BytePtr, "bytes");
auto newBytes = m_reallocFunc.call(m_builder, {bytes, reallocSize}, "newBytes");

2
evmjit/libevmjit/BasicBlock.h

@ -11,7 +11,7 @@ namespace eth
{
namespace jit
{
using namespace evmjit;
using instr_idx = uint64_t;
class BasicBlock

8
evmjit/libevmjit/CMakeLists.txt

@ -1,26 +1,22 @@
set(TARGET_NAME evmjit)
set(SOURCES
JIT.cpp ${EVMJIT_INCLUDE_DIR}/evmjit/JIT.h
Arith256.cpp Arith256.h
Array.cpp Array.h
BasicBlock.cpp BasicBlock.h
Cache.cpp Cache.h
Common.h
Common.h
Compiler.cpp Compiler.h
CompilerHelper.cpp CompilerHelper.h
${EVMJIT_INCLUDE_DIR}/evmjit/DataTypes.h
Endianness.cpp Endianness.h
ExecStats.cpp ExecStats.h
ExecutionEngine.cpp ExecutionEngine.h
Ext.cpp Ext.h
GasMeter.cpp GasMeter.h
Instruction.cpp Instruction.h
interface.cpp interface.h
JIT.cpp ${EVMJIT_INCLUDE_DIR}/evmjit/JIT.h
Memory.cpp Memory.h
Optimizer.cpp Optimizer.h
Runtime.cpp Runtime.h
RuntimeData.h
RuntimeManager.cpp RuntimeManager.h
Stack.cpp Stack.h
Type.cpp Type.h

27
evmjit/libevmjit/Cache.cpp

@ -12,15 +12,13 @@
#include <llvm/Support/raw_os_ostream.h>
#include "preprocessor/llvm_includes_end.h"
#include "ExecutionEngine.h"
#include "ExecStats.h"
#include "Utils.h"
#include "BuildInfo.gen.h"
namespace dev
{
namespace eth
{
namespace jit
namespace evmjit
{
namespace
@ -29,7 +27,7 @@ namespace
std::mutex x_cacheMutex;
CacheMode g_mode;
std::unique_ptr<llvm::MemoryBuffer> g_lastObject;
ExecutionEngineListener* g_listener;
JITListener* g_listener;
static const size_t c_versionStampLength = 32;
llvm::StringRef getLibVersionStamp()
@ -44,15 +42,25 @@ namespace
}
}
ObjectCache* Cache::getObjectCache(CacheMode _mode, ExecutionEngineListener* _listener)
ObjectCache* Cache::init(CacheMode _mode, JITListener* _listener)
{
static ObjectCache objectCache;
Guard g{x_cacheMutex};
g_mode = _mode;
g_listener = _listener;
return &objectCache;
if (g_mode == CacheMode::clear)
{
Cache::clear();
g_mode = CacheMode::off;
}
if (g_mode != CacheMode::off)
{
static ObjectCache objectCache;
return &objectCache;
}
return nullptr;
}
void Cache::clear()
@ -185,4 +193,3 @@ std::unique_ptr<llvm::MemoryBuffer> ObjectCache::getObject(llvm::Module const* _
}
}
}

9
evmjit/libevmjit/Cache.h

@ -14,11 +14,9 @@ namespace llvm
namespace dev
{
namespace eth
namespace evmjit
{
namespace jit
{
class ExecutionEngineListener;
class JITListener;
enum class CacheMode
{
@ -47,7 +45,7 @@ public:
class Cache
{
public:
static ObjectCache* getObjectCache(CacheMode _mode, ExecutionEngineListener* _listener);
static ObjectCache* init(CacheMode _mode, JITListener* _listener);
static std::unique_ptr<llvm::Module> getObject(std::string const& id);
/// Clears cache storage
@ -59,4 +57,3 @@ public:
}
}
}

39
evmjit/libevmjit/Common.h

@ -1,53 +1,16 @@
#pragma once
#include <tuple>
#include <cstdint>
#ifdef _MSC_VER
#define EXPORT __declspec(dllexport)
#define _ALLOW_KEYWORD_MACROS
#define noexcept throw()
#else
#define EXPORT
#endif
namespace dev
{
namespace eth
{
namespace jit
namespace evmjit
{
using byte = uint8_t;
using bytes_ref = std::tuple<byte const*, size_t>;
using code_iterator = byte const*;
enum class ReturnCode
{
// Success codes
Stop = 0,
Return = 1,
Suicide = 2,
// Standard error codes
OutOfGas = -1,
StackUnderflow = -2,
BadJumpDestination = -3,
BadInstruction = -4,
Rejected = -5, ///< Input data (code, gas, block info, etc.) does not meet JIT requirement and execution request has been rejected
// Internal error codes
LLVMConfigError = -101,
LLVMCompileError = -102,
LLVMLinkError = -103,
UnexpectedException = -111,
LinkerWorkaround = -299,
};
#define UNTESTED assert(false)
}
}
}

152
evmjit/libevmjit/Compiler.cpp

@ -49,6 +49,11 @@ void Compiler::createBasicBlocks(code_iterator _codeBegin, code_iterator _codeEn
return _curr + offset;
};
// Skip all STOPs in the end
for (; _codeEnd != _codeBegin; --_codeEnd)
if (*(_codeEnd - 1) != static_cast<byte>(Instruction::STOP))
break;
auto begin = _codeBegin; // begin of current block
bool nextJumpDest = false;
for (auto curr = begin, next = begin; curr != _codeEnd; curr = next)
@ -159,10 +164,10 @@ std::unique_ptr<llvm::Module> Compiler::compile(code_iterator _begin, code_itera
// TODO: Create Stop basic block on demand
m_stopBB = llvm::BasicBlock::Create(m_mainFunc->getContext(), "Stop", m_mainFunc);
auto abortBB = llvm::BasicBlock::Create(m_mainFunc->getContext(), "Abort", m_mainFunc);
m_abortBB = llvm::BasicBlock::Create(m_mainFunc->getContext(), "Abort", m_mainFunc);
auto firstBB = m_basicBlocks.empty() ? m_stopBB : m_basicBlocks.begin()->second.llvm();
m_builder.CreateCondBr(normalFlow, firstBB, abortBB, Type::expectTrue);
m_builder.CreateCondBr(normalFlow, firstBB, m_abortBB, Type::expectTrue);
for (auto basicBlockPairIt = m_basicBlocks.begin(); basicBlockPairIt != m_basicBlocks.end(); ++basicBlockPairIt)
{
@ -178,7 +183,7 @@ std::unique_ptr<llvm::Module> Compiler::compile(code_iterator _begin, code_itera
m_builder.SetInsertPoint(m_stopBB);
runtimeManager.exit(ReturnCode::Stop);
m_builder.SetInsertPoint(abortBB);
m_builder.SetInsertPoint(m_abortBB);
runtimeManager.exit(ReturnCode::OutOfGas);
removeDeadBlocks();
@ -270,44 +275,96 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, RuntimeManager& _runti
{
auto lhs = stack.pop();
auto rhs = stack.pop();
auto res = _arith.mul(lhs, rhs);
auto res = m_builder.CreateMul(lhs, rhs);
stack.push(res);
break;
}
case Instruction::DIV:
{
auto lhs = stack.pop();
auto rhs = stack.pop();
auto res = _arith.div(lhs, rhs);
stack.push(res.first);
auto d = stack.pop();
auto n = stack.pop();
auto divByZero = m_builder.CreateICmpEQ(n, Constant::get(0));
n = m_builder.CreateSelect(divByZero, Constant::get(1), n); // protect against hardware signal
auto r = m_builder.CreateUDiv(d, n);
r = m_builder.CreateSelect(divByZero, Constant::get(0), r);
stack.push(r);
break;
}
case Instruction::SDIV:
{
auto lhs = stack.pop();
auto rhs = stack.pop();
auto res = _arith.sdiv(lhs, rhs);
stack.push(res.first);
auto d = stack.pop();
auto n = stack.pop();
auto divByZero = m_builder.CreateICmpEQ(n, Constant::get(0));
auto divByMinusOne = m_builder.CreateICmpEQ(n, Constant::get(-1));
n = m_builder.CreateSelect(divByZero, Constant::get(1), n); // protect against hardware signal
auto r = m_builder.CreateSDiv(d, n);
r = m_builder.CreateSelect(divByZero, Constant::get(0), r);
auto dNeg = m_builder.CreateSub(Constant::get(0), d);
r = m_builder.CreateSelect(divByMinusOne, dNeg, r); // protect against undef i256.min / -1
stack.push(r);
break;
}
case Instruction::MOD:
{
auto lhs = stack.pop();
auto rhs = stack.pop();
auto res = _arith.div(lhs, rhs);
stack.push(res.second);
auto d = stack.pop();
auto n = stack.pop();
auto divByZero = m_builder.CreateICmpEQ(n, Constant::get(0));
n = m_builder.CreateSelect(divByZero, Constant::get(1), n); // protect against hardware signal
auto r = m_builder.CreateURem(d, n);
r = m_builder.CreateSelect(divByZero, Constant::get(0), r);
stack.push(r);
break;
}
case Instruction::SMOD:
{
auto lhs = stack.pop();
auto rhs = stack.pop();
auto res = _arith.sdiv(lhs, rhs);
stack.push(res.second);
auto d = stack.pop();
auto n = stack.pop();
auto divByZero = m_builder.CreateICmpEQ(n, Constant::get(0));
auto divByMinusOne = m_builder.CreateICmpEQ(n, Constant::get(-1));
n = m_builder.CreateSelect(divByZero, Constant::get(1), n); // protect against hardware signal
auto r = m_builder.CreateSRem(d, n);
r = m_builder.CreateSelect(divByZero, Constant::get(0), r);
r = m_builder.CreateSelect(divByMinusOne, Constant::get(0), r); // protect against undef i256.min / -1
stack.push(r);
break;
}
case Instruction::ADDMOD:
{
auto i512Ty = m_builder.getIntNTy(512);
auto a = stack.pop();
auto b = stack.pop();
auto m = stack.pop();
auto divByZero = m_builder.CreateICmpEQ(m, Constant::get(0));
a = m_builder.CreateZExt(a, i512Ty);
b = m_builder.CreateZExt(b, i512Ty);
m = m_builder.CreateZExt(m, i512Ty);
auto s = m_builder.CreateNUWAdd(a, b);
s = m_builder.CreateURem(s, m);
s = m_builder.CreateTrunc(s, Type::Word);
s = m_builder.CreateSelect(divByZero, Constant::get(0), s);
stack.push(s);
break;
}
case Instruction::MULMOD:
{
auto i512Ty = m_builder.getIntNTy(512);
auto a = stack.pop();
auto b = stack.pop();
auto m = stack.pop();
auto divByZero = m_builder.CreateICmpEQ(m, Constant::get(0));
m = m_builder.CreateZExt(m, i512Ty);
// TODO: Add support for i256 x i256 -> i512 in LowerEVM pass
llvm::Value* p = m_builder.CreateCall(Arith256::getMul512Func(*_basicBlock.llvm()->getParent()->getParent()), {a, b});
p = m_builder.CreateURem(p, m);
p = m_builder.CreateTrunc(p, Type::Word);
p = m_builder.CreateSelect(divByZero, Constant::get(0), p);
stack.push(p);
break;
}
@ -417,48 +474,29 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, RuntimeManager& _runti
case Instruction::BYTE:
{
const auto byteNum = stack.pop();
auto value = stack.pop();
const auto idx = stack.pop();
auto value = Endianness::toBE(m_builder, stack.pop());
value = Endianness::toBE(m_builder, value);
auto idxValid = m_builder.CreateICmpULT(idx, Constant::get(32), "idxValid");
auto bytes = m_builder.CreateBitCast(value, llvm::VectorType::get(Type::Byte, 32), "bytes");
auto safeByteNum = m_builder.CreateZExt(m_builder.CreateTrunc(byteNum, m_builder.getIntNTy(5)), Type::lowPrecision); // Trim index, large values can crash
auto byte = m_builder.CreateExtractElement(bytes, safeByteNum, "byte");
// TODO: Workaround for LLVM bug. Using big value of index causes invalid memory access.
auto safeIdx = m_builder.CreateTrunc(idx, m_builder.getIntNTy(5));
// TODO: Workaround for LLVM bug. DAG Builder used sext on index instead of zext
safeIdx = m_builder.CreateZExt(safeIdx, Type::Size);
auto byte = m_builder.CreateExtractElement(bytes, safeIdx, "byte");
value = m_builder.CreateZExt(byte, Type::Word);
auto byteNumValid = m_builder.CreateICmpULT(byteNum, Constant::get(32));
value = m_builder.CreateSelect(byteNumValid, value, Constant::get(0));
value = m_builder.CreateSelect(idxValid, value, Constant::get(0));
stack.push(value);
break;
}
case Instruction::ADDMOD:
{
auto lhs = stack.pop();
auto rhs = stack.pop();
auto mod = stack.pop();
auto res = _arith.addmod(lhs, rhs, mod);
stack.push(res);
break;
}
case Instruction::MULMOD:
{
auto lhs = stack.pop();
auto rhs = stack.pop();
auto mod = stack.pop();
auto res = _arith.mulmod(lhs, rhs, mod);
stack.push(res);
break;
}
case Instruction::SIGNEXTEND:
{
auto idx = stack.pop();
auto word = stack.pop();
auto k32_ = m_builder.CreateTrunc(idx, m_builder.getIntNTy(5), "k_32");
auto k32 = m_builder.CreateZExt(k32_, Type::lowPrecision);
auto k32 = m_builder.CreateZExt(k32_, Type::Size);
auto k32x8 = m_builder.CreateMul(k32, m_builder.getInt64(8), "kx8");
// test for word >> (k * 8 + 7)
@ -495,11 +533,7 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, RuntimeManager& _runti
case Instruction::POP:
{
auto val = stack.pop();
static_cast<void>(val);
// Generate a dummy use of val to make sure that a get(0) will be emitted at this point,
// so that StackUnderflow will be thrown
// m_builder.CreateICmpEQ(val, val, "dummy");
stack.pop();
break;
}
@ -656,7 +690,6 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, RuntimeManager& _runti
}
case Instruction::CODESIZE:
// TODO: Use constant
stack.push(_runtimeManager.getCodeSize());
break;
@ -729,8 +762,8 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, RuntimeManager& _runti
case Instruction::CALLDATALOAD:
{
auto index = stack.pop();
auto value = _ext.calldataload(index);
auto idx = stack.pop();
auto value = _ext.calldataload(idx);
stack.push(value);
break;
}
@ -797,7 +830,7 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, RuntimeManager& _runti
case Instruction::STOP:
{
m_builder.CreateRet(Constant::get(ReturnCode::Stop));
m_builder.CreateBr(m_stopBB);
break;
}
@ -824,7 +857,7 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, RuntimeManager& _runti
}
default: // Invalid instruction - abort
m_builder.CreateRet(Constant::get(ReturnCode::BadInstruction));
m_builder.CreateBr(m_abortBB);
it = _basicBlock.end() - 1; // finish block compilation
}
}
@ -942,4 +975,3 @@ void Compiler::dump()
}
}
}

4
evmjit/libevmjit/Compiler.h

@ -1,6 +1,5 @@
#pragma once
#include "Common.h"
#include "BasicBlock.h"
namespace dev
@ -65,6 +64,9 @@ private:
/// Stop basic block - terminates execution with STOP code (0)
llvm::BasicBlock* m_stopBB = nullptr;
/// Abort basic block - terminates execution with OOG-like state
llvm::BasicBlock* m_abortBB = nullptr;
/// Block with a jump table.
std::unique_ptr<BasicBlock> m_jumpTableBlock;

25
evmjit/libevmjit/CompilerHelper.h

@ -37,7 +37,6 @@ protected:
friend class RuntimeHelper;
};
/// Compiler helper that depends on runtime data
class RuntimeHelper : public CompilerHelper
{
@ -50,29 +49,7 @@ private:
RuntimeManager& m_runtimeManager;
};
/// Saves the insert point of the IR builder and restores it when destructed
struct InsertPointGuard
{
InsertPointGuard(llvm::IRBuilder<>& _builder) :
m_builder(_builder),
m_insertBB(m_builder.GetInsertBlock()),
m_insertPt(m_builder.GetInsertPoint())
{}
InsertPointGuard(const InsertPointGuard&) = delete;
void operator=(InsertPointGuard) = delete;
~InsertPointGuard()
{
m_builder.SetInsertPoint(m_insertBB, m_insertPt);
}
private:
llvm::IRBuilder<>& m_builder;
llvm::BasicBlock* m_insertBB;
llvm::BasicBlock::iterator m_insertPt;
};
using InsertPointGuard = llvm::IRBuilderBase::InsertPointGuard;
}
}

5
evmjit/libevmjit/ExecStats.cpp

@ -8,9 +8,7 @@
namespace dev
{
namespace eth
{
namespace jit
namespace evmjit
{
void ExecStats::stateChanged(ExecState _state)
@ -95,4 +93,3 @@ StatsCollector::~StatsCollector()
}
}
}

35
evmjit/libevmjit/ExecStats.h

@ -1,19 +1,43 @@
#pragma once
#include <memory>
#include <vector>
#include <string>
#include <chrono>
#include "ExecutionEngine.h"
namespace dev
{
namespace eth
namespace evmjit
{
enum class ExecState
{
namespace jit
Started,
CacheLoad,
CacheWrite,
Compilation,
Optimization,
CodeGen,
Execution,
Return,
Finished
};
class JITListener
{
public:
JITListener() = default;
JITListener(JITListener const&) = delete;
JITListener& operator=(JITListener) = delete;
virtual ~JITListener() {}
virtual void executionStarted() {}
virtual void executionEnded() {}
class ExecStats : public ExecutionEngineListener
virtual void stateChanged(ExecState) {}
};
class ExecStats : public JITListener
{
public:
using clock = std::chrono::high_resolution_clock;
@ -42,4 +66,3 @@ public:
}
}
}

192
evmjit/libevmjit/ExecutionEngine.cpp

@ -1,192 +0,0 @@
#include "ExecutionEngine.h"
#include <array>
#include <mutex>
#include <iostream>
#include <unordered_map>
#include <cstdlib>
#include <cstring>
#include "preprocessor/llvm_includes_start.h"
#include <llvm/IR/Module.h>
#include <llvm/ADT/Triple.h>
#include <llvm/ExecutionEngine/ExecutionEngine.h>
#include <llvm/ExecutionEngine/SectionMemoryManager.h>
#include <llvm/ExecutionEngine/MCJIT.h>
#include <llvm/Support/TargetSelect.h>
#include <llvm/Support/Host.h>
#include <llvm/Support/CommandLine.h>
#include <llvm/Support/ManagedStatic.h>
#include "preprocessor/llvm_includes_end.h"
#include "evmjit/JIT.h"
#include "Runtime.h"
#include "Compiler.h"
#include "Optimizer.h"
#include "Cache.h"
#include "ExecStats.h"
#include "Utils.h"
#include "BuildInfo.gen.h"
namespace dev
{
namespace eth
{
namespace jit
{
using evmjit::JIT;
namespace
{
using EntryFuncPtr = ReturnCode(*)(Runtime*);
std::string codeHash(i256 const& _hash)
{
static const auto size = sizeof(_hash);
static const auto hexChars = "0123456789abcdef";
std::string str;
str.resize(size * 2);
auto outIt = str.rbegin(); // reverse for BE
auto& arr = *(std::array<byte, size>*)&_hash;
for (auto b : arr)
{
*(outIt++) = hexChars[b & 0xf];
*(outIt++) = hexChars[b >> 4];
}
return str;
}
void printVersion()
{
std::cout << "Ethereum EVM JIT Compiler (http://github.com/ethereum/evmjit):\n"
<< " EVMJIT version " << EVMJIT_VERSION << "\n"
#ifdef NDEBUG
<< " Optimized build, " EVMJIT_VERSION_FULL "\n"
#else
<< " DEBUG build, " EVMJIT_VERSION_FULL "\n"
#endif
<< " Built " << __DATE__ << " (" << __TIME__ << ")\n"
<< std::endl;
}
namespace cl = llvm::cl;
cl::opt<bool> g_optimize{"O", cl::desc{"Optimize"}};
cl::opt<CacheMode> g_cache{"cache", cl::desc{"Cache compiled EVM code on disk"},
cl::values(
clEnumValN(CacheMode::on, "1", "Enabled"),
clEnumValN(CacheMode::off, "0", "Disabled"),
clEnumValN(CacheMode::read, "r", "Read only. No new objects are added to cache."),
clEnumValN(CacheMode::write, "w", "Write only. No objects are loaded from cache."),
clEnumValN(CacheMode::clear, "c", "Clear the cache storage. Cache is disabled."),
clEnumValN(CacheMode::preload, "p", "Preload all cached objects."),
clEnumValEnd)};
cl::opt<bool> g_stats{"st", cl::desc{"Statistics"}};
cl::opt<bool> g_dump{"dump", cl::desc{"Dump LLVM IR module"}};
void parseOptions()
{
static llvm::llvm_shutdown_obj shutdownObj{};
cl::AddExtraVersionPrinter(printVersion);
cl::ParseEnvironmentOptions("evmjit", "EVMJIT", "Ethereum EVM JIT Compiler");
}
}
ReturnCode ExecutionEngine::run(RuntimeData* _data, Env* _env)
{
static std::once_flag flag;
std::call_once(flag, parseOptions);
std::unique_ptr<ExecStats> listener{new ExecStats};
listener->stateChanged(ExecState::Started);
bool preloadCache = g_cache == CacheMode::preload;
if (preloadCache)
g_cache = CacheMode::on;
// TODO: Do not pseudo-init the cache every time
auto objectCache = (g_cache != CacheMode::off && g_cache != CacheMode::clear) ? Cache::getObjectCache(g_cache, listener.get()) : nullptr;
static std::unique_ptr<llvm::ExecutionEngine> ee;
if (!ee)
{
if (g_cache == CacheMode::clear)
Cache::clear();
llvm::InitializeNativeTarget();
llvm::InitializeNativeTargetAsmPrinter();
auto module = std::unique_ptr<llvm::Module>(new llvm::Module({}, llvm::getGlobalContext()));
// FIXME: LLVM 3.7: test on Windows
auto triple = llvm::Triple(llvm::sys::getProcessTriple());
if (triple.getOS() == llvm::Triple::OSType::Win32)
triple.setObjectFormat(llvm::Triple::ObjectFormatType::ELF); // MCJIT does not support COFF format
module->setTargetTriple(triple.str());
llvm::EngineBuilder builder(std::move(module));
builder.setEngineKind(llvm::EngineKind::JIT);
builder.setOptLevel(g_optimize ? llvm::CodeGenOpt::Default : llvm::CodeGenOpt::None);
ee.reset(builder.create());
if (!CHECK(ee))
return ReturnCode::LLVMConfigError;
ee->setObjectCache(objectCache);
// FIXME: Disabled during API changes
//if (preloadCache)
// Cache::preload(*ee, funcCache);
}
static StatsCollector statsCollector;
auto mainFuncName = codeHash(_data->codeHash);
m_runtime.init(_data, _env);
// TODO: Remove cast
auto entryFuncPtr = (EntryFuncPtr) JIT::getCode(_data->codeHash);
if (!entryFuncPtr)
{
auto module = objectCache ? Cache::getObject(mainFuncName) : nullptr;
if (!module)
{
listener->stateChanged(ExecState::Compilation);
assert(_data->code || !_data->codeSize); //TODO: Is it good idea to execute empty code?
module = Compiler{{}}.compile(_data->code, _data->code + _data->codeSize, mainFuncName);
if (g_optimize)
{
listener->stateChanged(ExecState::Optimization);
optimize(*module);
}
}
if (g_dump)
module->dump();
ee->addModule(std::move(module));
listener->stateChanged(ExecState::CodeGen);
entryFuncPtr = (EntryFuncPtr)ee->getFunctionAddress(mainFuncName);
if (!CHECK(entryFuncPtr))
return ReturnCode::LLVMLinkError;
JIT::mapCode(_data->codeHash, (uint64_t)entryFuncPtr); // FIXME: Remove cast
}
listener->stateChanged(ExecState::Execution);
auto returnCode = entryFuncPtr(&m_runtime);
listener->stateChanged(ExecState::Return);
if (returnCode == ReturnCode::Return)
returnData = m_runtime.getReturnData(); // Save reference to return data
listener->stateChanged(ExecState::Finished);
if (g_stats)
statsCollector.stats.push_back(std::move(listener));
return returnCode;
}
}
}
}

59
evmjit/libevmjit/ExecutionEngine.h

@ -1,59 +0,0 @@
#pragma once
#include <memory>
#include "Runtime.h"
namespace dev
{
namespace eth
{
namespace jit
{
enum class ExecState
{
Started,
CacheLoad,
CacheWrite,
Compilation,
Optimization,
CodeGen,
Execution,
Return,
Finished
};
class ExecutionEngineListener
{
public:
ExecutionEngineListener() = default;
ExecutionEngineListener(ExecutionEngineListener const&) = delete;
ExecutionEngineListener& operator=(ExecutionEngineListener) = delete;
virtual ~ExecutionEngineListener() {}
virtual void executionStarted() {}
virtual void executionEnded() {}
virtual void stateChanged(ExecState) {}
};
class ExecutionEngine
{
public:
ExecutionEngine() = default;
ExecutionEngine(ExecutionEngine const&) = delete;
ExecutionEngine& operator=(ExecutionEngine) = delete;
EXPORT ReturnCode run(RuntimeData* _data, Env* _env);
/// Reference to returned data (RETURN opcode used)
bytes_ref returnData;
private:
Runtime m_runtime;
};
}
}
}

24
evmjit/libevmjit/Ext.cpp

@ -45,7 +45,6 @@ std::array<FuncDesc, sizeOf<EnvFunc>::value> const& getEnvFuncDescs()
FuncDesc{"env_log", getFunctionType(Type::Void, {Type::EnvPtr, Type::BytePtr, Type::Size, Type::WordPtr, Type::WordPtr, Type::WordPtr, Type::WordPtr})},
FuncDesc{"env_blockhash", getFunctionType(Type::Void, {Type::EnvPtr, Type::WordPtr, Type::WordPtr})},
FuncDesc{"env_extcode", getFunctionType(Type::BytePtr, {Type::EnvPtr, Type::WordPtr, Type::Size->getPointerTo()})},
FuncDesc{"ext_calldataload", getFunctionType(Type::Void, {Type::RuntimeDataPtr, Type::WordPtr, Type::WordPtr})},
}};
return descs;
@ -101,12 +100,27 @@ void Ext::sstore(llvm::Value* _index, llvm::Value* _value)
createCall(EnvFunc::sstore, {getRuntimeManager().getEnvPtr(), byPtr(_index), byPtr(_value)}); // Uses native endianness
}
llvm::Value* Ext::calldataload(llvm::Value* _index)
llvm::Value* Ext::calldataload(llvm::Value* _idx)
{
auto ret = getArgAlloca();
createCall(EnvFunc::calldataload, {getRuntimeManager().getDataPtr(), byPtr(_index), ret});
ret = m_builder.CreateLoad(ret);
return Endianness::toNative(m_builder, ret);
auto result = m_builder.CreateBitCast(ret, Type::BytePtr);
auto callDataSize = getRuntimeManager().getCallDataSize();
auto callDataSize64 = m_builder.CreateTrunc(callDataSize, Type::Size);
auto idxValid = m_builder.CreateICmpULT(_idx, callDataSize);
auto idx = m_builder.CreateTrunc(m_builder.CreateSelect(idxValid, _idx, callDataSize), Type::Size, "idx");
auto end = m_builder.CreateNUWAdd(idx, m_builder.getInt64(32));
end = m_builder.CreateSelect(m_builder.CreateICmpULE(end, callDataSize64), end, callDataSize64);
auto copySize = m_builder.CreateNUWSub(end, idx);
auto padSize = m_builder.CreateNUWSub(m_builder.getInt64(32), copySize);
auto dataBegin = m_builder.CreateGEP(Type::Byte, getRuntimeManager().getCallData(), idx);
m_builder.CreateMemCpy(result, dataBegin, copySize, 1);
auto pad = m_builder.CreateGEP(Type::Byte, result, copySize);
m_builder.CreateMemSet(pad, m_builder.getInt8(0), padSize, 1);
m_argCounter = 0; // Release args allocas. TODO: This is a bad design
return Endianness::toNative(m_builder, m_builder.CreateLoad(ret));
}
llvm::Value* Ext::balance(llvm::Value* _address)

2
evmjit/libevmjit/Ext.h

@ -35,7 +35,6 @@ enum class EnvFunc
log,
blockhash,
extcode,
calldataload, // Helper function, not client Env interface
_size
};
@ -63,7 +62,6 @@ private:
Memory& m_memoryMan;
llvm::Value* m_size;
llvm::Value* m_data = nullptr;
std::array<llvm::Function*, sizeOf<EnvFunc>::value> m_funcs;
std::array<llvm::Value*, 8> m_argAllocas;

1
evmjit/libevmjit/GasMeter.h

@ -10,6 +10,7 @@ namespace eth
namespace jit
{
class RuntimeManager;
using namespace evmjit;
class GasMeter : public CompilerHelper // TODO: Use RuntimeHelper
{

5
evmjit/libevmjit/Instruction.cpp

@ -6,9 +6,7 @@
namespace dev
{
namespace eth
{
namespace jit
namespace evmjit
{
llvm::APInt readPushData(code_iterator& _curr, code_iterator _end)
@ -39,4 +37,3 @@ void skipPushData(code_iterator& _curr, code_iterator _end)
}
}
}

5
evmjit/libevmjit/Instruction.h

@ -9,9 +9,7 @@ namespace llvm
namespace dev
{
namespace eth
{
namespace jit
namespace evmjit
{
/// Virtual machine bytecode instruction.
@ -236,4 +234,3 @@ void skipPushData(code_iterator& _curr, code_iterator _end);
}
}
}

222
evmjit/libevmjit/JIT.cpp

@ -1,45 +1,239 @@
#include "evmjit/JIT.h"
#include <unordered_map>
#include <array>
#include "preprocessor/llvm_includes_start.h"
#include <llvm/IR/Module.h>
#include <llvm/ADT/Triple.h>
#include <llvm/ExecutionEngine/MCJIT.h>
#include <llvm/Support/TargetSelect.h>
#include <llvm/Support/Host.h>
#include <llvm/Support/CommandLine.h>
#include <llvm/Support/ManagedStatic.h>
#include "preprocessor/llvm_includes_end.h"
#include "Compiler.h"
#include "Optimizer.h"
#include "Cache.h"
#include "ExecStats.h"
#include "Utils.h"
#include "BuildInfo.gen.h"
namespace dev
{
namespace evmjit
{
using namespace eth::jit;
namespace
{
using ExecFunc = ReturnCode(*)(ExecutionContext*);
class JITImpl: JIT
std::string hash2str(i256 const& _hash)
{
public:
std::unordered_map<h256, uint64_t> codeMap;
static const auto size = sizeof(_hash);
static const auto hexChars = "0123456789abcdef";
std::string str;
str.resize(size * 2);
auto outIt = str.rbegin(); // reverse for BE
auto& arr = *(std::array<byte, size>*)&_hash;
for (auto b : arr)
{
*(outIt++) = hexChars[b & 0xf];
*(outIt++) = hexChars[b >> 4];
}
return str;
}
void printVersion()
{
std::cout << "Ethereum EVM JIT Compiler (http://github.com/ethereum/evmjit):\n"
<< " EVMJIT version " << EVMJIT_VERSION << "\n"
#ifdef NDEBUG
<< " Optimized build, " EVMJIT_VERSION_FULL "\n"
#else
<< " DEBUG build, " EVMJIT_VERSION_FULL "\n"
#endif
<< " Built " << __DATE__ << " (" << __TIME__ << ")\n"
<< std::endl;
}
namespace cl = llvm::cl;
cl::opt<bool> g_optimize{"O", cl::desc{"Optimize"}};
cl::opt<CacheMode> g_cache{"cache", cl::desc{"Cache compiled EVM code on disk"},
cl::values(
clEnumValN(CacheMode::on, "1", "Enabled"),
clEnumValN(CacheMode::off, "0", "Disabled"),
clEnumValN(CacheMode::read, "r", "Read only. No new objects are added to cache."),
clEnumValN(CacheMode::write, "w", "Write only. No objects are loaded from cache."),
clEnumValN(CacheMode::clear, "c", "Clear the cache storage. Cache is disabled."),
clEnumValN(CacheMode::preload, "p", "Preload all cached objects."),
clEnumValEnd)};
cl::opt<bool> g_stats{"st", cl::desc{"Statistics"}};
cl::opt<bool> g_dump{"dump", cl::desc{"Dump LLVM IR module"}};
void parseOptions()
{
static llvm::llvm_shutdown_obj shutdownObj{};
cl::AddExtraVersionPrinter(printVersion);
cl::ParseEnvironmentOptions("evmjit", "EVMJIT", "Ethereum EVM JIT Compiler");
}
class JITImpl
{
std::unique_ptr<llvm::ExecutionEngine> m_engine;
std::unordered_map<h256, ExecFunc> m_codeMap;
public:
static JITImpl& instance()
{
static JITImpl s_instance;
return s_instance;
}
JITImpl();
llvm::ExecutionEngine& engine() { return *m_engine; }
ExecFunc getExecFunc(h256 const& _codeHash) const;
void mapExecFunc(h256 _codeHash, ExecFunc _funcAddr);
};
JITImpl::JITImpl()
{
parseOptions();
bool preloadCache = g_cache == CacheMode::preload;
if (preloadCache)
g_cache = CacheMode::on;
llvm::InitializeNativeTarget();
llvm::InitializeNativeTargetAsmPrinter();
auto module = std::unique_ptr<llvm::Module>(new llvm::Module({}, llvm::getGlobalContext()));
// FIXME: LLVM 3.7: test on Windows
auto triple = llvm::Triple(llvm::sys::getProcessTriple());
if (triple.getOS() == llvm::Triple::OSType::Win32)
triple.setObjectFormat(llvm::Triple::ObjectFormatType::ELF); // MCJIT does not support COFF format
module->setTargetTriple(triple.str());
llvm::EngineBuilder builder(std::move(module));
builder.setEngineKind(llvm::EngineKind::JIT);
builder.setOptLevel(g_optimize ? llvm::CodeGenOpt::Default : llvm::CodeGenOpt::None);
m_engine.reset(builder.create());
// TODO: Update cache listener
m_engine->setObjectCache(Cache::init(g_cache, nullptr));
// FIXME: Disabled during API changes
//if (preloadCache)
// Cache::preload(*m_engine, funcCache);
}
ExecFunc JITImpl::getExecFunc(h256 const& _codeHash) const
{
auto it = m_codeMap.find(_codeHash);
if (it != m_codeMap.end())
return it->second;
return nullptr;
}
void JITImpl::mapExecFunc(h256 _codeHash, ExecFunc _funcAddr)
{
m_codeMap.emplace(std::move(_codeHash), _funcAddr);
}
} // anonymous namespace
bool JIT::isCodeReady(h256 _codeHash)
bool JIT::isCodeReady(h256 const& _codeHash)
{
return JITImpl::instance().codeMap.count(_codeHash) != 0;
return JITImpl::instance().getExecFunc(_codeHash) != nullptr;
}
uint64_t JIT::getCode(h256 _codeHash)
ReturnCode JIT::exec(ExecutionContext& _context)
{
auto& codeMap = JITImpl::instance().codeMap;
auto it = codeMap.find(_codeHash);
if (it != codeMap.end())
return it->second;
return 0;
auto& jit = JITImpl::instance();
std::unique_ptr<ExecStats> listener{new ExecStats};
listener->stateChanged(ExecState::Started);
auto code = _context.code();
auto codeSize = _context.codeSize();
auto codeHash = _context.codeHash();
static StatsCollector statsCollector;
auto mainFuncName = hash2str(codeHash);
// TODO: Remove cast
auto execFunc = jit.getExecFunc(codeHash);
if (!execFunc)
{
auto module = Cache::getObject(mainFuncName);
if (!module)
{
listener->stateChanged(ExecState::Compilation);
assert(code || !codeSize); //TODO: Is it good idea to execute empty code?
module = Compiler{{}}.compile(code, code + codeSize, mainFuncName);
if (g_optimize)
{
listener->stateChanged(ExecState::Optimization);
optimize(*module);
}
prepare(*module);
}
if (g_dump)
module->dump();
jit.engine().addModule(std::move(module));
listener->stateChanged(ExecState::CodeGen);
execFunc = (ExecFunc)jit.engine().getFunctionAddress(mainFuncName);
if (!CHECK(execFunc))
return ReturnCode::LLVMLinkError;
jit.mapExecFunc(codeHash, execFunc);
}
listener->stateChanged(ExecState::Execution);
auto returnCode = execFunc(&_context);
listener->stateChanged(ExecState::Return);
if (returnCode == ReturnCode::Return)
_context.returnData = _context.getReturnData(); // Save reference to return data
listener->stateChanged(ExecState::Finished);
if (g_stats)
statsCollector.stats.push_back(std::move(listener));
return returnCode;
}
void JIT::mapCode(h256 _codeHash, uint64_t _funcAddr)
extern "C" void ext_free(void* _data) noexcept;
ExecutionContext::~ExecutionContext()
{
JITImpl::instance().codeMap.insert(std::make_pair(_codeHash, _funcAddr));
if (m_memData)
ext_free(m_memData); // Use helper free to check memory leaks
}
bytes_ref ExecutionContext::getReturnData() const
{
auto data = m_data->callData;
auto size = static_cast<size_t>(m_data->callDataSize);
if (data < m_memData || data >= m_memData + m_memSize || size == 0)
{
assert(size == 0); // data can be an invalid pointer only if size is 0
m_data->callData = nullptr;
return {};
}
return bytes_ref{data, size};
}
}

6
evmjit/libevmjit/Memory.cpp

@ -5,7 +5,6 @@
#include "preprocessor/llvm_includes_end.h"
#include "Type.h"
#include "Runtime.h"
#include "GasMeter.h"
#include "Endianness.h"
#include "RuntimeManager.h"
@ -191,8 +190,7 @@ llvm::Value* Memory::getSize()
llvm::Value* Memory::getBytePtr(llvm::Value* _index)
{
auto idx = m_builder.CreateTrunc(_index, Type::Size, "idx"); // Never allow memory index be a type bigger than i64
return m_builder.CreateGEP(getData(), idx, "ptr");
return m_builder.CreateGEP(getData(), _index, "ptr");
}
void Memory::require(llvm::Value* _offset, llvm::Value* _size)
@ -235,7 +233,7 @@ void Memory::copyBytes(llvm::Value* _srcPtr, llvm::Value* _srcSize, llvm::Value*
auto bytesToZero = m_builder.CreateNUWSub(reqBytes, bytesToCopy, "bytesToZero");
auto src = m_builder.CreateGEP(_srcPtr, idx64, "src");
auto dstIdx = m_builder.CreateTrunc(_destMemIdx, Type::Size, "dstIdx"); // Never allow memory index be a type bigger than i64
auto dstIdx = m_builder.CreateTrunc(_destMemIdx, Type::Size, "dstIdx");
auto padIdx = m_builder.CreateNUWAdd(dstIdx, bytesToCopy, "padIdx");
auto dst = m_memory.getPtr(getRuntimeManager().getMem(), dstIdx);
auto pad = m_memory.getPtr(getRuntimeManager().getMem(), padIdx);

97
evmjit/libevmjit/Optimizer.cpp

@ -1,11 +1,16 @@
#include "Optimizer.h"
#include "preprocessor/llvm_includes_start.h"
#include <llvm/IR/BasicBlock.h>
#include <llvm/IR/Function.h>
#include <llvm/IR/LegacyPassManager.h>
#include <llvm/Transforms/Scalar.h>
#include <llvm/Transforms/IPO.h>
#include "preprocessor/llvm_includes_end.h"
#include "Arith256.h"
#include "Type.h"
namespace dev
{
namespace eth
@ -16,7 +21,7 @@ namespace jit
bool optimize(llvm::Module& _module)
{
auto pm = llvm::legacy::PassManager{};
//pm.add(llvm::createFunctionInliningPass(2, 2)); // Problem with APInt value bigger than 64bit
pm.add(llvm::createFunctionInliningPass(2, 2));
pm.add(llvm::createCFGSimplificationPass());
pm.add(llvm::createInstructionCombiningPass());
pm.add(llvm::createAggressiveDCEPass());
@ -24,6 +29,96 @@ bool optimize(llvm::Module& _module)
return pm.run(_module);
}
namespace
{
class LowerEVMPass: public llvm::BasicBlockPass
{
static char ID;
public:
LowerEVMPass():
llvm::BasicBlockPass(ID)
{}
virtual bool runOnBasicBlock(llvm::BasicBlock& _bb) override;
using llvm::BasicBlockPass::doFinalization;
virtual bool doFinalization(llvm::Module& _module) override;
};
char LowerEVMPass::ID = 0;
bool LowerEVMPass::runOnBasicBlock(llvm::BasicBlock& _bb)
{
auto modified = false;
auto module = _bb.getParent()->getParent();
auto i512Ty = llvm::IntegerType::get(_bb.getContext(), 512);
for (auto it = _bb.begin(); it != _bb.end(); )
{
auto& inst = *it++;
llvm::Function* func = nullptr;
if (inst.getType() == Type::Word)
{
switch (inst.getOpcode())
{
case llvm::Instruction::Mul:
func = Arith256::getMulFunc(*module);
break;
case llvm::Instruction::UDiv:
func = Arith256::getUDiv256Func(*module);
break;
case llvm::Instruction::URem:
func = Arith256::getURem256Func(*module);
break;
case llvm::Instruction::SDiv:
func = Arith256::getSDiv256Func(*module);
break;
case llvm::Instruction::SRem:
func = Arith256::getSRem256Func(*module);
break;
}
}
else if (inst.getType() == i512Ty)
{
switch (inst.getOpcode())
{
case llvm::Instruction::URem:
func = Arith256::getURem512Func(*module);
break;
}
}
if (func)
{
auto call = llvm::CallInst::Create(func, {inst.getOperand(0), inst.getOperand(1)}, "", &inst);
inst.replaceAllUsesWith(call);
inst.eraseFromParent();
modified = true;
}
}
return modified;
}
bool LowerEVMPass::doFinalization(llvm::Module&)
{
return false;
}
}
bool prepare(llvm::Module& _module)
{
auto pm = llvm::legacy::PassManager{};
pm.add(llvm::createDeadCodeEliminationPass());
pm.add(new LowerEVMPass{});
return pm.run(_module);
}
}
}
}

2
evmjit/libevmjit/Optimizer.h

@ -14,6 +14,8 @@ namespace jit
bool optimize(llvm::Module& _module);
bool prepare(llvm::Module& _module);
}
}
}

43
evmjit/libevmjit/Runtime.cpp

@ -1,43 +0,0 @@
#include "Runtime.h"
#include <cassert>
namespace dev
{
namespace eth
{
namespace jit
{
void Runtime::init(RuntimeData* _data, Env* _env)
{
m_data = _data;
m_env = _env;
}
extern "C" void ext_free(void* _data) noexcept;
Runtime::~Runtime()
{
if (m_memData)
ext_free(m_memData); // Use helper free to check memory leaks
}
bytes_ref Runtime::getReturnData() const
{
auto data = m_data->callData;
auto size = static_cast<size_t>(m_data->callDataSize);
if (data < m_memData || data >= m_memData + m_memSize || size == 0)
{
assert(size == 0); // data can be an invalid pointer only if size is 0
m_data->callData = nullptr;
return {};
}
return bytes_ref{data, size};
}
}
}
}

30
evmjit/libevmjit/Runtime.h

@ -1,30 +0,0 @@
#pragma once
#include "RuntimeData.h"
namespace dev
{
namespace eth
{
namespace jit
{
class Runtime
{
public:
void init(RuntimeData* _data, Env* _env);
EXPORT ~Runtime();
bytes_ref getReturnData() const;
private:
RuntimeData* m_data = nullptr; ///< Pointer to data. Expected by compiled contract.
Env* m_env = nullptr; ///< Pointer to environment proxy. Expected by compiled contract.
byte* m_memData = nullptr;
uint64_t m_memSize = 0;
uint64_t m_memCap = 0;
};
}
}
}

63
evmjit/libevmjit/RuntimeData.h

@ -1,63 +0,0 @@
#pragma once
#include "evmjit/DataTypes.h"
#include "Common.h"
namespace dev
{
namespace eth
{
namespace jit
{
using evmjit::i256;
using evmjit::h256;
struct RuntimeData
{
enum Index
{
Gas,
GasPrice,
CallData,
CallDataSize,
Address,
Caller,
Origin,
CallValue,
CoinBase,
Difficulty,
GasLimit,
Number,
Timestamp,
Code,
CodeSize,
SuicideDestAddress = Address, ///< Suicide balance destination address
ReturnData = CallData, ///< Return data pointer (set only in case of RETURN)
ReturnDataSize = CallDataSize, ///< Return data size (set only in case of RETURN)
};
int64_t gas = 0;
int64_t gasPrice = 0;
byte const* callData = nullptr;
uint64_t callDataSize = 0;
i256 address;
i256 caller;
i256 origin;
i256 callValue;
i256 coinBase;
i256 difficulty;
i256 gasLimit;
uint64_t number = 0;
int64_t timestamp = 0;
byte const* code = nullptr;
uint64_t codeSize = 0;
h256 codeHash;
};
/// VM Environment (ExtVM) opaque type
struct Env;
}
}
}

56
evmjit/libevmjit/RuntimeManager.cpp

@ -64,22 +64,22 @@ llvm::Twine getName(RuntimeData::Index _index)
{
switch (_index)
{
default: return "data";
case RuntimeData::Address: return "address";
case RuntimeData::Caller: return "caller";
case RuntimeData::Origin: return "origin";
case RuntimeData::CallValue: return "callvalue";
case RuntimeData::GasPrice: return "gasprice";
case RuntimeData::CoinBase: return "coinbase";
case RuntimeData::Difficulty: return "difficulty";
case RuntimeData::GasLimit: return "gaslimit";
case RuntimeData::CallData: return "callData";
case RuntimeData::Code: return "code";
case RuntimeData::CodeSize: return "code";
case RuntimeData::CallDataSize: return "callDataSize";
case RuntimeData::Gas: return "gas";
case RuntimeData::Number: return "number";
case RuntimeData::Timestamp: return "timestamp";
default: return "";
case RuntimeData::Gas: return "msg.gas";
case RuntimeData::GasPrice: return "tx.gasprice";
case RuntimeData::CallData: return "msg.data.ptr";
case RuntimeData::CallDataSize: return "msg.data.size";
case RuntimeData::Address: return "this.address";
case RuntimeData::Caller: return "msg.caller";
case RuntimeData::Origin: return "tx.origin";
case RuntimeData::CallValue: return "msg.value";
case RuntimeData::CoinBase: return "block.coinbase";
case RuntimeData::Difficulty: return "block.difficulty";
case RuntimeData::GasLimit: return "block.gaslimit";
case RuntimeData::Number: return "block.number";
case RuntimeData::Timestamp: return "block.timestamp";
case RuntimeData::Code: return "code.ptr";
case RuntimeData::CodeSize: return "code.size";
}
}
}
@ -93,10 +93,8 @@ RuntimeManager::RuntimeManager(llvm::IRBuilder<>& _builder, code_iterator _codeB
// Unpack data
auto rtPtr = getRuntimePtr();
m_dataPtr = m_builder.CreateLoad(m_builder.CreateStructGEP(getRuntimeType(), rtPtr, 0), "data");
m_dataPtr = m_builder.CreateLoad(m_builder.CreateStructGEP(getRuntimeType(), rtPtr, 0), "dataPtr");
assert(m_dataPtr->getType() == Type::RuntimeDataPtr);
m_gasPtr = m_builder.CreateStructGEP(getRuntimeDataType(), m_dataPtr, 0, "gas");
assert(m_gasPtr->getType() == Type::Gas->getPointerTo());
m_memPtr = m_builder.CreateStructGEP(getRuntimeType(), rtPtr, 2, "mem");
assert(m_memPtr->getType() == Array::getType()->getPointerTo());
m_envPtr = m_builder.CreateLoad(m_builder.CreateStructGEP(getRuntimeType(), rtPtr, 1), "env");
@ -105,6 +103,13 @@ RuntimeManager::RuntimeManager(llvm::IRBuilder<>& _builder, code_iterator _codeB
m_stackSize = m_builder.CreateAlloca(Type::Size, nullptr, "stackSize");
m_builder.CreateStore(m_builder.getInt64(0), m_stackSize);
auto data = m_builder.CreateLoad(m_dataPtr, "data");
for (unsigned i = 0; i < m_dataElts.size(); ++i)
m_dataElts[i] = m_builder.CreateExtractValue(data, i, getName(RuntimeData::Index(i)));
m_gasPtr = m_builder.CreateAlloca(Type::Gas, nullptr, "gas.ptr");
m_builder.CreateStore(m_dataElts[RuntimeData::Index::Gas], m_gasPtr);
llvm::Type* checkStackLimitArgs[] = {Type::Size->getPointerTo(), Type::Size, Type::Size, Type::BytePtr};
m_checkStackLimit = llvm::Function::Create(llvm::FunctionType::get(Type::Void, checkStackLimitArgs, false), llvm::Function::PrivateLinkage, "stack.checkSize", getModule());
m_checkStackLimit->setDoesNotThrow();
@ -180,7 +185,7 @@ llvm::Value* RuntimeManager::getPtr(RuntimeData::Index _index)
llvm::Value* RuntimeManager::get(RuntimeData::Index _index)
{
return getBuilder().CreateLoad(getPtr(_index), getName(_index));
return m_dataElts[_index];
}
void RuntimeManager::set(RuntimeData::Index _index, llvm::Value* _value)
@ -194,8 +199,7 @@ void RuntimeManager::registerReturnData(llvm::Value* _offset, llvm::Value* _size
{
auto memPtr = m_builder.CreateBitCast(getMem(), Type::BytePtr->getPointerTo());
auto mem = getBuilder().CreateLoad(memPtr, "memory");
auto idx = m_builder.CreateTrunc(_offset, Type::Size, "idx"); // Never allow memory index be a type bigger than i64 // TODO: Report bug & fix to LLVM
auto returnDataPtr = getBuilder().CreateGEP(mem, idx);
auto returnDataPtr = getBuilder().CreateGEP(mem, _offset);
set(RuntimeData::ReturnData, returnDataPtr);
auto size64 = getBuilder().CreateTrunc(_size, Type::Size);
@ -212,6 +216,8 @@ void RuntimeManager::exit(ReturnCode _returnCode)
if (m_stack)
m_stack->free();
auto extGasPtr = m_builder.CreateStructGEP(getRuntimeDataType(), getDataPtr(), RuntimeData::Index::Gas, "msg.gas.ptr");
m_builder.CreateStore(getGas(), extGasPtr);
m_builder.CreateRet(Constant::get(_returnCode));
}
@ -265,9 +271,7 @@ llvm::Value* RuntimeManager::getCallDataSize()
llvm::Value* RuntimeManager::getGas()
{
auto gas = get(RuntimeData::Gas);
assert(gas->getType() == Type::Gas);
return gas;
return getBuilder().CreateLoad(getGasPtr(), "gas");
}
llvm::Value* RuntimeManager::getGasPtr()
@ -285,7 +289,7 @@ llvm::Value* RuntimeManager::getMem()
void RuntimeManager::setGas(llvm::Value* _gas)
{
assert(_gas->getType() == Type::Gas);
set(RuntimeData::Gas, _gas);
getBuilder().CreateStore(_gas, getGasPtr());
}
}

6
evmjit/libevmjit/RuntimeManager.h

@ -1,8 +1,9 @@
#pragma once
#include <array>
#include "CompilerHelper.h"
#include "Type.h"
#include "RuntimeData.h"
#include "Instruction.h"
namespace dev
@ -11,6 +12,7 @@ namespace eth
{
namespace jit
{
using namespace evmjit;
class Stack;
class RuntimeManager: public CompilerHelper
@ -61,6 +63,8 @@ private:
llvm::Value* m_memPtr = nullptr;
llvm::Value* m_envPtr = nullptr;
std::array<llvm::Value*, RuntimeData::numElements> m_dataElts;
llvm::Value* m_stackSize = nullptr;
llvm::Function* m_checkStackLimit = nullptr;

120
evmjit/libevmjit/Stack.cpp

@ -5,7 +5,6 @@
#include "preprocessor/llvm_includes_end.h"
#include "RuntimeManager.h"
#include "Runtime.h"
#include "Utils.h"
#include <set> // DEBUG only
@ -23,96 +22,6 @@ Stack::Stack(llvm::IRBuilder<>& _builder, RuntimeManager& _runtimeManager):
m_stack(_builder, "stack")
{}
llvm::Function* Stack::getPushFunc()
{
auto& func = m_push;
if (!func)
{
llvm::Type* argTypes[] = {Type::RuntimePtr, Type::Word};
func = llvm::Function::Create(llvm::FunctionType::get(Type::Void, argTypes, false), llvm::Function::ExternalLinkage, "stack.push", getModule());
llvm::Type* extArgTypes[] = {Type::RuntimePtr, Type::WordPtr};
auto extPushFunc = llvm::Function::Create(llvm::FunctionType::get(Type::Void, extArgTypes, false), llvm::Function::ExternalLinkage, "stack_push", getModule());
auto rt = &func->getArgumentList().front();
rt->setName("rt");
auto value = rt->getNextNode();
value->setName("value");
InsertPointGuard guard{m_builder};
auto entryBB = llvm::BasicBlock::Create(m_builder.getContext(), {}, func);
m_builder.SetInsertPoint(entryBB);
auto a = m_builder.CreateAlloca(Type::Word);
m_builder.CreateStore(value, a);
createCall(extPushFunc, {rt, a});
m_builder.CreateRetVoid();
}
return func;
}
llvm::Function* Stack::getSetFunc()
{
auto& func = m_set;
if (!func)
{
llvm::Type* argTypes[] = {Type::RuntimePtr, Type::Size, Type::Word};
func = llvm::Function::Create(llvm::FunctionType::get(Type::Void, argTypes, false), llvm::Function::ExternalLinkage, "stack.set", getModule());
llvm::Type* extArgTypes[] = {Type::RuntimePtr, Type::Size, Type::WordPtr};
auto extSetFunc = llvm::Function::Create(llvm::FunctionType::get(Type::Void, extArgTypes, false), llvm::Function::ExternalLinkage, "stack_set", getModule());
auto rt = &func->getArgumentList().front();
rt->setName("rt");
auto index = rt->getNextNode();
index->setName("index");
auto value = index->getNextNode();
value->setName("value");
InsertPointGuard guard{m_builder};
auto entryBB = llvm::BasicBlock::Create(m_builder.getContext(), {}, func);
m_builder.SetInsertPoint(entryBB);
auto a = m_builder.CreateAlloca(Type::Word);
m_builder.CreateStore(value, a);
createCall(extSetFunc, {rt, index, a});
m_builder.CreateRetVoid();
}
return func;
}
llvm::Function* Stack::getPopFunc()
{
auto& func = m_pop;
if (!func)
{
llvm::Type* argTypes[] = {Type::RuntimePtr, Type::Size, Type::BytePtr};
func = llvm::Function::Create(llvm::FunctionType::get(Type::Void, argTypes, false), llvm::Function::ExternalLinkage, "stack.pop", getModule());
llvm::Type* extArgTypes[] = {Type::RuntimePtr, Type::Size};
auto extPopFunc = llvm::Function::Create(llvm::FunctionType::get(Type::Bool, extArgTypes, false), llvm::Function::ExternalLinkage, "stack_pop", getModule());
auto rt = &func->getArgumentList().front();
rt->setName("rt");
auto index = rt->getNextNode();
index->setName("index");
auto jmpBuf = index->getNextNode();
jmpBuf->setName("jmpBuf");
InsertPointGuard guard{m_builder};
auto entryBB = llvm::BasicBlock::Create(m_builder.getContext(), {}, func);
auto underflowBB = llvm::BasicBlock::Create(m_builder.getContext(), "Underflow", func);
auto returnBB = llvm::BasicBlock::Create(m_builder.getContext(), "Return", func);
m_builder.SetInsertPoint(entryBB);
auto ok = createCall(extPopFunc, {rt, index});
m_builder.CreateCondBr(ok, returnBB, underflowBB); //TODO: Add branch weight
m_builder.SetInsertPoint(underflowBB);
m_runtimeManager.abort(jmpBuf);
m_builder.CreateUnreachable();
m_builder.SetInsertPoint(returnBB);
m_builder.CreateRetVoid();
}
return func;
}
llvm::Function* Stack::getGetFunc()
{
auto& func = m_get;
@ -175,32 +84,3 @@ void Stack::push(llvm::Value* _value)
}
}
}
extern "C"
{
using namespace dev::eth::jit;
EXPORT void ext_calldataload(RuntimeData* _rtData, i256* _index, byte* o_value)
{
// It asumes all indexes are less than 2^64
auto index = _index->a;
if (_index->b || _index->c || _index->d) // if bigger that 2^64
index = std::numeric_limits<decltype(index)>::max(); // set max to fill with 0 leter
auto data = _rtData->callData;
auto size = _rtData->callDataSize;
for (auto i = 0; i < 32; ++i)
{
if (index < size)
{
o_value[i] = data[index];
++index; // increment only if in range
}
else
o_value[i] = 0;
}
}
} // extern "C"

8
evmjit/libevmjit/Stack.h

@ -24,18 +24,10 @@ public:
void free() { m_stack.free(); }
private:
llvm::Function* getPopFunc();
llvm::Function* getPushFunc();
llvm::Function* getGetFunc();
llvm::Function* getSetFunc();
RuntimeManager& m_runtimeManager;
llvm::Function* m_pop = nullptr;
llvm::Function* m_push = nullptr;
llvm::Function* m_get = nullptr;
llvm::Function* m_set = nullptr;
Array m_stack;
};

3
evmjit/libevmjit/Type.cpp

@ -13,7 +13,6 @@ namespace jit
llvm::IntegerType* Type::Word;
llvm::PointerType* Type::WordPtr;
llvm::IntegerType* Type::lowPrecision;
llvm::IntegerType* Type::Bool;
llvm::IntegerType* Type::Size;
llvm::IntegerType* Type::Gas;
@ -34,8 +33,6 @@ void Type::init(llvm::LLVMContext& _context)
{
Word = llvm::Type::getIntNTy(_context, 256);
WordPtr = Word->getPointerTo();
lowPrecision = llvm::Type::getInt64Ty(_context);
// TODO: Size should be architecture-dependent
Bool = llvm::Type::getInt1Ty(_context);
Size = llvm::Type::getInt64Ty(_context);
Gas = Size;

9
evmjit/libevmjit/Type.h

@ -4,9 +4,9 @@
#include <llvm/IR/Type.h>
#include <llvm/IR/Constants.h>
#include <llvm/IR/Metadata.h>
#include "preprocessor/llvm_includes_end.h" // FIXME: LLVM 3.7: check if needed
#include "preprocessor/llvm_includes_end.h"
#include "Common.h"
#include "evmjit/JIT.h" // ReturnCode
namespace dev
{
@ -14,16 +14,13 @@ namespace eth
{
namespace jit
{
using namespace evmjit;
struct Type
{
static llvm::IntegerType* Word;
static llvm::PointerType* WordPtr;
/// Type for doing low precision arithmetics where 256-bit precision is not supported by native target
/// @TODO: Use 64-bit for now. In 128-bit compiler-rt library functions are required
static llvm::IntegerType* lowPrecision;
static llvm::IntegerType* Bool;
static llvm::IntegerType* Size;
static llvm::IntegerType* Gas;

25
evmjit/libevmjit/interface.cpp

@ -1,29 +1,28 @@
#include "ExecutionEngine.h"
#include "evmjit/JIT.h"
extern "C"
{
using namespace dev::evmjit;
using namespace dev::eth::jit;
EXPORT void* evmjit_create() noexcept
EXPORT void* evmjit_create(RuntimeData* _data, Env* _env) noexcept
{
// TODO: Make sure ExecutionEngine constructor does not throw
return new(std::nothrow) ExecutionEngine;
if (!_data)
return nullptr;
// TODO: Make sure ExecutionEngine constructor does not throw + make JIT/ExecutionEngine interface all nothrow
return new(std::nothrow) ExecutionContext{*_data, _env};
}
EXPORT void evmjit_destroy(ExecutionEngine* _engine) noexcept
EXPORT void evmjit_destroy(ExecutionContext* _context) noexcept
{
delete _engine;
delete _context;
}
EXPORT int evmjit_run(ExecutionEngine* _engine, RuntimeData* _data, Env* _env) noexcept
EXPORT int evmjit_run(ExecutionContext* _context) noexcept
{
if (!_engine || !_data)
return static_cast<int>(ReturnCode::UnexpectedException);
try
{
auto returnCode = _engine->run(_data, _env);
auto returnCode = JIT::exec(*_context);
return static_cast<int>(returnCode);
}
catch(...)

9
libdevcore/Common.cpp

@ -32,10 +32,13 @@ char const* Version = "0.9.27";
const u256 UndefinedU256 = ~(u256)0;
void HasInvariants::checkInvariants() const
void InvariantChecker::checkInvariants() const
{
if (!invariants())
BOOST_THROW_EXCEPTION(FailedInvariant());
if (!m_this->invariants())
{
cwarn << "Invariant failed in" << m_function << "at" << m_file << ":" << m_line;
::boost::exception_detail::throw_exception_(FailedInvariant(), m_function, m_file, m_line);
}
}
struct TimerChannel: public LogChannel { static const char* name(); static const int verbosity = 0; };

17
libdevcore/Common.h

@ -41,6 +41,7 @@
#include <functional>
#include <string>
#include <chrono>
#include <boost/current_function.hpp>
#include <boost/functional/hash.hpp>
#pragma warning(push)
#pragma GCC diagnostic push
@ -163,10 +164,6 @@ private:
class HasInvariants
{
public:
/// Check invariants are met, throw if not.
void checkInvariants() const;
protected:
/// Reimplement to specify the invariants.
virtual bool invariants() const = 0;
};
@ -175,16 +172,22 @@ protected:
class InvariantChecker
{
public:
InvariantChecker(HasInvariants* _this): m_this(_this) { m_this->checkInvariants(); }
~InvariantChecker() { m_this->checkInvariants(); }
InvariantChecker(HasInvariants* _this, char const* _fn, char const* _file, int _line): m_this(_this), m_function(_fn), m_file(_file), m_line(_line) { checkInvariants(); }
~InvariantChecker() { checkInvariants(); }
private:
/// Check invariants are met, throw if not.
void checkInvariants() const;
HasInvariants const* m_this;
char const* m_function;
char const* m_file;
int m_line;
};
/// Scope guard for invariant check in a class derived from HasInvariants.
#if ETH_DEBUG
#define DEV_INVARIANT_CHECK { ::dev::InvariantChecker __dev_invariantCheck(this); }
#define DEV_INVARIANT_CHECK { ::dev::InvariantChecker __dev_invariantCheck(this, BOOST_CURRENT_FUNCTION, __FILE__, __LINE__); }
#else
#define DEV_INVARIANT_CHECK (void)0;
#endif

6
libdevcore/CommonData.h

@ -334,4 +334,10 @@ std::vector<T> keysOf(std::unordered_map<T, U> const& _m)
return ret;
}
template <class T, class V>
bool contains(T const& _t, V const& _v)
{
return std::end(_t) != std::find(std::begin(_t), std::end(_t), _v);
}
}

2
libdevcore/RLP.cpp

@ -320,7 +320,7 @@ std::ostream& dev::operator<<(std::ostream& _out, RLP const& _d)
if (_d.isNull())
_out << "null";
else if (_d.isInt())
_out << std::showbase << std::hex << std::nouppercase << _d.toInt<bigint>(RLP::LaisezFaire) << dec;
_out << std::showbase << std::hex << std::nouppercase << _d.toInt<bigint>(RLP::LaissezFaire) << dec;
else if (_d.isData())
_out << escaped(_d.toString(), false);
else if (_d.isList())

2
libdevcore/RLP.h

@ -71,7 +71,7 @@ public:
FailIfTooSmall = 16,
Strict = ThrowOnFail | FailIfTooBig,
VeryStrict = ThrowOnFail | FailIfTooBig | FailIfTooSmall,
LaisezFaire = AllowNonCanon
LaissezFaire = AllowNonCanon
};
using Strictness = int;

4
libdevcrypto/CMakeLists.txt

@ -24,6 +24,10 @@ target_link_libraries(${EXECUTABLE} ${DB_LIBRARIES})
target_link_libraries(${EXECUTABLE} ${CRYPTOPP_LIBRARIES})
target_link_libraries(${EXECUTABLE} scrypt)
target_link_libraries(${EXECUTABLE} devcore)
if (NOT WIN32)
add_definitions(-DETH_HAVE_SECP256K1)
target_link_libraries(${EXECUTABLE} secp256k1)
endif ()
install( TARGETS ${EXECUTABLE} RUNTIME DESTINATION bin ARCHIVE DESTINATION lib LIBRARY DESTINATION lib )
install( FILES ${HEADERS} DESTINATION include/${EXECUTABLE} )

49
libdevcrypto/Common.cpp

@ -29,6 +29,9 @@
#include <libdevcore/Guards.h>
#include <libdevcore/SHA3.h>
#include <libdevcore/FileSystem.h>
#if ETH_HAVE_SECP256K1
#include <secp256k1/secp256k1.h>
#endif
#include "AES.h"
#include "CryptoPP.h"
#include "Exceptions.h"
@ -36,7 +39,17 @@ using namespace std;
using namespace dev;
using namespace dev::crypto;
static Secp256k1 s_secp256k1;
#ifdef ETH_HAVE_SECP256K1
struct Secp256k1Context
{
Secp256k1Context() { secp256k1_start(); }
~Secp256k1Context() { secp256k1_stop(); }
};
static Secp256k1Context s_secp256k1;
void dev::crypto::secp256k1Init() { (void)s_secp256k1; }
#endif
static Secp256k1PP s_secp256k1pp;
bool dev::SignatureStruct::isValid() const noexcept
{
@ -53,34 +66,42 @@ Address dev::ZeroAddress = Address();
Public dev::toPublic(Secret const& _secret)
{
#ifdef ETH_HAVE_SECP256K1
bytes o(65);
int pubkeylen;
if (!secp256k1_ecdsa_pubkey_create(o.data(), &pubkeylen, _secret.data(), false))
return Public();
return FixedHash<64>(o.data()+1, Public::ConstructFromPointer);
#else
Public p;
s_secp256k1.toPublic(_secret, p);
s_secp256k1pp.toPublic(_secret, p);
return p;
#endif
}
Address dev::toAddress(Public const& _public)
{
return s_secp256k1.toAddress(_public);
return right160(sha3(_public.ref()));
}
Address dev::toAddress(Secret const& _secret)
{
Public p;
s_secp256k1.toPublic(_secret, p);
return s_secp256k1.toAddress(p);
s_secp256k1pp.toPublic(_secret, p);
return toAddress(p);
}
void dev::encrypt(Public const& _k, bytesConstRef _plain, bytes& o_cipher)
{
bytes io = _plain.toBytes();
s_secp256k1.encrypt(_k, io);
s_secp256k1pp.encrypt(_k, io);
o_cipher = std::move(io);
}
bool dev::decrypt(Secret const& _k, bytesConstRef _cipher, bytes& o_plaintext)
{
bytes io = _cipher.toBytes();
s_secp256k1.decrypt(_k, io);
s_secp256k1pp.decrypt(_k, io);
if (io.empty())
return false;
o_plaintext = std::move(io);
@ -90,14 +111,14 @@ bool dev::decrypt(Secret const& _k, bytesConstRef _cipher, bytes& o_plaintext)
void dev::encryptECIES(Public const& _k, bytesConstRef _plain, bytes& o_cipher)
{
bytes io = _plain.toBytes();
s_secp256k1.encryptECIES(_k, io);
s_secp256k1pp.encryptECIES(_k, io);
o_cipher = std::move(io);
}
bool dev::decryptECIES(Secret const& _k, bytesConstRef _cipher, bytes& o_plaintext)
{
bytes io = _cipher.toBytes();
if (!s_secp256k1.decryptECIES(_k, io))
if (!s_secp256k1pp.decryptECIES(_k, io))
return false;
o_plaintext = std::move(io);
return true;
@ -163,17 +184,17 @@ bytes dev::decryptAES128CTR(bytesConstRef _k, h128 const& _iv, bytesConstRef _ci
Public dev::recover(Signature const& _sig, h256 const& _message)
{
return s_secp256k1.recover(_sig, _message.ref());
return s_secp256k1pp.recover(_sig, _message.ref());
}
Signature dev::sign(Secret const& _k, h256 const& _hash)
{
return s_secp256k1.sign(_k, _hash);
return s_secp256k1pp.sign(_k, _hash);
}
bool dev::verify(Public const& _p, Signature const& _s, h256 const& _hash)
{
return s_secp256k1.verify(_p, _s, _hash.ref(), true);
return s_secp256k1pp.verify(_p, _s, _hash.ref(), true);
}
bytes dev::pbkdf2(string const& _pass, bytes const& _salt, unsigned _iterations, unsigned _dkLen)
@ -232,8 +253,8 @@ KeyPair KeyPair::create()
KeyPair::KeyPair(h256 _sec):
m_secret(_sec)
{
if (s_secp256k1.verifySecret(m_secret, m_public))
m_address = s_secp256k1.toAddress(m_public);
if (s_secp256k1pp.verifySecret(m_secret, m_public))
m_address = toAddress(m_public);
}
KeyPair KeyPair::fromEncryptedSeed(bytesConstRef _seed, std::string const& _password)

2
libdevcrypto/Common.h

@ -177,6 +177,8 @@ namespace crypto
{
struct InvalidState: public dev::Exception {};
void secp256k1Init();
/// Key derivation
h256 kdf(Secret const& _priv, h256 const& _hash);

30
libdevcrypto/CryptoPP.cpp

@ -33,7 +33,7 @@ static_assert(dev::Secret::size == 32, "Secret key must be 32 bytes.");
static_assert(dev::Public::size == 64, "Public key must be 64 bytes.");
static_assert(dev::Signature::size == 65, "Signature must be 65 bytes.");
bytes Secp256k1::eciesKDF(Secret _z, bytes _s1, unsigned kdByteLen)
bytes Secp256k1PP::eciesKDF(Secret _z, bytes _s1, unsigned kdByteLen)
{
// interop w/go ecies implementation
@ -64,7 +64,7 @@ bytes Secp256k1::eciesKDF(Secret _z, bytes _s1, unsigned kdByteLen)
return k;
}
void Secp256k1::encryptECIES(Public const& _k, bytes& io_cipher)
void Secp256k1PP::encryptECIES(Public const& _k, bytes& io_cipher)
{
// interop w/go ecies implementation
auto r = KeyPair::create();
@ -98,7 +98,7 @@ void Secp256k1::encryptECIES(Public const& _k, bytes& io_cipher)
io_cipher.swap(msg);
}
bool Secp256k1::decryptECIES(Secret const& _k, bytes& io_text)
bool Secp256k1PP::decryptECIES(Secret const& _k, bytes& io_text)
{
// interop w/go ecies implementation
@ -145,7 +145,7 @@ bool Secp256k1::decryptECIES(Secret const& _k, bytes& io_text)
return true;
}
void Secp256k1::encrypt(Public const& _k, bytes& io_cipher)
void Secp256k1PP::encrypt(Public const& _k, bytes& io_cipher)
{
ECIES<ECP>::Encryptor e;
initializeDLScheme(_k, e);
@ -163,7 +163,7 @@ void Secp256k1::encrypt(Public const& _k, bytes& io_cipher)
io_cipher = std::move(ciphertext);
}
void Secp256k1::decrypt(Secret const& _k, bytes& io_text)
void Secp256k1PP::decrypt(Secret const& _k, bytes& io_text)
{
CryptoPP::ECIES<CryptoPP::ECP>::Decryptor d;
initializeDLScheme(_k, d);
@ -194,12 +194,12 @@ void Secp256k1::decrypt(Secret const& _k, bytes& io_text)
io_text = std::move(plain);
}
Signature Secp256k1::sign(Secret const& _k, bytesConstRef _message)
Signature Secp256k1PP::sign(Secret const& _k, bytesConstRef _message)
{
return sign(_k, sha3(_message));
}
Signature Secp256k1::sign(Secret const& _key, h256 const& _hash)
Signature Secp256k1PP::sign(Secret const& _key, h256 const& _hash)
{
// assumption made by signing alogrithm
asserts(m_q == m_qs);
@ -240,18 +240,18 @@ Signature Secp256k1::sign(Secret const& _key, h256 const& _hash)
return sig;
}
bool Secp256k1::verify(Signature const& _signature, bytesConstRef _message)
bool Secp256k1PP::verify(Signature const& _signature, bytesConstRef _message)
{
return !!recover(_signature, _message);
}
bool Secp256k1::verify(Public const& _p, Signature const& _sig, bytesConstRef _message, bool _hashed)
bool Secp256k1PP::verify(Public const& _p, Signature const& _sig, bytesConstRef _message, bool _hashed)
{
// todo: verify w/o recovery (if faster)
return (bool)_p == _hashed ? (bool)recover(_sig, _message) : (bool)recover(_sig, sha3(_message).ref());
return _p == (_hashed ? recover(_sig, _message) : recover(_sig, sha3(_message).ref()));
}
Public Secp256k1::recover(Signature _signature, bytesConstRef _message)
Public Secp256k1PP::recover(Signature _signature, bytesConstRef _message)
{
Public recovered;
@ -293,7 +293,7 @@ Public Secp256k1::recover(Signature _signature, bytesConstRef _message)
return recovered;
}
bool Secp256k1::verifySecret(Secret const& _s, Public& _p)
bool Secp256k1PP::verifySecret(Secret const& _s, Public& _p)
{
DL_PrivateKey_EC<ECP> k;
k.Initialize(m_params, secretToExponent(_s));
@ -309,7 +309,7 @@ bool Secp256k1::verifySecret(Secret const& _s, Public& _p)
return true;
}
void Secp256k1::agree(Secret const& _s, Public const& _r, h256& o_s)
void Secp256k1PP::agree(Secret const& _s, Public const& _r, h256& o_s)
{
// TODO: mutex ASN1::secp256k1() singleton
// Creating Domain is non-const for m_oid and m_oid is not thread-safe
@ -320,7 +320,7 @@ void Secp256k1::agree(Secret const& _s, Public const& _r, h256& o_s)
d.Agree(o_s.data(), _s.data(), remote);
}
void Secp256k1::exportPublicKey(CryptoPP::DL_PublicKey_EC<CryptoPP::ECP> const& _k, Public& o_p)
void Secp256k1PP::exportPublicKey(CryptoPP::DL_PublicKey_EC<CryptoPP::ECP> const& _k, Public& o_p)
{
bytes prefixedKey(_k.GetGroupParameters().GetEncodedElementSize(true));
@ -333,7 +333,7 @@ void Secp256k1::exportPublicKey(CryptoPP::DL_PublicKey_EC<CryptoPP::ECP> const&
memcpy(o_p.data(), &prefixedKey[1], Public::size);
}
void Secp256k1::exponentToPublic(Integer const& _e, Public& o_p)
void Secp256k1PP::exponentToPublic(Integer const& _e, Public& o_p)
{
CryptoPP::DL_PublicKey_EC<CryptoPP::ECP> pk;

8
libdevcrypto/CryptoPP.h

@ -67,13 +67,11 @@ inline Integer secretToExponent(Secret const& _s) { return std::move(Integer(_s.
* CryptoPP secp256k1 algorithms.
* @todo Collect ECIES methods into class.
*/
class Secp256k1
class Secp256k1PP
{
public:
Secp256k1(): m_oid(ASN1::secp256k1()), m_params(m_oid), m_curve(m_params.GetCurve()), m_q(m_params.GetGroupOrder()), m_qs(m_params.GetSubgroupOrder()) {}
Address toAddress(Public const& _p) { return right160(sha3(_p.ref())); }
Secp256k1PP(): m_oid(ASN1::secp256k1()), m_params(m_oid), m_curve(m_params.GetCurve()), m_q(m_params.GetGroupOrder()), m_qs(m_params.GetSubgroupOrder()) {}
void toPublic(Secret const& _s, Public& o_public) { exponentToPublic(Integer(_s.data(), sizeof(_s)), o_public); }
/// Encrypts text (replace input). (ECIES w/XOR-SHA1)

2
libdevcrypto/ECDHE.cpp

@ -27,7 +27,7 @@ using namespace std;
using namespace dev;
using namespace dev::crypto;
static Secp256k1 s_secp256k1;
static Secp256k1PP s_secp256k1;
void dev::crypto::ecdh::agree(Secret const& _s, Public const& _r, h256& o_s)
{

4
libethash-cl/ethash_cl_miner.cpp

@ -139,6 +139,7 @@ unsigned ethash_cl_miner::getNumDevices(unsigned _platformId)
}
bool ethash_cl_miner::configureGPU(
unsigned _platformId,
bool _allowCPU,
unsigned _extraGPUMemory,
boost::optional<uint64_t> _currentBlock
@ -149,7 +150,7 @@ bool ethash_cl_miner::configureGPU(
// by default let's only consider the DAG of the first epoch
uint64_t dagSize = _currentBlock ? ethash_get_datasize(*_currentBlock) : 1073739904U;
uint64_t requiredSize = dagSize + _extraGPUMemory;
return searchForAllDevices([&requiredSize](cl::Device const _device) -> bool
return searchForAllDevices(_platformId, [&requiredSize](cl::Device const _device) -> bool
{
cl_ulong result;
_device.getInfo(CL_DEVICE_GLOBAL_MEM_SIZE, &result);
@ -416,6 +417,7 @@ bool ethash_cl_miner::init(
void ethash_cl_miner::search(uint8_t const* header, uint64_t target, search_hook& hook, unsigned _msPerBatch)
{
(void)_msPerBatch;
try
{
struct pending_batch

1
libethash-cl/ethash_cl_miner.h

@ -44,6 +44,7 @@ public:
static std::string platform_info(unsigned _platformId = 0, unsigned _deviceId = 0);
static void listDevices();
static bool configureGPU(
unsigned _platformId,
bool _allowCPU,
unsigned _extraGPUMemory,
boost::optional<uint64_t> _currentBlock

2
libethcore/CMakeLists.txt

@ -28,7 +28,7 @@ add_library(${EXECUTABLE} ${SRC_LIST} ${HEADERS})
target_link_libraries(${EXECUTABLE} ethash)
target_link_libraries(${EXECUTABLE} devcrypto)
#target_link_libraries(${EXECUTABLE} evmcore)
target_link_libraries(${EXECUTABLE} evmcore)
if (ETHASHCL)
target_link_libraries(${EXECUTABLE} ethash-cl)

2
libethcore/Ethash.cpp

@ -418,7 +418,7 @@ bool Ethash::GPUMiner::configureGPU(
{
s_platformId = _platformId;
s_deviceId = _deviceId;
return ethash_cl_miner::configureGPU(_allowCPU, _extraGPUMemory, _currentBlock);
return ethash_cl_miner::configureGPU(_platformId, _allowCPU, _extraGPUMemory, _currentBlock);
}
#endif

118
libethcore/Transaction.cpp

@ -0,0 +1,118 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file TransactionBase.cpp
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#include <libdevcore/vector_ref.h>
#include <libdevcore/Log.h>
#include <libdevcore/CommonIO.h>
#include <libdevcrypto/Common.h>
#include <libethcore/Exceptions.h>
#include "Transaction.h"
using namespace std;
using namespace dev;
using namespace dev::eth;
TransactionBase::TransactionBase(bytesConstRef _rlpData, CheckTransaction _checkSig)
{
int field = 0;
RLP rlp(_rlpData);
try
{
if (!rlp.isList())
BOOST_THROW_EXCEPTION(InvalidTransactionFormat() << errinfo_comment("transaction RLP must be a list"));
m_nonce = rlp[field = 0].toInt<u256>();
m_gasPrice = rlp[field = 1].toInt<u256>();
m_gas = rlp[field = 2].toInt<u256>();
m_type = rlp[field = 3].isEmpty() ? ContractCreation : MessageCall;
m_receiveAddress = rlp[field = 3].isEmpty() ? Address() : rlp[field = 3].toHash<Address>(RLP::VeryStrict);
m_value = rlp[field = 4].toInt<u256>();
if (!rlp[field = 5].isData())
BOOST_THROW_EXCEPTION(InvalidTransactionFormat() << errinfo_comment("transaction data RLP must be an array"));
m_data = rlp[field = 5].toBytes();
byte v = rlp[field = 6].toInt<byte>() - 27;
h256 r = rlp[field = 7].toInt<u256>();
h256 s = rlp[field = 8].toInt<u256>();
if (rlp.itemCount() > 9)
BOOST_THROW_EXCEPTION(InvalidTransactionFormat() << errinfo_comment("to many fields in the transaction RLP"));
m_vrs = SignatureStruct{ r, s, v };
if (_checkSig >= CheckTransaction::Cheap && !m_vrs.isValid())
BOOST_THROW_EXCEPTION(InvalidSignature());
if (_checkSig == CheckTransaction::Everything)
m_sender = sender();
}
catch (Exception& _e)
{
_e << errinfo_name("invalid transaction format") << BadFieldError(field, toHex(rlp[field].data().toBytes()));
throw;
}
}
Address const& TransactionBase::safeSender() const noexcept
{
try
{
return sender();
}
catch (...)
{
cwarn << "safeSender() did throw an exception: " << boost::current_exception_diagnostic_information();
return ZeroAddress;
}
}
Address const& TransactionBase::sender() const
{
if (!m_sender)
{
auto p = recover(m_vrs, sha3(WithoutSignature));
if (!p)
BOOST_THROW_EXCEPTION(InvalidSignature());
m_sender = right160(dev::sha3(bytesConstRef(p.data(), sizeof(p))));
}
return m_sender;
}
void TransactionBase::sign(Secret const& _priv)
{
auto sig = dev::sign(_priv, sha3(WithoutSignature));
SignatureStruct sigStruct = *(SignatureStruct const*)&sig;
if (sigStruct.isValid())
m_vrs = sigStruct;
}
void TransactionBase::streamRLP(RLPStream& _s, IncludeSignature _sig) const
{
if (m_type == NullTransaction)
return;
_s.appendList((_sig ? 3 : 0) + 6);
_s << m_nonce << m_gasPrice << m_gas;
if (m_type == MessageCall)
_s << m_receiveAddress;
else
_s << "";
_s << m_value << m_data;
if (_sig)
_s << (m_vrs.v + 27) << (u256)m_vrs.r << (u256)m_vrs.s;
}

177
libethcore/Transaction.h

@ -0,0 +1,177 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file TransactionBase.h
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#pragma once
#include <libdevcore/RLP.h>
#include <libdevcore/SHA3.h>
#include <libethcore/Common.h>
namespace dev
{
namespace eth
{
/// Named-boolean type to encode whether a signature be included in the serialisation process.
enum IncludeSignature
{
WithoutSignature = 0, ///< Do not include a signature.
WithSignature = 1, ///< Do include a signature.
};
enum class CheckTransaction
{
None,
Cheap,
Everything
};
/// Encodes a transaction, ready to be exported to or freshly imported from RLP.
class TransactionBase
{
public:
/// Constructs a null transaction.
TransactionBase() {}
/// Constructs a signed message-call transaction.
TransactionBase(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.
TransactionBase(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.
TransactionBase(u256 const& _value, u256 const& _gasPrice, u256 const& _gas, Address const& _dest, bytes const& _data, u256 const& _nonce = 0): m_type(MessageCall), m_nonce(_nonce), m_value(_value), m_receiveAddress(_dest), m_gasPrice(_gasPrice), m_gas(_gas), m_data(_data) {}
/// Constructs an unsigned contract-creation transaction.
TransactionBase(u256 const& _value, u256 const& _gasPrice, u256 const& _gas, bytes const& _data, u256 const& _nonce = 0): m_type(ContractCreation), m_nonce(_nonce), m_value(_value), m_gasPrice(_gasPrice), m_gas(_gas), m_data(_data) {}
/// Constructs a transaction from the given RLP.
explicit TransactionBase(bytesConstRef _rlp, CheckTransaction _checkSig);
/// Constructs a transaction from the given RLP.
explicit TransactionBase(bytes const& _rlp, CheckTransaction _checkSig): TransactionBase(&_rlp, _checkSig) {}
/// Checks equality of transactions.
bool operator==(TransactionBase const& _c) const { return m_type == _c.m_type && (m_type == ContractCreation || m_receiveAddress == _c.m_receiveAddress) && m_value == _c.m_value && m_data == _c.m_data; }
/// Checks inequality of transactions.
bool operator!=(TransactionBase const& _c) const { return !operator==(_c); }
/// @returns sender of the transaction from the signature (and hash).
Address const& sender() const;
/// Like sender() but will never throw. @returns a null Address if the signature is invalid.
Address const& safeSender() const noexcept;
/// Force the sender to a particular value. This will result in an invalid transaction RLP.
void forceSender(Address const& _a) { m_sender = _a; }
/// @returns true if transaction is non-null.
explicit operator bool() const { return m_type != NullTransaction; }
/// @returns true if transaction is contract-creation.
bool isCreation() const { return m_type == ContractCreation; }
/// @returns true if transaction is message-call.
bool isMessageCall() const { return m_type == MessageCall; }
/// Serialises this transaction to an RLPStream.
void streamRLP(RLPStream& _s, IncludeSignature _sig = WithSignature) const;
/// @returns the RLP serialisation of this transaction.
bytes rlp(IncludeSignature _sig = WithSignature) const { RLPStream s; streamRLP(s, _sig); return s.out(); }
/// @returns the SHA3 hash of the RLP serialisation of this transaction.
h256 sha3(IncludeSignature _sig = WithSignature) const { if (_sig == WithSignature && m_hashWith) return m_hashWith; RLPStream s; streamRLP(s, _sig); auto ret = dev::sha3(s.out()); if (_sig == WithSignature) m_hashWith = ret; return ret; }
/// @returns the amount of ETH to be transferred by this (message-call) transaction, in Wei. Synonym for endowment().
u256 value() const { return m_value; }
/// @returns the amount of ETH to be endowed by this (contract-creation) transaction, in Wei. Synonym for value().
u256 endowment() const { return m_value; }
/// @returns the base fee and thus the implied exchange rate of ETH to GAS.
u256 gasPrice() const { return m_gasPrice; }
/// @returns the total gas to convert, paid for from sender's account. Any unused gas gets refunded once the contract is ended.
u256 gas() const { return m_gas; }
/// @returns the receiving address of the message-call transaction (undefined for contract-creation transactions).
Address receiveAddress() const { return m_receiveAddress; }
/// Synonym for receiveAddress().
Address to() const { return m_receiveAddress; }
/// Synonym for safeSender().
Address from() const { return safeSender(); }
/// @returns the data associated with this (message-call) transaction. Synonym for initCode().
bytes const& data() const { return m_data; }
/// @returns the initialisation code associated with this (contract-creation) transaction. Synonym for data().
bytes const& initCode() const { return m_data; }
/// @returns the transaction-count of the sender.
u256 nonce() const { return m_nonce; }
/// @returns the signature of the transaction. Encodes the sender.
SignatureStruct const& signature() const { return m_vrs; }
void sign(Secret const& _priv); ///< Sign the transaction.
protected:
/// Type of transaction.
enum Type
{
NullTransaction, ///< Null transaction.
ContractCreation, ///< Transaction to create contracts - receiveAddress() is ignored.
MessageCall ///< Transaction to invoke a message call - receiveAddress() is used.
};
Type m_type = NullTransaction; ///< Is this a contract-creation transaction or a message-call transaction?
u256 m_nonce; ///< The transaction-count of the sender.
u256 m_value; ///< The amount of ETH to be transferred by this transaction. Called 'endowment' for contract-creation transactions.
Address m_receiveAddress; ///< The receiving address of the transaction.
u256 m_gasPrice; ///< The base fee and thus the implied exchange rate of ETH to GAS.
u256 m_gas; ///< The total gas to convert, paid for from sender's account. Any unused gas gets refunded once the contract is ended.
bytes m_data; ///< The data associated with the transaction, or the initialiser if it's a creation transaction.
SignatureStruct m_vrs; ///< The signature of the transaction. Encodes the sender.
mutable h256 m_hashWith; ///< Cached hash of transaction with signature.
mutable Address m_sender; ///< Cached sender, determined from signature.
mutable bigint m_gasRequired = 0; ///< Memoised amount required for the transaction to run.
};
/// Nice name for vector of Transaction.
using TransactionBases = std::vector<TransactionBase>;
/// Simple human-readable stream-shift operator.
inline std::ostream& operator<<(std::ostream& _out, TransactionBase const& _t)
{
_out << _t.sha3().abridged() << "{";
if (_t.receiveAddress())
_out << _t.receiveAddress().abridged();
else
_out << "[CREATE]";
_out << "/" << _t.data().size() << "$" << _t.value() << "+" << _t.gas() << "@" << _t.gasPrice();
_out << "<-" << _t.safeSender().abridged() << " #" << _t.nonce() << "}";
return _out;
}
}
}

53
libethereum/BlockChain.cpp

@ -24,6 +24,7 @@
#if ETH_PROFILING_GPERF
#include <gperftools/profiler.h>
#endif
#include <boost/timer.hpp>
#include <boost/filesystem.hpp>
#include <test/JsonSpiritHeaders.h>
@ -96,6 +97,15 @@ ldb::Slice dev::eth::toSlice(h256 const& _h, unsigned _sub)
#endif
}
namespace dev
{
class WriteBatchNoter: public ldb::WriteBatch::Handler
{
virtual void Put(ldb::Slice const& _key, ldb::Slice const& _value) { cnote << "Put" << toHex(bytesConstRef(_key)) << "=>" << toHex(bytesConstRef(_value)); }
virtual void Delete(ldb::Slice const& _key) { cnote << "Delete" << toHex(bytesConstRef(_key)); }
};
}
#if ETH_DEBUG&&0
static const chrono::system_clock::duration c_collectionDuration = chrono::seconds(15);
static const unsigned c_collectionQueueSize = 2;
@ -282,15 +292,6 @@ void BlockChain::rebuild(std::string const& _path, std::function<void(unsigned,
boost::filesystem::remove_all(path + "/details.old");
}
template <class T, class V>
bool contains(T const& _t, V const& _v)
{
for (auto const& i: _t)
if (i == _v)
return true;
return false;
}
LastHashes BlockChain::lastHashes(unsigned _n) const
{
Guard l(x_lastLastHashes);
@ -324,7 +325,7 @@ tuple<ImportRoute, bool, unsigned> BlockChain::sync(BlockQueue& _bq, OverlayDB c
{
// Nonce & uncle nonces already verified in verification thread at this point.
ImportRoute r;
DEV_TIMED_ABOVE("Block import", 500)
DEV_TIMED_ABOVE("Block import " + toString(block.verified.info.number), 500)
r = import(block.verified, _stateDB, ImportRequirements::Default & ~ImportRequirements::ValidNonce & ~ImportRequirements::CheckUncles);
fresh += r.liveBlocks;
dead += r.deadBlocks;
@ -532,7 +533,7 @@ ImportRoute BlockChain::import(VerifiedBlockRef const& _block, OverlayDB const&
#endif
}
#if ETH_CATCH
catch (BadRoot& ex)
catch (BadRoot&)
{
cwarn << "BadRoot error. Retrying import later.";
BOOST_THROW_EXCEPTION(FutureTime());
@ -638,8 +639,25 @@ ImportRoute BlockChain::import(VerifiedBlockRef const& _block, OverlayDB const&
clog(BlockChainChat) << " Imported but not best (oTD:" << details(last).totalDifficulty << " > TD:" << td << ")";
}
m_blocksDB->Write(m_writeOptions, &blocksBatch);
m_extrasDB->Write(m_writeOptions, &extrasBatch);
ldb::Status o = m_blocksDB->Write(m_writeOptions, &blocksBatch);
if (!o.ok())
{
cwarn << "Error writing to blockchain database: " << o.ToString();
WriteBatchNoter n;
blocksBatch.Iterate(&n);
cwarn << "Fail writing to blockchain database. Bombing out.";
exit(-1);
}
o = m_extrasDB->Write(m_writeOptions, &extrasBatch);
if (!o.ok())
{
cwarn << "Error writing to extras database: " << o.ToString();
WriteBatchNoter n;
extrasBatch.Iterate(&n);
cwarn << "Fail writing to extras database. Bombing out.";
exit(-1);
}
#if ETH_PARANOIA || !ETH_TRUE
if (isKnown(_block.info.hash()) && !details(_block.info.hash()))
@ -667,7 +685,14 @@ ImportRoute BlockChain::import(VerifiedBlockRef const& _block, OverlayDB const&
{
m_lastBlockHash = newLastBlockHash;
m_lastBlockNumber = newLastBlockNumber;
m_extrasDB->Put(m_writeOptions, ldb::Slice("best"), ldb::Slice((char const*)&m_lastBlockHash, 32));
o = m_extrasDB->Put(m_writeOptions, ldb::Slice("best"), ldb::Slice((char const*)&m_lastBlockHash, 32));
if (!o.ok())
{
cwarn << "Error writing to extras database: " << o.ToString();
cout << "Put" << toHex(bytesConstRef(ldb::Slice("best"))) << "=>" << toHex(bytesConstRef(ldb::Slice((char const*)&m_lastBlockHash, 32)));
cwarn << "Fail writing to extras database. Bombing out.";
exit(-1);
}
}
#if ETH_PARANOIA || !ETH_TRUE

5
libethereum/BlockChainSync.cpp

@ -534,7 +534,7 @@ bool PV60Sync::shouldGrabBlocks(std::shared_ptr<EthereumPeer> _peer) const
void PV60Sync::attemptSync(std::shared_ptr<EthereumPeer> _peer)
{
if (m_state != SyncState::Idle)
if (m_state != SyncState::Idle || _peer->m_asking != Asking::Nothing)
{
clog(NetAllDetail) << "Can't sync with this peer - outstanding asks.";
return;
@ -649,9 +649,8 @@ void PV60Sync::noteDoneBlocks(std::shared_ptr<EthereumPeer> _peer, bool _clemenc
}
resetSync();
downloadMan().reset();
transition(_peer, SyncState::Idle);
}
_peer->m_sub.doneFetch();
}
}
void PV60Sync::onPeerHashes(std::shared_ptr<EthereumPeer> _peer, h256s const& _hashes)

65
libethereum/BlockQueue.cpp

@ -102,7 +102,7 @@ void BlockQueue::verifierBody()
BlockInfo bi;
bi.mixHash = work.hash;
bi.parentHash = work.parentHash;
m_verifying.push_back(VerifiedBlock { VerifiedBlockRef { bytesConstRef(), move(bi), Transactions() }, bytes() });
m_verifying.emplace_back(move(bi));
}
VerifiedBlock res;
@ -114,15 +114,11 @@ void BlockQueue::verifierBody()
catch (...)
{
// bad block.
{
// has to be this order as that's how invariants() assumes.
WriteGuard l2(m_lock);
unique_lock<Mutex> l(m_verification);
m_readySet.erase(work.hash);
m_knownBad.insert(work.hash);
}
// has to be this order as that's how invariants() assumes.
WriteGuard l2(m_lock);
unique_lock<Mutex> l(m_verification);
m_readySet.erase(work.hash);
m_knownBad.insert(work.hash);
for (auto it = m_verifying.begin(); it != m_verifying.end(); ++it)
if (it->verified.info.mixHash == work.hash)
{
@ -131,6 +127,7 @@ void BlockQueue::verifierBody()
}
cwarn << "BlockQueue missing our job: was there a GM?";
OK1:;
drainVerified_WITH_BOTH_LOCKS();
continue;
}
@ -148,18 +145,9 @@ void BlockQueue::verifierBody()
m_knownBad.insert(res.verified.info.hash());
}
else
m_verified.push_back(move(res));
while (m_verifying.size() && !m_verifying.front().blockData.empty())
{
if (m_knownBad.count(m_verifying.front().verified.info.parentHash))
{
m_readySet.erase(m_verifying.front().verified.info.hash());
m_knownBad.insert(res.verified.info.hash());
}
else
m_verified.push_back(move(m_verifying.front()));
m_verifying.pop_front();
}
m_verified.emplace_back(move(res));
drainVerified_WITH_BOTH_LOCKS();
ready = true;
}
else
@ -179,8 +167,24 @@ void BlockQueue::verifierBody()
}
}
void BlockQueue::drainVerified_WITH_BOTH_LOCKS()
{
while (!m_verifying.empty() && !m_verifying.front().blockData.empty())
{
if (m_knownBad.count(m_verifying.front().verified.info.parentHash))
{
m_readySet.erase(m_verifying.front().verified.info.hash());
m_knownBad.insert(m_verifying.front().verified.info.hash());
}
else
m_verified.emplace_back(move(m_verifying.front()));
m_verifying.pop_front();
}
}
ImportResult BlockQueue::import(bytesConstRef _block, BlockChain const& _bc, bool _isOurs)
{
cdebug << std::this_thread::get_id();
// Check if we already know this block.
h256 h = BlockInfo::headerHash(_block);
@ -242,7 +246,7 @@ ImportResult BlockQueue::import(bytesConstRef _block, BlockChain const& _bc, boo
if (m_knownBad.count(bi.parentHash))
{
m_knownBad.insert(bi.hash());
updateBad(bi.hash());
updateBad_WITH_LOCK(bi.hash());
// bad parent; this is bad too, note it as such
return ImportResult::BadChain;
}
@ -277,12 +281,12 @@ ImportResult BlockQueue::import(bytesConstRef _block, BlockChain const& _bc, boo
}
}
void BlockQueue::updateBad(h256 const& _bad)
void BlockQueue::updateBad_WITH_LOCK(h256 const& _bad)
{
DEV_INVARIANT_CHECK;
DEV_GUARDED(m_verification)
{
collectUnknownBad(_bad);
collectUnknownBad_WITH_BOTH_LOCKS(_bad);
bool moreBad = true;
while (moreBad)
{
@ -294,7 +298,7 @@ void BlockQueue::updateBad(h256 const& _bad)
{
m_knownBad.insert(b.verified.info.hash());
m_readySet.erase(b.verified.info.hash());
collectUnknownBad(b.verified.info.hash());
collectUnknownBad_WITH_BOTH_LOCKS(b.verified.info.hash());
moreBad = true;
}
else
@ -307,7 +311,7 @@ void BlockQueue::updateBad(h256 const& _bad)
{
m_knownBad.insert(b.hash);
m_readySet.erase(b.hash);
collectUnknownBad(b.hash);
collectUnknownBad_WITH_BOTH_LOCKS(b.hash);
moreBad = true;
}
else
@ -321,17 +325,16 @@ void BlockQueue::updateBad(h256 const& _bad)
h256 const& h = b.blockData.size() != 0 ? b.verified.info.hash() : b.verified.info.mixHash;
m_knownBad.insert(h);
m_readySet.erase(h);
collectUnknownBad(h);
collectUnknownBad_WITH_BOTH_LOCKS(h);
moreBad = true;
}
else
m_verifying.push_back(std::move(b));
}
}
DEV_INVARIANT_CHECK;
}
void BlockQueue::collectUnknownBad(h256 const& _bad)
void BlockQueue::collectUnknownBad_WITH_BOTH_LOCKS(h256 const& _bad)
{
list<h256> badQueue(1, _bad);
while (!badQueue.empty())
@ -349,7 +352,6 @@ void BlockQueue::collectUnknownBad(h256 const& _bad)
}
m_unknown.erase(r.first, r.second);
}
}
bool BlockQueue::doneDrain(h256s const& _bad)
@ -364,7 +366,7 @@ bool BlockQueue::doneDrain(h256s const& _bad)
// at least one of them was bad.
m_knownBad += _bad;
for (h256 const& b : _bad)
updateBad(b);
updateBad_WITH_LOCK(b);
}
return !m_readySet.empty();
}
@ -501,7 +503,6 @@ void BlockQueue::noteReady_WITH_LOCK(h256 const& _good)
}
if (notify)
m_moreToVerify.notify_all();
DEV_INVARIANT_CHECK;
}
void BlockQueue::retryAllUnknown()

5
libethereum/BlockQueue.h

@ -134,8 +134,9 @@ private:
bool invariants() const override;
void verifierBody();
void collectUnknownBad(h256 const& _bad);
void updateBad(h256 const& _bad);
void collectUnknownBad_WITH_BOTH_LOCKS(h256 const& _bad);
void updateBad_WITH_LOCK(h256 const& _bad);
void drainVerified_WITH_BOTH_LOCKS();
mutable boost::shared_mutex m_lock; ///< General lock for the sets, m_future and m_unknown.
h256Hash m_drainingSet; ///< All blocks being imported.

1
libethereum/CMakeLists.txt

@ -32,7 +32,6 @@ target_link_libraries(${EXECUTABLE} p2p)
target_link_libraries(${EXECUTABLE} devcrypto)
target_link_libraries(${EXECUTABLE} ethcore)
target_link_libraries(${EXECUTABLE} ${Boost_REGEX_LIBRARIES})
target_link_libraries(${EXECUTABLE} secp256k1)
if (JSONRPC)
target_link_libraries(${EXECUTABLE} ${JSON_RPC_CPP_CLIENT_LIBRARIES})
target_link_libraries(${EXECUTABLE} ${CURL_LIBRARIES})

21
libethereum/Client.cpp

@ -89,13 +89,14 @@ void VersionChecker::setOk()
ImportResult Client::queueBlock(bytes const& _block, bool _isSafe)
{
if (m_bq.status().verified + m_bq.status().verifying + m_bq.status().unverified > 30000)
if (m_bq.status().verified + m_bq.status().verifying + m_bq.status().unverified > 10000)
this_thread::sleep_for(std::chrono::milliseconds(500));
return m_bq.import(&_block, bc(), _isSafe);
}
tuple<ImportRoute, bool, unsigned> Client::syncQueue(unsigned _max)
{
stopWorking();
return m_bc.sync(m_bq, m_stateDB, _max);
}
@ -338,16 +339,26 @@ Client::~Client()
stopWorking();
}
static const Address c_canary("0x");
static const Addresses c_canaries =
{
Address("4bb7e8ae99b645c2b7860b8f3a2328aae28bd80a"), // gav
Address("1baf27b88c48dd02b744999cf3522766929d2b2a"), // vitalik
Address("a8edb1ac2c86d3d9d78f96cd18001f60df29e52c"), // jeff
Address("60d11b58744784dc97f878f7e3749c0f1381a004") // christoph
};
bool Client::isChainBad() const
{
return stateAt(c_canary, 0) != 0;
unsigned numberBad = 0;
for (auto const& a: c_canaries)
if (!!stateAt(a, 0))
numberBad++;
return numberBad >= 2;
}
bool Client::isUpgradeNeeded() const
{
return stateAt(c_canary, 0) == 2;
return stateAt(c_canaries[0], 0) == 2;
}
void Client::setNetworkId(u256 _n)
@ -792,7 +803,7 @@ void Client::rejigMining()
{
clog(ClientTrace) << "Rejigging mining...";
DEV_WRITE_GUARDED(x_working)
m_working.commitToMine(m_bc);
m_working.commitToMine(m_bc, m_extraData);
DEV_READ_GUARDED(x_working)
{
DEV_WRITE_GUARDED(x_postMine)

5
libethereum/Client.h

@ -240,6 +240,10 @@ public:
ActivityReport activityReport() { ActivityReport ret; std::swap(m_report, ret); return ret; }
/// Set a JSONRPC server to which we can report bad blocks.
void setSentinel(std::string const& _server) { m_sentinel = _server; }
/// Get the JSONRPC server to which we report bad blocks.
std::string const& sentinel() const { return m_sentinel; }
/// Set the extra data that goes into mined blocks.
void setExtraData(bytes const& _extraData) { m_extraData = _extraData; }
protected:
/// InterfaceStub methods
@ -357,6 +361,7 @@ private:
std::atomic<bool> m_syncBlockQueue = {false};
std::string m_sentinel;
bytes m_extraData;
};
}

4
libethereum/EthereumPeer.cpp

@ -86,7 +86,7 @@ unsigned EthereumPeer::askOverride() const
if (s->info().clientVersion.substr(0, badGeth.size()) == badGeth)
return 1;
bytes const& d = repMan().data(*s, name());
return d.empty() ? c_maxBlocksAsk : RLP(d).toInt<unsigned>(RLP::LaisezFaire);
return d.empty() ? c_maxBlocksAsk : RLP(d).toInt<unsigned>(RLP::LaissezFaire);
}
void EthereumPeer::setRude()
@ -152,6 +152,8 @@ void EthereumPeer::requestHashes(u256 _number, unsigned _count)
void EthereumPeer::requestHashes(h256 const& _lastHash)
{
if (m_asking != Asking::Nothing)
clog(NetWarn) << "Asking hashes while requesting " << (m_asking == Asking::Nothing ? "nothing" : m_asking == Asking::State ? "state" : m_asking == Asking::Hashes ? "hashes" : m_asking == Asking::Blocks ? "blocks" : "?");
assert(m_asking == Asking::Nothing);
setAsking(Asking::Hashes);
RLPStream s;

6
libethereum/State.cpp

@ -25,7 +25,6 @@
#include <random>
#include <boost/filesystem.hpp>
#include <boost/timer.hpp>
#include <secp256k1/secp256k1.h>
#include <libdevcore/CommonIO.h>
#include <libdevcore/Assertions.h>
#include <libdevcore/StructuredLogger.h>
@ -117,7 +116,7 @@ State::State(OverlayDB const& _db, BaseState _bs, Address _coinbaseAddress):
PopulationStatistics State::populateFromChain(BlockChain const& _bc, h256 const& _h, ImportRequirements::value _ir)
{
PopulationStatistics ret;
PopulationStatistics ret { 0.0, 0.0 };
if (!_bc.isKnown(_h))
{
@ -884,7 +883,7 @@ LogBloom State::logBloom() const
return ret;
}
void State::commitToMine(BlockChain const& _bc)
void State::commitToMine(BlockChain const& _bc, bytes const& _extraData)
{
uncommitToMine();
@ -967,6 +966,7 @@ void State::commitToMine(BlockChain const& _bc)
m_currentBlock.gasUsed = gasUsed();
m_currentBlock.stateRoot = m_state.root();
m_currentBlock.parentHash = m_previousBlock.hash();
m_currentBlock.extraData = _extraData;
m_committedToMine = true;
}

2
libethereum/State.h

@ -194,7 +194,7 @@ public:
/// The only thing left to do after this is to actually mine().
///
/// This may be called multiple times and without issue.
void commitToMine(BlockChain const& _bc);
void commitToMine(BlockChain const& _bc, bytes const& _extraData = {});
/// @returns true iff commitToMine() has been called without any subsequest transactions added &c.
bool isCommittedToMine() const { return m_committedToMine; }

87
libethereum/Transaction.cpp

@ -94,99 +94,16 @@ std::ostream& dev::eth::operator<<(std::ostream& _out, TransactionException cons
return _out;
}
Transaction::Transaction(bytesConstRef _rlpData, CheckTransaction _checkSig)
Transaction::Transaction(bytesConstRef _rlpData, CheckTransaction _checkSig):
TransactionBase(_rlpData, _checkSig)
{
int field = 0;
RLP rlp(_rlpData);
try
{
if (!rlp.isList())
BOOST_THROW_EXCEPTION(InvalidTransactionFormat() << errinfo_comment("transaction RLP must be a list"));
m_nonce = rlp[field = 0].toInt<u256>();
m_gasPrice = rlp[field = 1].toInt<u256>();
m_gas = rlp[field = 2].toInt<u256>();
m_type = rlp[field = 3].isEmpty() ? ContractCreation : MessageCall;
m_receiveAddress = rlp[field = 3].isEmpty() ? Address() : rlp[field = 3].toHash<Address>(RLP::VeryStrict);
m_value = rlp[field = 4].toInt<u256>();
if (!rlp[field = 5].isData())
BOOST_THROW_EXCEPTION(InvalidTransactionFormat() << errinfo_comment("transaction data RLP must be an array"));
m_data = rlp[field = 5].toBytes();
byte v = rlp[field = 6].toInt<byte>() - 27;
h256 r = rlp[field = 7].toInt<u256>();
h256 s = rlp[field = 8].toInt<u256>();
if (rlp.itemCount() > 9)
BOOST_THROW_EXCEPTION(InvalidTransactionFormat() << errinfo_comment("to many fields in the transaction RLP"));
m_vrs = SignatureStruct{ r, s, v };
if (_checkSig >= CheckTransaction::Cheap && !m_vrs.isValid())
BOOST_THROW_EXCEPTION(InvalidSignature());
if (_checkSig == CheckTransaction::Everything)
m_sender = sender();
}
catch (Exception& _e)
{
_e << errinfo_name("invalid transaction format") << BadFieldError(field, toHex(rlp[field].data().toBytes()));
throw;
}
if (_checkSig >= CheckTransaction::Cheap && !checkPayment())
BOOST_THROW_EXCEPTION(OutOfGasIntrinsic() << RequirementError(gasRequired(), (bigint)gas()));
}
Address const& Transaction::safeSender() const noexcept
{
try
{
return sender();
}
catch (...)
{
cwarn << "safeSender() did throw an exception: " << boost::current_exception_diagnostic_information();
return ZeroAddress;
}
}
Address const& Transaction::sender() const
{
if (!m_sender)
{
auto p = recover(m_vrs, sha3(WithoutSignature));
if (!p)
BOOST_THROW_EXCEPTION(InvalidSignature());
m_sender = right160(dev::sha3(bytesConstRef(p.data(), sizeof(p))));
}
return m_sender;
}
bigint Transaction::gasRequired() const
{
if (!m_gasRequired)
m_gasRequired = Transaction::gasRequired(m_data);
return m_gasRequired;
}
void Transaction::sign(Secret _priv)
{
auto sig = dev::sign(_priv, sha3(WithoutSignature));
SignatureStruct sigStruct = *(SignatureStruct const*)&sig;
if (sigStruct.isValid())
m_vrs = sigStruct;
}
void Transaction::streamRLP(RLPStream& _s, IncludeSignature _sig) const
{
if (m_type == NullTransaction)
return;
_s.appendList((_sig ? 3 : 0) + 6);
_s << m_nonce << m_gasPrice << m_gas;
if (m_type == MessageCall)
_s << m_receiveAddress;
else
_s << "";
_s << m_value << m_data;
if (_sig)
_s << (m_vrs.v + 27) << (u256)m_vrs.r << (u256)m_vrs.s;
}

126
libethereum/Transaction.h

@ -24,6 +24,7 @@
#include <libdevcore/RLP.h>
#include <libdevcore/SHA3.h>
#include <libethcore/Common.h>
#include <libethcore/Transaction.h>
#include <libevmcore/Params.h>
namespace dev
@ -31,20 +32,6 @@ namespace dev
namespace eth
{
/// Named-boolean type to encode whether a signature be included in the serialisation process.
enum IncludeSignature
{
WithoutSignature = 0, ///< Do not include a signature.
WithSignature = 1, ///< Do include a signature.
};
enum class CheckTransaction
{
None,
Cheap,
Everything
};
enum class TransactionException
{
None = 0,
@ -92,23 +79,13 @@ struct ExecutionResult
std::ostream& operator<<(std::ostream& _out, ExecutionResult const& _er);
/// Encodes a transaction, ready to be exported to or freshly imported from RLP.
class Transaction
class Transaction: public TransactionBase
{
public:
/// Constructs a null transaction.
Transaction() {}
/// Constructs a signed message-call transaction.
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 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 const& _value, u256 const& _gasPrice, u256 const& _gas, Address const& _dest, bytes const& _data, u256 const& _nonce = 0): m_type(MessageCall), m_nonce(_nonce), m_value(_value), m_receiveAddress(_dest), m_gasPrice(_gasPrice), m_gas(_gas), m_data(_data) {}
/// Constructs an unsigned contract-creation transaction.
Transaction(u256 const& _value, u256 const& _gasPrice, u256 const& _gas, bytes const& _data, u256 const& _nonce = 0): m_type(ContractCreation), m_nonce(_nonce), m_value(_value), m_gasPrice(_gasPrice), m_gas(_gas), m_data(_data) {}
using TransactionBase::TransactionBase;
/// Constructs a transaction from the given RLP.
explicit Transaction(bytesConstRef _rlp, CheckTransaction _checkSig);
@ -116,68 +93,6 @@ public:
/// Constructs a transaction from the given RLP.
explicit Transaction(bytes const& _rlp, CheckTransaction _checkSig): Transaction(&_rlp, _checkSig) {}
/// Checks equality of transactions.
bool operator==(Transaction const& _c) const { return m_type == _c.m_type && (m_type == ContractCreation || m_receiveAddress == _c.m_receiveAddress) && m_value == _c.m_value && m_data == _c.m_data; }
/// Checks inequality of transactions.
bool operator!=(Transaction const& _c) const { return !operator==(_c); }
/// @returns sender of the transaction from the signature (and hash).
Address const& sender() const;
/// Like sender() but will never throw. @returns a null Address if the signature is invalid.
Address const& safeSender() const noexcept;
/// Force the sender to a particular value. This will result in an invalid transaction RLP.
void forceSender(Address const& _a) { m_sender = _a; }
/// @returns true if transaction is non-null.
explicit operator bool() const { return m_type != NullTransaction; }
/// @returns true if transaction is contract-creation.
bool isCreation() const { return m_type == ContractCreation; }
/// @returns true if transaction is message-call.
bool isMessageCall() const { return m_type == MessageCall; }
/// Serialises this transaction to an RLPStream.
void streamRLP(RLPStream& _s, IncludeSignature _sig = WithSignature) const;
/// @returns the RLP serialisation of this transaction.
bytes rlp(IncludeSignature _sig = WithSignature) const { RLPStream s; streamRLP(s, _sig); return s.out(); }
/// @returns the SHA3 hash of the RLP serialisation of this transaction.
h256 sha3(IncludeSignature _sig = WithSignature) const { if (_sig == WithSignature && m_hashWith) return m_hashWith; RLPStream s; streamRLP(s, _sig); auto ret = dev::sha3(s.out()); if (_sig == WithSignature) m_hashWith = ret; return ret; }
/// @returns the amount of ETH to be transferred by this (message-call) transaction, in Wei. Synonym for endowment().
u256 value() const { return m_value; }
/// @returns the amount of ETH to be endowed by this (contract-creation) transaction, in Wei. Synonym for value().
u256 endowment() const { return m_value; }
/// @returns the base fee and thus the implied exchange rate of ETH to GAS.
u256 gasPrice() const { return m_gasPrice; }
/// @returns the total gas to convert, paid for from sender's account. Any unused gas gets refunded once the contract is ended.
u256 gas() const { return m_gas; }
/// @returns the receiving address of the message-call transaction (undefined for contract-creation transactions).
Address receiveAddress() const { return m_receiveAddress; }
/// Synonym for receiveAddress().
Address to() const { return m_receiveAddress; }
/// Synonym for safeSender().
Address from() const { return safeSender(); }
/// @returns the data associated with this (message-call) transaction. Synonym for initCode().
bytes const& data() const { return m_data; }
/// @returns the initialisation code associated with this (contract-creation) transaction. Synonym for data().
bytes const& initCode() const { return m_data; }
/// @returns the transaction-count of the sender.
u256 nonce() const { return m_nonce; }
/// @returns the signature of the transaction. Encodes the sender.
SignatureStruct const& signature() const { return m_vrs; }
/// @returns true if the transaction contains enough gas for the basic payment.
bool checkPayment() const { return m_gas >= gasRequired(); }
@ -188,46 +103,11 @@ public:
template <class T> static bigint gasRequired(T const& _data, u256 _gas = 0) { bigint ret = c_txGas + _gas; for (auto i: _data) ret += i ? c_txDataNonZeroGas : c_txDataZeroGas; return ret; }
private:
/// Type of transaction.
enum Type
{
NullTransaction, ///< Null transaction.
ContractCreation, ///< Transaction to create contracts - receiveAddress() is ignored.
MessageCall ///< Transaction to invoke a message call - receiveAddress() is used.
};
void sign(Secret _priv); ///< Sign the transaction.
Type m_type = NullTransaction; ///< Is this a contract-creation transaction or a message-call transaction?
u256 m_nonce; ///< The transaction-count of the sender.
u256 m_value; ///< The amount of ETH to be transferred by this transaction. Called 'endowment' for contract-creation transactions.
Address m_receiveAddress; ///< The receiving address of the transaction.
u256 m_gasPrice; ///< The base fee and thus the implied exchange rate of ETH to GAS.
u256 m_gas; ///< The total gas to convert, paid for from sender's account. Any unused gas gets refunded once the contract is ended.
bytes m_data; ///< The data associated with the transaction, or the initialiser if it's a creation transaction.
SignatureStruct m_vrs; ///< The signature of the transaction. Encodes the sender.
mutable h256 m_hashWith; ///< Cached hash of transaction with signature.
mutable Address m_sender; ///< Cached sender, determined from signature.
mutable bigint m_gasRequired = 0; ///< Memoised amount required for the transaction to run.
};
/// Nice name for vector of Transaction.
using Transactions = std::vector<Transaction>;
/// Simple human-readable stream-shift operator.
inline std::ostream& operator<<(std::ostream& _out, Transaction const& _t)
{
_out << _t.sha3().abridged() << "{";
if (_t.receiveAddress())
_out << _t.receiveAddress().abridged();
else
_out << "[CREATE]";
_out << "/" << _t.data().size() << "$" << _t.value() << "+" << _t.gas() << "@" << _t.gasPrice();
_out << "<-" << _t.safeSender().abridged() << " #" << _t.nonce() << "}";
return _out;
}
}
}

1
libethereum/TransactionQueue.cpp

@ -220,6 +220,7 @@ bool TransactionQueue::remove_WITH_LOCK(h256 const& _txHash)
unsigned TransactionQueue::waiting(Address const& _a) const
{
ReadGuard l(m_lock);
auto it = m_senders.equal_range(_a);
unsigned ret = 0;
for (auto i = it.first; i != it.second; ++i, ++ret) {}

24
libethereum/VerifiedBlock.h

@ -43,8 +43,32 @@ struct VerifiedBlockRef
/// @brief Verified block info, combines block data and verified info/transactions
struct VerifiedBlock
{
VerifiedBlock() {};
VerifiedBlock(BlockInfo&& _bi)
{
verified.info = _bi;
}
VerifiedBlock(VerifiedBlock&& _other):
verified(std::move(_other.verified)),
blockData(std::move(_other.blockData))
{
}
VerifiedBlock& operator=(VerifiedBlock&& _other)
{
verified = (std::move(_other.verified));
blockData = (std::move(_other.blockData));
return *this;
}
VerifiedBlockRef verified; ///< Verified block structures
bytes blockData; ///< Block data
private:
VerifiedBlock(VerifiedBlock const&) = delete;
VerifiedBlock operator=(VerifiedBlock const&) = delete;
};
using VerifiedBlocks = std::vector<VerifiedBlock>;

1
libethereumx/CMakeLists.txt

@ -24,7 +24,6 @@ target_link_libraries(${EXECUTABLE} ethereum)
target_link_libraries(${EXECUTABLE} evm)
target_link_libraries(${EXECUTABLE} lll)
target_link_libraries(${EXECUTABLE} ethcore)
target_link_libraries(${EXECUTABLE} secp256k1)
install( TARGETS ${EXECUTABLE} RUNTIME DESTINATION bin ARCHIVE DESTINATION lib LIBRARY DESTINATION lib )
install( FILES ${HEADERS} DESTINATION include/${EXECUTABLE} )

2
libevm/SmartVM.cpp

@ -47,7 +47,7 @@ bytesConstRef SmartVM::execImpl(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _
auto vmKind = VMKind::Interpreter; // default VM
// Jitted EVM code already in memory?
if (evmjit::JIT::isCodeReady(eth2llvm(codeHash)))
if (evmjit::JIT::isCodeReady(eth2jit(codeHash)))
{
cnote << "Jitted";
vmKind = VMKind::JIT;

1
libjsqrc/ethereumjs/.travis.yml

@ -11,7 +11,6 @@ script:
- "jshint *.js lib"
after_script:
- npm run-script build
- npm run-script karma
- npm run-script test-coveralls
- cd test/node && npm install && node app.js

2
libjsqrc/ethereumjs/bower.json

@ -1,7 +1,7 @@
{
"name": "web3",
"namespace": "ethereum",
"version": "0.6.0",
"version": "0.7.1",
"description": "Ethereum Compatible JavaScript API",
"main": [
"./dist/web3.js",

312
libjsqrc/ethereumjs/dist/web3-light.js

@ -263,6 +263,13 @@ var coder = new SolidityCoder([
inputFormatter: f.formatInputBytes,
outputFormatter: f.formatOutputBytes
}),
new SolidityType({
name: 'string',
match: 'strict',
mode: 'bytes',
inputFormatter: f.formatInputString,
outputFormatter: f.formatOutputString
}),
new SolidityType({
name: 'real',
match: 'prefix',
@ -328,26 +335,43 @@ var formatInputInt = function (value) {
};
/**
* Formats input value to byte representation of string
* Formats input bytes
*
* @method formatInputBytes
* @param {String}
* @returns {SolidityParam}
*/
var formatInputBytes = function (value) {
var result = utils.fromAscii(value, c.ETH_PADDING).substr(2);
var result = utils.padRight(utils.toHex(value).substr(2), 64);
return new SolidityParam(result);
};
/**
* Formats input value to byte representation of string
* Formats input bytes
*
* @method formatInputDynamicBytes
* @method formatDynamicInputBytes
* @param {String}
* @returns {SolidityParam}
*/
var formatInputDynamicBytes = function (value) {
var result = utils.fromAscii(value, c.ETH_PADDING).substr(2);
value = utils.toHex(value).substr(2);
var l = Math.floor((value.length + 63) / 64);
var result = utils.padRight(value, l * 64);
var length = Math.floor(value.length / 2);
return new SolidityParam(formatInputInt(length).value + result, 32);
};
/**
* Formats input value to byte representation of string
*
* @method formatInputString
* @param {String}
* @returns {SolidityParam}
*/
var formatInputString = function (value) {
var result = utils.fromAscii(value).substr(2);
var l = Math.floor((result.length + 63) / 64);
result = utils.padRight(result, l * 64);
return new SolidityParam(formatInputInt(value.length).value + result, 32);
};
@ -450,27 +474,38 @@ var formatOutputBool = function (param) {
};
/**
* Should be used to format output string
* Should be used to format output bytes
*
* @method formatOutputBytes
* @param {SolidityParam} left-aligned hex representation of string
* @returns {String} ascii string
* @returns {String} hex string
*/
var formatOutputBytes = function (param) {
// length might also be important!
return utils.toAscii(param.staticPart());
return '0x' + param.staticPart();
};
/**
* Should be used to format output string
* Should be used to format output bytes
*
* @method formatOutputDynamicBytes
* @param {SolidityParam} left-aligned hex representation of string
* @returns {String} ascii string
* @returns {String} hex string
*/
var formatOutputDynamicBytes = function (param) {
// length might also be important!
return utils.toAscii(param.dynamicPart().slice(64));
var length = (new BigNumber(param.dynamicPart().slice(0, 64), 16)).toNumber() * 2;
return '0x' + param.dynamicPart().substr(64, length);
};
/**
* Should be used to format output string
*
* @method formatOutputString
* @param {SolidityParam} left-aligned hex representation of string
* @returns {String} ascii string
*/
var formatOutputString = function (param) {
var length = (new BigNumber(param.dynamicPart().slice(0, 64), 16)).toNumber() * 2;
return utils.toAscii(param.dynamicPart().substr(64, length));
};
/**
@ -489,6 +524,7 @@ module.exports = {
formatInputInt: formatInputInt,
formatInputBytes: formatInputBytes,
formatInputDynamicBytes: formatInputDynamicBytes,
formatInputString: formatInputString,
formatInputBool: formatInputBool,
formatInputReal: formatInputReal,
formatOutputInt: formatOutputInt,
@ -498,6 +534,7 @@ module.exports = {
formatOutputBool: formatOutputBool,
formatOutputBytes: formatOutputBytes,
formatOutputDynamicBytes: formatOutputDynamicBytes,
formatOutputString: formatOutputString,
formatOutputAddress: formatOutputAddress
};
@ -687,13 +724,14 @@ var getOffset = function (bytes, index) {
*/
SolidityParam.decodeBytes = function (bytes, index) {
index = index || 0;
//TODO add support for strings longer than 32 bytes
//var length = parseInt('0x' + bytes.substr(offset * 64, 64));
var offset = getOffset(bytes, index);
// 2 * , cause we also parse length
return new SolidityParam(bytes.substr(offset * 2, 2 * 64), 0);
var l = parseInt('0x' + bytes.substr(offset * 2, 64));
l = Math.floor((l + 31) / 32);
// (1 + l) * , cause we also parse length
return new SolidityParam(bytes.substr(offset * 2, (1 + l) * 64), 0);
};
/**
@ -846,7 +884,7 @@ module.exports = function (str, isNew) {
};
},{"./utils":7,"crypto-js/sha3":33}],7:[function(require,module,exports){
},{"./utils":7,"crypto-js/sha3":34}],7:[function(require,module,exports){
/*
This file is part of ethereum.js.
@ -924,6 +962,19 @@ var padLeft = function (string, chars, sign) {
return new Array(chars - string.length + 1).join(sign ? sign : "0") + string;
};
/**
* Should be called to pad string to expected length
*
* @method padRight
* @param {String} string to be padded
* @param {Number} characters that result string should have
* @param {String} sign, by default 0
* @returns {String} right aligned string
*/
var padRight = function (string, chars, sign) {
return string + (new Array(chars - string.length + 1).join(sign ? sign : "0"));
};
/**
* Should be called to get sting from it's hex representation
*
@ -940,10 +991,6 @@ var toAscii = function(hex) {
}
for (; i < l; i+=2) {
var code = parseInt(hex.substr(i, 2), 16);
if (code === 0) {
break;
}
str += String.fromCharCode(code);
}
@ -1053,7 +1100,7 @@ var fromDecimal = function (value) {
* @return {String}
*/
var toHex = function (val) {
/*jshint maxcomplexity:7 */
/*jshint maxcomplexity: 8 */
if (isBoolean(val))
return fromDecimal(+val);
@ -1067,9 +1114,11 @@ var toHex = function (val) {
// if its a negative number, pass it through fromDecimal
if (isString(val)) {
if (val.indexOf('-0x') === 0)
return fromDecimal(val);
return fromDecimal(val);
else if (!isFinite(val))
return fromAscii(val);
else if(val.indexOf('0x') === 0)
return val;
}
return fromDecimal(val);
@ -1320,6 +1369,7 @@ var isIBAN = function (iban) {
module.exports = {
padLeft: padLeft,
padRight: padRight,
toHex: toHex,
toDecimal: toDecimal,
fromDecimal: fromDecimal,
@ -1348,7 +1398,7 @@ module.exports = {
},{"bignumber.js":"bignumber.js"}],8:[function(require,module,exports){
module.exports={
"version": "0.6.0"
"version": "0.7.1"
}
},{}],9:[function(require,module,exports){
@ -1434,31 +1484,25 @@ var setupProperties = function (obj, properties) {
/// setups web3 object, and it's in-browser executed methods
var web3 = {};
web3.providers = {};
web3.currentProvider = null;
web3.version = {};
web3.version.api = version.version;
web3.eth = {};
/*jshint maxparams:4 */
web3.eth.filter = function (fil, eventParams, options, formatter) {
// if its event, treat it differently
// TODO: simplify and remove
if (fil._isEvent) {
return fil(eventParams, options);
}
// output logs works for blockFilter and pendingTransaction filters?
return new Filter(fil, watches.eth(), formatter || formatters.outputLogFormatter);
web3.eth.filter = function (fil, callback) {
return new Filter(fil, watches.eth(), formatters.outputLogFormatter, callback);
};
/*jshint maxparams:3 */
web3.shh = {};
web3.shh.filter = function (fil) {
return new Filter(fil, watches.shh(), formatters.outputPostFormatter);
web3.shh.filter = function (fil, callback) {
return new Filter(fil, watches.shh(), formatters.outputPostFormatter, callback);
};
web3.net = {};
web3.db = {};
web3.setProvider = function (provider) {
this.currentProvider = provider;
RequestManager.getInstance().setProvider(provider);
};
web3.reset = function () {
@ -1531,7 +1575,90 @@ setupMethods(web3.shh, shh.methods);
module.exports = web3;
},{"./utils/config":5,"./utils/sha3":6,"./utils/utils":7,"./version.json":8,"./web3/batch":10,"./web3/db":12,"./web3/eth":14,"./web3/filter":16,"./web3/formatters":17,"./web3/method":22,"./web3/net":24,"./web3/property":25,"./web3/requestmanager":27,"./web3/shh":28,"./web3/watches":30}],10:[function(require,module,exports){
},{"./utils/config":5,"./utils/sha3":6,"./utils/utils":7,"./version.json":8,"./web3/batch":11,"./web3/db":13,"./web3/eth":15,"./web3/filter":17,"./web3/formatters":18,"./web3/method":23,"./web3/net":25,"./web3/property":26,"./web3/requestmanager":28,"./web3/shh":29,"./web3/watches":31}],10:[function(require,module,exports){
/*
This file is part of ethereum.js.
ethereum.js is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
ethereum.js 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with ethereum.js. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @file allevents.js
* @author Marek Kotewicz <marek@ethdev.com>
* @date 2014
*/
var sha3 = require('../utils/sha3');
var SolidityEvent = require('./event');
var formatters = require('./formatters');
var utils = require('../utils/utils');
var Filter = require('./filter');
var watches = require('./watches');
var AllSolidityEvents = function (json, address) {
this._json = json;
this._address = address;
};
AllSolidityEvents.prototype.encode = function (options) {
options = options || {};
var result = {};
['fromBlock', 'toBlock'].filter(function (f) {
return options[f] !== undefined;
}).forEach(function (f) {
result[f] = formatters.inputBlockNumberFormatter(options[f]);
});
result.topics = [null, null, null, null, null]; // match all topics
result.address = this._address;
return result;
};
AllSolidityEvents.prototype.decode = function (data) {
data.data = data.data || '';
data.topics = data.topics || [];
var eventTopic = data.topics[0].slice(2);
var match = this._json.filter(function (j) {
return eventTopic === sha3(utils.transformToFullName(j));
})[0];
if (!match) { // cannot find matching event?
console.warn('cannot find event for log');
return data;
}
var event = new SolidityEvent(match, this._address);
return event.decode(data);
};
AllSolidityEvents.prototype.execute = function (options, callback) {
var o = this.encode(options);
var formatter = this.decode.bind(this);
return new Filter(o, watches.eth(), formatter, callback);
};
AllSolidityEvents.prototype.attachToContract = function (contract) {
var execute = this.execute.bind(this);
contract.allEvents = execute;
};
module.exports = AllSolidityEvents;
},{"../utils/sha3":6,"../utils/utils":7,"./event":16,"./filter":17,"./formatters":18,"./watches":31}],11:[function(require,module,exports){
/*
This file is part of ethereum.js.
@ -1594,7 +1721,7 @@ Batch.prototype.execute = function () {
module.exports = Batch;
},{"./requestmanager":27}],11:[function(require,module,exports){
},{"./requestmanager":28}],12:[function(require,module,exports){
/*
This file is part of ethereum.js.
@ -1622,6 +1749,7 @@ var utils = require('../utils/utils');
var coder = require('../solidity/coder');
var SolidityEvent = require('./event');
var SolidityFunction = require('./function');
var AllEvents = require('./allevents');
/**
* Should be called to encode constructor params
@ -1667,9 +1795,14 @@ var addFunctionsToContract = function (contract, abi) {
* @param {Array} abi
*/
var addEventsToContract = function (contract, abi) {
abi.filter(function (json) {
var events = abi.filter(function (json) {
return json.type === 'event';
}).map(function (json) {
});
var All = new AllEvents(events, contract.address);
All.attachToContract(contract);
events.map(function (json) {
return new SolidityEvent(json, contract.address);
}).forEach(function (e) {
e.attachToContract(contract);
@ -1776,7 +1909,7 @@ var Contract = function (abi, address) {
module.exports = contract;
},{"../solidity/coder":1,"../utils/utils":7,"../web3":9,"./event":15,"./function":18}],12:[function(require,module,exports){
},{"../solidity/coder":1,"../utils/utils":7,"../web3":9,"./allevents":10,"./event":16,"./function":19}],13:[function(require,module,exports){
/*
This file is part of ethereum.js.
@ -1834,7 +1967,7 @@ module.exports = {
methods: methods
};
},{"./method":22}],13:[function(require,module,exports){
},{"./method":23}],14:[function(require,module,exports){
/*
This file is part of ethereum.js.
@ -1874,7 +2007,7 @@ module.exports = {
};
},{}],14:[function(require,module,exports){
},{}],15:[function(require,module,exports){
/*
This file is part of ethereum.js.
@ -2159,7 +2292,7 @@ module.exports = {
};
},{"../utils/utils":7,"./formatters":17,"./method":22,"./property":25}],15:[function(require,module,exports){
},{"../utils/utils":7,"./formatters":18,"./method":23,"./property":26}],16:[function(require,module,exports){
/*
This file is part of ethereum.js.
@ -2184,9 +2317,10 @@ module.exports = {
var utils = require('../utils/utils');
var coder = require('../solidity/coder');
var web3 = require('../web3');
var formatters = require('./formatters');
var sha3 = require('../utils/sha3');
var Filter = require('./filter');
var watches = require('./watches');
/**
* This prototype should be used to create event filters
@ -2332,10 +2466,21 @@ SolidityEvent.prototype.decode = function (data) {
* @param {Object} options
* @return {Object} filter object
*/
SolidityEvent.prototype.execute = function (indexed, options) {
SolidityEvent.prototype.execute = function (indexed, options, callback) {
if (utils.isFunction(arguments[arguments.length - 1])) {
callback = arguments[arguments.length - 1];
if(arguments.length === 2)
options = null;
if(arguments.length === 1) {
options = null;
indexed = {};
}
}
var o = this.encode(indexed, options);
var formatter = this.decode.bind(this);
return web3.eth.filter(o, undefined, undefined, formatter);
return new Filter(o, watches.eth(), formatter, callback);
};
/**
@ -2356,7 +2501,7 @@ SolidityEvent.prototype.attachToContract = function (contract) {
module.exports = SolidityEvent;
},{"../solidity/coder":1,"../utils/sha3":6,"../utils/utils":7,"../web3":9,"./formatters":17}],16:[function(require,module,exports){
},{"../solidity/coder":1,"../utils/sha3":6,"../utils/utils":7,"./filter":17,"./formatters":18,"./watches":31}],17:[function(require,module,exports){
/*
This file is part of ethereum.js.
@ -2486,7 +2631,7 @@ var pollFilter = function(self) {
};
var Filter = function (options, methods, formatter) {
var Filter = function (options, methods, formatter, callback) {
var self = this;
var implementation = {};
methods.forEach(function (method) {
@ -2494,23 +2639,32 @@ var Filter = function (options, methods, formatter) {
});
this.options = getOptions(options);
this.implementation = implementation;
this.filterId = null;
this.callbacks = [];
this.pollFilters = [];
this.formatter = formatter;
this.implementation.newFilter(this.options, function(error, id){
if(error) {
self.callbacks.forEach(function(callback){
callback(error);
self.callbacks.forEach(function(cb){
cb(error);
});
} else {
self.filterId = id;
// get filter logs at start
self.callbacks.forEach(function(callback){
getLogsAtStart(self, callback);
// get filter logs for the already existing watch calls
self.callbacks.forEach(function(cb){
getLogsAtStart(self, cb);
});
pollFilter(self);
if(self.callbacks.length > 0)
pollFilter(self);
// start to watch immediately
if(callback) {
return self.watch(callback);
}
}
});
};
Filter.prototype.watch = function (callback) {
@ -2556,7 +2710,7 @@ Filter.prototype.get = function (callback) {
module.exports = Filter;
},{"../utils/utils":7,"./formatters":17,"./requestmanager":27}],17:[function(require,module,exports){
},{"../utils/utils":7,"./formatters":18,"./requestmanager":28}],18:[function(require,module,exports){
/*
This file is part of ethereum.js.
@ -2778,7 +2932,7 @@ module.exports = {
};
},{"../utils/config":5,"../utils/utils":7}],18:[function(require,module,exports){
},{"../utils/config":5,"../utils/utils":7}],19:[function(require,module,exports){
/*
This file is part of ethereum.js.
@ -2969,8 +3123,9 @@ SolidityFunction.prototype.request = function () {
var format = this.unpackOutput.bind(this);
return {
method: this._constant ? 'eth_call' : 'eth_sendTransaction',
callback: callback,
payload: payload,
params: [payload],
format: format
};
};
@ -3014,7 +3169,7 @@ SolidityFunction.prototype.attachToContract = function (contract) {
module.exports = SolidityFunction;
},{"../solidity/coder":1,"../utils/sha3":6,"../utils/utils":7,"../web3":9,"./formatters":17}],19:[function(require,module,exports){
},{"../solidity/coder":1,"../utils/sha3":6,"../utils/utils":7,"../web3":9,"./formatters":18}],20:[function(require,module,exports){
/*
This file is part of ethereum.js.
@ -3041,7 +3196,8 @@ module.exports = SolidityFunction;
"use strict";
var XMLHttpRequest = require('xmlhttprequest').XMLHttpRequest; // jshint ignore:line
// resolves the problem for electron/atom shell environments, which use node integration, but have no process variable available
var XMLHttpRequest = (typeof window !== 'undefined' && window.XMLHttpRequest) ? window.XMLHttpRequest : require('xmlhttprequest').XMLHttpRequest; // jshint ignore:line
var errors = require('./errors');
var HttpProvider = function (host) {
@ -3108,7 +3264,7 @@ HttpProvider.prototype.sendAsync = function (payload, callback) {
module.exports = HttpProvider;
},{"./errors":13,"xmlhttprequest":4}],20:[function(require,module,exports){
},{"./errors":14,"xmlhttprequest":4}],21:[function(require,module,exports){
/*
This file is part of ethereum.js.
@ -3218,7 +3374,7 @@ ICAP.prototype.address = function () {
module.exports = ICAP;
},{"../utils/utils":7}],21:[function(require,module,exports){
},{"../utils/utils":7}],22:[function(require,module,exports){
/*
This file is part of ethereum.js.
@ -3311,7 +3467,7 @@ Jsonrpc.prototype.toBatchPayload = function (messages) {
module.exports = Jsonrpc;
},{}],22:[function(require,module,exports){
},{}],23:[function(require,module,exports){
/*
This file is part of ethereum.js.
@ -3485,7 +3641,7 @@ Method.prototype.send = function () {
module.exports = Method;
},{"../utils/utils":7,"./errors":13,"./requestmanager":27}],23:[function(require,module,exports){
},{"../utils/utils":7,"./errors":14,"./requestmanager":28}],24:[function(require,module,exports){
/*
This file is part of ethereum.js.
@ -3533,7 +3689,7 @@ var abi = [
module.exports = contract(abi).at(address);
},{"./contract":11}],24:[function(require,module,exports){
},{"./contract":12}],25:[function(require,module,exports){
/*
This file is part of ethereum.js.
@ -3583,7 +3739,7 @@ module.exports = {
};
},{"../utils/utils":7,"./property":25}],25:[function(require,module,exports){
},{"../utils/utils":7,"./property":26}],26:[function(require,module,exports){
/*
This file is part of ethereum.js.
@ -3701,7 +3857,7 @@ Property.prototype.getAsync = function (callback) {
module.exports = Property;
},{"./requestmanager":27}],26:[function(require,module,exports){
},{"./requestmanager":28}],27:[function(require,module,exports){
/*
This file is part of ethereum.js.
@ -3736,7 +3892,7 @@ QtSyncProvider.prototype.send = function (payload) {
module.exports = QtSyncProvider;
},{}],27:[function(require,module,exports){
},{}],28:[function(require,module,exports){
/*
This file is part of ethereum.js.
@ -4001,7 +4157,7 @@ RequestManager.prototype.poll = function () {
module.exports = RequestManager;
},{"../utils/config":5,"../utils/utils":7,"./errors":13,"./jsonrpc":21}],28:[function(require,module,exports){
},{"../utils/config":5,"../utils/utils":7,"./errors":14,"./jsonrpc":22}],29:[function(require,module,exports){
/*
This file is part of ethereum.js.
@ -4071,7 +4227,7 @@ module.exports = {
};
},{"./formatters":17,"./method":22}],29:[function(require,module,exports){
},{"./formatters":18,"./method":23}],30:[function(require,module,exports){
/*
This file is part of ethereum.js.
@ -4167,7 +4323,7 @@ var deposit = function (from, address, value, client, callback) {
module.exports = transfer;
},{"../web3":9,"./contract":11,"./icap":20,"./namereg":23}],30:[function(require,module,exports){
},{"../web3":9,"./contract":12,"./icap":21,"./namereg":24}],31:[function(require,module,exports){
/*
This file is part of ethereum.js.
@ -4283,9 +4439,9 @@ module.exports = {
};
},{"./method":22}],31:[function(require,module,exports){
},{"./method":23}],32:[function(require,module,exports){
},{}],32:[function(require,module,exports){
},{}],33:[function(require,module,exports){
;(function (root, factory) {
if (typeof exports === "object") {
// CommonJS
@ -5028,7 +5184,7 @@ module.exports = {
return CryptoJS;
}));
},{}],33:[function(require,module,exports){
},{}],34:[function(require,module,exports){
;(function (root, factory, undef) {
if (typeof exports === "object") {
// CommonJS
@ -5352,7 +5508,7 @@ module.exports = {
return CryptoJS.SHA3;
}));
},{"./core":32,"./x64-core":34}],34:[function(require,module,exports){
},{"./core":33,"./x64-core":35}],35:[function(require,module,exports){
;(function (root, factory) {
if (typeof exports === "object") {
// CommonJS
@ -5657,7 +5813,7 @@ module.exports = {
return CryptoJS;
}));
},{"./core":32}],"bignumber.js":[function(require,module,exports){
},{"./core":33}],"bignumber.js":[function(require,module,exports){
'use strict';
module.exports = BigNumber; // jshint ignore:line
@ -5679,5 +5835,5 @@ if (typeof window !== 'undefined' && typeof window.web3 === 'undefined') {
module.exports = web3;
},{"./lib/web3":9,"./lib/web3/contract":11,"./lib/web3/httpprovider":19,"./lib/web3/namereg":23,"./lib/web3/qtsync":26,"./lib/web3/transfer":29}]},{},["web3"])
},{"./lib/web3":9,"./lib/web3/contract":12,"./lib/web3/httpprovider":20,"./lib/web3/namereg":24,"./lib/web3/qtsync":27,"./lib/web3/transfer":30}]},{},["web3"])
//# sourceMappingURL=web3-light.js.map

4
libjsqrc/ethereumjs/dist/web3-light.min.js

File diff suppressed because one or more lines are too long

314
libjsqrc/ethereumjs/dist/web3.js

@ -263,6 +263,13 @@ var coder = new SolidityCoder([
inputFormatter: f.formatInputBytes,
outputFormatter: f.formatOutputBytes
}),
new SolidityType({
name: 'string',
match: 'strict',
mode: 'bytes',
inputFormatter: f.formatInputString,
outputFormatter: f.formatOutputString
}),
new SolidityType({
name: 'real',
match: 'prefix',
@ -328,26 +335,43 @@ var formatInputInt = function (value) {
};
/**
* Formats input value to byte representation of string
* Formats input bytes
*
* @method formatInputBytes
* @param {String}
* @returns {SolidityParam}
*/
var formatInputBytes = function (value) {
var result = utils.fromAscii(value, c.ETH_PADDING).substr(2);
var result = utils.padRight(utils.toHex(value).substr(2), 64);
return new SolidityParam(result);
};
/**
* Formats input value to byte representation of string
* Formats input bytes
*
* @method formatInputDynamicBytes
* @method formatDynamicInputBytes
* @param {String}
* @returns {SolidityParam}
*/
var formatInputDynamicBytes = function (value) {
var result = utils.fromAscii(value, c.ETH_PADDING).substr(2);
value = utils.toHex(value).substr(2);
var l = Math.floor((value.length + 63) / 64);
var result = utils.padRight(value, l * 64);
var length = Math.floor(value.length / 2);
return new SolidityParam(formatInputInt(length).value + result, 32);
};
/**
* Formats input value to byte representation of string
*
* @method formatInputString
* @param {String}
* @returns {SolidityParam}
*/
var formatInputString = function (value) {
var result = utils.fromAscii(value).substr(2);
var l = Math.floor((result.length + 63) / 64);
result = utils.padRight(result, l * 64);
return new SolidityParam(formatInputInt(value.length).value + result, 32);
};
@ -450,27 +474,38 @@ var formatOutputBool = function (param) {
};
/**
* Should be used to format output string
* Should be used to format output bytes
*
* @method formatOutputBytes
* @param {SolidityParam} left-aligned hex representation of string
* @returns {String} ascii string
* @returns {String} hex string
*/
var formatOutputBytes = function (param) {
// length might also be important!
return utils.toAscii(param.staticPart());
return '0x' + param.staticPart();
};
/**
* Should be used to format output string
* Should be used to format output bytes
*
* @method formatOutputDynamicBytes
* @param {SolidityParam} left-aligned hex representation of string
* @returns {String} ascii string
* @returns {String} hex string
*/
var formatOutputDynamicBytes = function (param) {
// length might also be important!
return utils.toAscii(param.dynamicPart().slice(64));
var length = (new BigNumber(param.dynamicPart().slice(0, 64), 16)).toNumber() * 2;
return '0x' + param.dynamicPart().substr(64, length);
};
/**
* Should be used to format output string
*
* @method formatOutputString
* @param {SolidityParam} left-aligned hex representation of string
* @returns {String} ascii string
*/
var formatOutputString = function (param) {
var length = (new BigNumber(param.dynamicPart().slice(0, 64), 16)).toNumber() * 2;
return utils.toAscii(param.dynamicPart().substr(64, length));
};
/**
@ -489,6 +524,7 @@ module.exports = {
formatInputInt: formatInputInt,
formatInputBytes: formatInputBytes,
formatInputDynamicBytes: formatInputDynamicBytes,
formatInputString: formatInputString,
formatInputBool: formatInputBool,
formatInputReal: formatInputReal,
formatOutputInt: formatOutputInt,
@ -498,6 +534,7 @@ module.exports = {
formatOutputBool: formatOutputBool,
formatOutputBytes: formatOutputBytes,
formatOutputDynamicBytes: formatOutputDynamicBytes,
formatOutputString: formatOutputString,
formatOutputAddress: formatOutputAddress
};
@ -687,13 +724,14 @@ var getOffset = function (bytes, index) {
*/
SolidityParam.decodeBytes = function (bytes, index) {
index = index || 0;
//TODO add support for strings longer than 32 bytes
//var length = parseInt('0x' + bytes.substr(offset * 64, 64));
var offset = getOffset(bytes, index);
// 2 * , cause we also parse length
return new SolidityParam(bytes.substr(offset * 2, 2 * 64), 0);
var l = parseInt('0x' + bytes.substr(offset * 2, 64));
l = Math.floor((l + 31) / 32);
// (1 + l) * , cause we also parse length
return new SolidityParam(bytes.substr(offset * 2, (1 + l) * 64), 0);
};
/**
@ -846,7 +884,7 @@ module.exports = function (str, isNew) {
};
},{"./utils":7,"crypto-js/sha3":33}],7:[function(require,module,exports){
},{"./utils":7,"crypto-js/sha3":34}],7:[function(require,module,exports){
/*
This file is part of ethereum.js.
@ -924,6 +962,19 @@ var padLeft = function (string, chars, sign) {
return new Array(chars - string.length + 1).join(sign ? sign : "0") + string;
};
/**
* Should be called to pad string to expected length
*
* @method padRight
* @param {String} string to be padded
* @param {Number} characters that result string should have
* @param {String} sign, by default 0
* @returns {String} right aligned string
*/
var padRight = function (string, chars, sign) {
return string + (new Array(chars - string.length + 1).join(sign ? sign : "0"));
};
/**
* Should be called to get sting from it's hex representation
*
@ -940,10 +991,6 @@ var toAscii = function(hex) {
}
for (; i < l; i+=2) {
var code = parseInt(hex.substr(i, 2), 16);
if (code === 0) {
break;
}
str += String.fromCharCode(code);
}
@ -1053,7 +1100,7 @@ var fromDecimal = function (value) {
* @return {String}
*/
var toHex = function (val) {
/*jshint maxcomplexity:7 */
/*jshint maxcomplexity: 8 */
if (isBoolean(val))
return fromDecimal(+val);
@ -1067,9 +1114,11 @@ var toHex = function (val) {
// if its a negative number, pass it through fromDecimal
if (isString(val)) {
if (val.indexOf('-0x') === 0)
return fromDecimal(val);
return fromDecimal(val);
else if (!isFinite(val))
return fromAscii(val);
else if(val.indexOf('0x') === 0)
return val;
}
return fromDecimal(val);
@ -1320,6 +1369,7 @@ var isIBAN = function (iban) {
module.exports = {
padLeft: padLeft,
padRight: padRight,
toHex: toHex,
toDecimal: toDecimal,
fromDecimal: fromDecimal,
@ -1348,7 +1398,7 @@ module.exports = {
},{"bignumber.js":"bignumber.js"}],8:[function(require,module,exports){
module.exports={
"version": "0.6.0"
"version": "0.7.1"
}
},{}],9:[function(require,module,exports){
@ -1434,31 +1484,25 @@ var setupProperties = function (obj, properties) {
/// setups web3 object, and it's in-browser executed methods
var web3 = {};
web3.providers = {};
web3.currentProvider = null;
web3.version = {};
web3.version.api = version.version;
web3.eth = {};
/*jshint maxparams:4 */
web3.eth.filter = function (fil, eventParams, options, formatter) {
// if its event, treat it differently
// TODO: simplify and remove
if (fil._isEvent) {
return fil(eventParams, options);
}
// output logs works for blockFilter and pendingTransaction filters?
return new Filter(fil, watches.eth(), formatter || formatters.outputLogFormatter);
web3.eth.filter = function (fil, callback) {
return new Filter(fil, watches.eth(), formatters.outputLogFormatter, callback);
};
/*jshint maxparams:3 */
web3.shh = {};
web3.shh.filter = function (fil) {
return new Filter(fil, watches.shh(), formatters.outputPostFormatter);
web3.shh.filter = function (fil, callback) {
return new Filter(fil, watches.shh(), formatters.outputPostFormatter, callback);
};
web3.net = {};
web3.db = {};
web3.setProvider = function (provider) {
this.currentProvider = provider;
RequestManager.getInstance().setProvider(provider);
};
web3.reset = function () {
@ -1531,7 +1575,90 @@ setupMethods(web3.shh, shh.methods);
module.exports = web3;
},{"./utils/config":5,"./utils/sha3":6,"./utils/utils":7,"./version.json":8,"./web3/batch":10,"./web3/db":12,"./web3/eth":14,"./web3/filter":16,"./web3/formatters":17,"./web3/method":22,"./web3/net":24,"./web3/property":25,"./web3/requestmanager":27,"./web3/shh":28,"./web3/watches":30}],10:[function(require,module,exports){
},{"./utils/config":5,"./utils/sha3":6,"./utils/utils":7,"./version.json":8,"./web3/batch":11,"./web3/db":13,"./web3/eth":15,"./web3/filter":17,"./web3/formatters":18,"./web3/method":23,"./web3/net":25,"./web3/property":26,"./web3/requestmanager":28,"./web3/shh":29,"./web3/watches":31}],10:[function(require,module,exports){
/*
This file is part of ethereum.js.
ethereum.js is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
ethereum.js 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with ethereum.js. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @file allevents.js
* @author Marek Kotewicz <marek@ethdev.com>
* @date 2014
*/
var sha3 = require('../utils/sha3');
var SolidityEvent = require('./event');
var formatters = require('./formatters');
var utils = require('../utils/utils');
var Filter = require('./filter');
var watches = require('./watches');
var AllSolidityEvents = function (json, address) {
this._json = json;
this._address = address;
};
AllSolidityEvents.prototype.encode = function (options) {
options = options || {};
var result = {};
['fromBlock', 'toBlock'].filter(function (f) {
return options[f] !== undefined;
}).forEach(function (f) {
result[f] = formatters.inputBlockNumberFormatter(options[f]);
});
result.topics = [null, null, null, null, null]; // match all topics
result.address = this._address;
return result;
};
AllSolidityEvents.prototype.decode = function (data) {
data.data = data.data || '';
data.topics = data.topics || [];
var eventTopic = data.topics[0].slice(2);
var match = this._json.filter(function (j) {
return eventTopic === sha3(utils.transformToFullName(j));
})[0];
if (!match) { // cannot find matching event?
console.warn('cannot find event for log');
return data;
}
var event = new SolidityEvent(match, this._address);
return event.decode(data);
};
AllSolidityEvents.prototype.execute = function (options, callback) {
var o = this.encode(options);
var formatter = this.decode.bind(this);
return new Filter(o, watches.eth(), formatter, callback);
};
AllSolidityEvents.prototype.attachToContract = function (contract) {
var execute = this.execute.bind(this);
contract.allEvents = execute;
};
module.exports = AllSolidityEvents;
},{"../utils/sha3":6,"../utils/utils":7,"./event":16,"./filter":17,"./formatters":18,"./watches":31}],11:[function(require,module,exports){
/*
This file is part of ethereum.js.
@ -1594,7 +1721,7 @@ Batch.prototype.execute = function () {
module.exports = Batch;
},{"./requestmanager":27}],11:[function(require,module,exports){
},{"./requestmanager":28}],12:[function(require,module,exports){
/*
This file is part of ethereum.js.
@ -1622,6 +1749,7 @@ var utils = require('../utils/utils');
var coder = require('../solidity/coder');
var SolidityEvent = require('./event');
var SolidityFunction = require('./function');
var AllEvents = require('./allevents');
/**
* Should be called to encode constructor params
@ -1667,9 +1795,14 @@ var addFunctionsToContract = function (contract, abi) {
* @param {Array} abi
*/
var addEventsToContract = function (contract, abi) {
abi.filter(function (json) {
var events = abi.filter(function (json) {
return json.type === 'event';
}).map(function (json) {
});
var All = new AllEvents(events, contract.address);
All.attachToContract(contract);
events.map(function (json) {
return new SolidityEvent(json, contract.address);
}).forEach(function (e) {
e.attachToContract(contract);
@ -1776,7 +1909,7 @@ var Contract = function (abi, address) {
module.exports = contract;
},{"../solidity/coder":1,"../utils/utils":7,"../web3":9,"./event":15,"./function":18}],12:[function(require,module,exports){
},{"../solidity/coder":1,"../utils/utils":7,"../web3":9,"./allevents":10,"./event":16,"./function":19}],13:[function(require,module,exports){
/*
This file is part of ethereum.js.
@ -1834,7 +1967,7 @@ module.exports = {
methods: methods
};
},{"./method":22}],13:[function(require,module,exports){
},{"./method":23}],14:[function(require,module,exports){
/*
This file is part of ethereum.js.
@ -1874,7 +2007,7 @@ module.exports = {
};
},{}],14:[function(require,module,exports){
},{}],15:[function(require,module,exports){
/*
This file is part of ethereum.js.
@ -2159,7 +2292,7 @@ module.exports = {
};
},{"../utils/utils":7,"./formatters":17,"./method":22,"./property":25}],15:[function(require,module,exports){
},{"../utils/utils":7,"./formatters":18,"./method":23,"./property":26}],16:[function(require,module,exports){
/*
This file is part of ethereum.js.
@ -2184,9 +2317,10 @@ module.exports = {
var utils = require('../utils/utils');
var coder = require('../solidity/coder');
var web3 = require('../web3');
var formatters = require('./formatters');
var sha3 = require('../utils/sha3');
var Filter = require('./filter');
var watches = require('./watches');
/**
* This prototype should be used to create event filters
@ -2332,10 +2466,21 @@ SolidityEvent.prototype.decode = function (data) {
* @param {Object} options
* @return {Object} filter object
*/
SolidityEvent.prototype.execute = function (indexed, options) {
SolidityEvent.prototype.execute = function (indexed, options, callback) {
if (utils.isFunction(arguments[arguments.length - 1])) {
callback = arguments[arguments.length - 1];
if(arguments.length === 2)
options = null;
if(arguments.length === 1) {
options = null;
indexed = {};
}
}
var o = this.encode(indexed, options);
var formatter = this.decode.bind(this);
return web3.eth.filter(o, undefined, undefined, formatter);
return new Filter(o, watches.eth(), formatter, callback);
};
/**
@ -2356,7 +2501,7 @@ SolidityEvent.prototype.attachToContract = function (contract) {
module.exports = SolidityEvent;
},{"../solidity/coder":1,"../utils/sha3":6,"../utils/utils":7,"../web3":9,"./formatters":17}],16:[function(require,module,exports){
},{"../solidity/coder":1,"../utils/sha3":6,"../utils/utils":7,"./filter":17,"./formatters":18,"./watches":31}],17:[function(require,module,exports){
/*
This file is part of ethereum.js.
@ -2486,7 +2631,7 @@ var pollFilter = function(self) {
};
var Filter = function (options, methods, formatter) {
var Filter = function (options, methods, formatter, callback) {
var self = this;
var implementation = {};
methods.forEach(function (method) {
@ -2494,23 +2639,32 @@ var Filter = function (options, methods, formatter) {
});
this.options = getOptions(options);
this.implementation = implementation;
this.filterId = null;
this.callbacks = [];
this.pollFilters = [];
this.formatter = formatter;
this.implementation.newFilter(this.options, function(error, id){
if(error) {
self.callbacks.forEach(function(callback){
callback(error);
self.callbacks.forEach(function(cb){
cb(error);
});
} else {
self.filterId = id;
// get filter logs at start
self.callbacks.forEach(function(callback){
getLogsAtStart(self, callback);
// get filter logs for the already existing watch calls
self.callbacks.forEach(function(cb){
getLogsAtStart(self, cb);
});
pollFilter(self);
if(self.callbacks.length > 0)
pollFilter(self);
// start to watch immediately
if(callback) {
return self.watch(callback);
}
}
});
};
Filter.prototype.watch = function (callback) {
@ -2556,7 +2710,7 @@ Filter.prototype.get = function (callback) {
module.exports = Filter;
},{"../utils/utils":7,"./formatters":17,"./requestmanager":27}],17:[function(require,module,exports){
},{"../utils/utils":7,"./formatters":18,"./requestmanager":28}],18:[function(require,module,exports){
/*
This file is part of ethereum.js.
@ -2778,7 +2932,7 @@ module.exports = {
};
},{"../utils/config":5,"../utils/utils":7}],18:[function(require,module,exports){
},{"../utils/config":5,"../utils/utils":7}],19:[function(require,module,exports){
/*
This file is part of ethereum.js.
@ -2969,8 +3123,9 @@ SolidityFunction.prototype.request = function () {
var format = this.unpackOutput.bind(this);
return {
method: this._constant ? 'eth_call' : 'eth_sendTransaction',
callback: callback,
payload: payload,
params: [payload],
format: format
};
};
@ -3014,7 +3169,7 @@ SolidityFunction.prototype.attachToContract = function (contract) {
module.exports = SolidityFunction;
},{"../solidity/coder":1,"../utils/sha3":6,"../utils/utils":7,"../web3":9,"./formatters":17}],19:[function(require,module,exports){
},{"../solidity/coder":1,"../utils/sha3":6,"../utils/utils":7,"../web3":9,"./formatters":18}],20:[function(require,module,exports){
/*
This file is part of ethereum.js.
@ -3041,7 +3196,8 @@ module.exports = SolidityFunction;
"use strict";
var XMLHttpRequest = require('xmlhttprequest').XMLHttpRequest; // jshint ignore:line
// resolves the problem for electron/atom shell environments, which use node integration, but have no process variable available
var XMLHttpRequest = (typeof window !== 'undefined' && window.XMLHttpRequest) ? window.XMLHttpRequest : require('xmlhttprequest').XMLHttpRequest; // jshint ignore:line
var errors = require('./errors');
var HttpProvider = function (host) {
@ -3108,7 +3264,7 @@ HttpProvider.prototype.sendAsync = function (payload, callback) {
module.exports = HttpProvider;
},{"./errors":13,"xmlhttprequest":4}],20:[function(require,module,exports){
},{"./errors":14,"xmlhttprequest":4}],21:[function(require,module,exports){
/*
This file is part of ethereum.js.
@ -3218,7 +3374,7 @@ ICAP.prototype.address = function () {
module.exports = ICAP;
},{"../utils/utils":7}],21:[function(require,module,exports){
},{"../utils/utils":7}],22:[function(require,module,exports){
/*
This file is part of ethereum.js.
@ -3311,7 +3467,7 @@ Jsonrpc.prototype.toBatchPayload = function (messages) {
module.exports = Jsonrpc;
},{}],22:[function(require,module,exports){
},{}],23:[function(require,module,exports){
/*
This file is part of ethereum.js.
@ -3485,7 +3641,7 @@ Method.prototype.send = function () {
module.exports = Method;
},{"../utils/utils":7,"./errors":13,"./requestmanager":27}],23:[function(require,module,exports){
},{"../utils/utils":7,"./errors":14,"./requestmanager":28}],24:[function(require,module,exports){
/*
This file is part of ethereum.js.
@ -3533,7 +3689,7 @@ var abi = [
module.exports = contract(abi).at(address);
},{"./contract":11}],24:[function(require,module,exports){
},{"./contract":12}],25:[function(require,module,exports){
/*
This file is part of ethereum.js.
@ -3583,7 +3739,7 @@ module.exports = {
};
},{"../utils/utils":7,"./property":25}],25:[function(require,module,exports){
},{"../utils/utils":7,"./property":26}],26:[function(require,module,exports){
/*
This file is part of ethereum.js.
@ -3701,7 +3857,7 @@ Property.prototype.getAsync = function (callback) {
module.exports = Property;
},{"./requestmanager":27}],26:[function(require,module,exports){
},{"./requestmanager":28}],27:[function(require,module,exports){
/*
This file is part of ethereum.js.
@ -3736,7 +3892,7 @@ QtSyncProvider.prototype.send = function (payload) {
module.exports = QtSyncProvider;
},{}],27:[function(require,module,exports){
},{}],28:[function(require,module,exports){
/*
This file is part of ethereum.js.
@ -4001,7 +4157,7 @@ RequestManager.prototype.poll = function () {
module.exports = RequestManager;
},{"../utils/config":5,"../utils/utils":7,"./errors":13,"./jsonrpc":21}],28:[function(require,module,exports){
},{"../utils/config":5,"../utils/utils":7,"./errors":14,"./jsonrpc":22}],29:[function(require,module,exports){
/*
This file is part of ethereum.js.
@ -4071,7 +4227,7 @@ module.exports = {
};
},{"./formatters":17,"./method":22}],29:[function(require,module,exports){
},{"./formatters":18,"./method":23}],30:[function(require,module,exports){
/*
This file is part of ethereum.js.
@ -4167,7 +4323,7 @@ var deposit = function (from, address, value, client, callback) {
module.exports = transfer;
},{"../web3":9,"./contract":11,"./icap":20,"./namereg":23}],30:[function(require,module,exports){
},{"../web3":9,"./contract":12,"./icap":21,"./namereg":24}],31:[function(require,module,exports){
/*
This file is part of ethereum.js.
@ -4283,9 +4439,9 @@ module.exports = {
};
},{"./method":22}],31:[function(require,module,exports){
},{"./method":23}],32:[function(require,module,exports){
},{}],32:[function(require,module,exports){
},{}],33:[function(require,module,exports){
;(function (root, factory) {
if (typeof exports === "object") {
// CommonJS
@ -5028,7 +5184,7 @@ module.exports = {
return CryptoJS;
}));
},{}],33:[function(require,module,exports){
},{}],34:[function(require,module,exports){
;(function (root, factory, undef) {
if (typeof exports === "object") {
// CommonJS
@ -5352,7 +5508,7 @@ module.exports = {
return CryptoJS.SHA3;
}));
},{"./core":32,"./x64-core":34}],34:[function(require,module,exports){
},{"./core":33,"./x64-core":35}],35:[function(require,module,exports){
;(function (root, factory) {
if (typeof exports === "object") {
// CommonJS
@ -5657,7 +5813,7 @@ module.exports = {
return CryptoJS;
}));
},{"./core":32}],"bignumber.js":[function(require,module,exports){
},{"./core":33}],"bignumber.js":[function(require,module,exports){
/*! bignumber.js v2.0.7 https://github.com/MikeMcl/bignumber.js/LICENCE */
;(function (global) {
@ -8342,7 +8498,7 @@ module.exports = {
}
})(this);
},{"crypto":31}],"web3":[function(require,module,exports){
},{"crypto":32}],"web3":[function(require,module,exports){
var web3 = require('./lib/web3');
web3.providers.HttpProvider = require('./lib/web3/httpprovider');
web3.providers.QtSyncProvider = require('./lib/web3/qtsync');
@ -8358,5 +8514,5 @@ if (typeof window !== 'undefined' && typeof window.web3 === 'undefined') {
module.exports = web3;
},{"./lib/web3":9,"./lib/web3/contract":11,"./lib/web3/httpprovider":19,"./lib/web3/namereg":23,"./lib/web3/qtsync":26,"./lib/web3/transfer":29}]},{},["web3"])
},{"./lib/web3":9,"./lib/web3/contract":12,"./lib/web3/httpprovider":20,"./lib/web3/namereg":24,"./lib/web3/qtsync":27,"./lib/web3/transfer":30}]},{},["web3"])
//# sourceMappingURL=web3.js.map

26
libjsqrc/ethereumjs/dist/web3.js.map

File diff suppressed because one or more lines are too long

6
libjsqrc/ethereumjs/dist/web3.min.js

File diff suppressed because one or more lines are too long

83
libjsqrc/ethereumjs/example/contract_array.html

@ -0,0 +1,83 @@
<!doctype>
<html>
<head>
<script type="text/javascript" src="../dist/web3.js"></script>
<script type="text/javascript">
var web3 = require('web3');
web3.setProvider(new web3.providers.HttpProvider("http://localhost:8545"));
// solidity code code
var source = "" +
"contract test {\n" +
" function take(uint[] a, uint b) constant returns(uint d) {\n" +
" return a[b];\n" +
" }\n" +
"}\n";
var compiled = web3.eth.compile.solidity(source);
var code = compiled.test.code;
// contract json abi, this is autogenerated using solc CLI
var abi = compiled.test.info.abiDefinition;
var myContract;
function createExampleContract() {
// hide create button
document.getElementById('create').style.visibility = 'hidden';
document.getElementById('code').innerText = code;
// let's assume that coinbase is our account
web3.eth.defaultAccount = web3.eth.coinbase;
var watch = web3.eth.filter('latest');
// create contract
myContract = web3.eth.contract(abi).new({data: code});
console.log('address: ' + myContract.address);
document.getElementById('status').innerText = "transaction sent, waiting for confirmation";
watch.watch(function (err, hash) {
var block = web3.eth.getBlock(hash, true);
var contractMined = block.transactions.reduce(function (mined, th) {
// TODO: compiled code do not have 0x prefix
return mined || (th.from === web3.eth.defaultAccount && th.input.indexOf(code) !== -1);
}, false);
if (contractMined) {
document.getElementById('status').innerText = 'Mined!';
document.getElementById('call').style.visibility = 'visible';
}
});
}
function callExampleContract() {
// this should be generated by ethereum
var param = parseInt(document.getElementById('value').value);
// call the contract
var res = myContract.take([0,6,5,2,1,5,6], param);
document.getElementById('result').innerText = res.toString(10);
}
</script>
</head>
<body>
<h1>contract</h1>
<div id="code"></div>
<div id="status"></div>
<div id='create'>
<button type="button" onClick="createExampleContract();">create example contract</button>
</div>
<div id='call' style='visibility: hidden;'>
<div>var array = [0,6,5,2,1,5,6];</div>
<div>var x = array[
<input type="number" id="value" onkeyup='callExampleContract()'></input>
];
</div>
</div>
<div id="result"></div>
</body>
</html>

27
libjsqrc/ethereumjs/gulpfile.js

@ -17,7 +17,7 @@ var bower = require('bower');
var streamify = require('gulp-streamify');
var replace = require('gulp-replace');
var DEST = './dist/';
var DEST = path.join(__dirname, 'dist/');
var src = 'index';
var dst = 'web3';
var lightDst = 'web3-light';
@ -29,7 +29,7 @@ var browserifyOptions = {
bundleExternal: true
};
gulp.task('versionReplace', function(){
gulp.task('version', function(){
gulp.src(['./package.json'])
.pipe(replace(/\"version\"\: \"(.{5})\"/, '"version": "'+ version.version + '"'))
.pipe(gulp.dest('./'));
@ -41,24 +41,24 @@ gulp.task('versionReplace', function(){
.pipe(gulp.dest('./'));
});
gulp.task('bower', function(cb){
gulp.task('bower', ['version'], function(cb){
bower.commands.install().on('end', function (installed){
console.log(installed);
cb();
});
});
gulp.task('clean', ['lint'], function(cb) {
del([ DEST ], cb);
});
gulp.task('lint', function(){
gulp.task('lint', ['bower'], function(){
return gulp.src(['./*.js', './lib/*.js'])
.pipe(jshint())
.pipe(jshint.reporter('default'));
});
gulp.task('buildLight', ['clean'], function () {
gulp.task('clean', ['lint'], function(cb) {
del([ DEST ], cb);
});
gulp.task('light', ['clean'], function () {
return browserify(browserifyOptions)
.require('./' + src + '.js', {expose: 'web3'})
.ignore('bignumber.js')
@ -73,7 +73,7 @@ gulp.task('buildLight', ['clean'], function () {
.pipe(gulp.dest( DEST ));
});
gulp.task('buildStandalone', ['clean'], function () {
gulp.task('standalone', ['clean'], function () {
return browserify(browserifyOptions)
.require('./' + src + '.js', {expose: 'web3'})
.require('bignumber.js') // expose it to dapp users
@ -92,10 +92,5 @@ gulp.task('watch', function() {
gulp.watch(['./lib/*.js'], ['lint', 'build']);
});
gulp.task('light', ['versionReplace','bower', 'lint', 'buildLight']);
gulp.task('standalone', ['versionReplace','bower', 'lint', 'buildStandalone']);
gulp.task('default', ['light', 'standalone']);
gulp.task('version', ['versionReplace']);
gulp.task('default', ['version', 'bower', 'lint', 'clean', 'light', 'standalone']);

95
libjsqrc/ethereumjs/karma.conf.js

@ -1,95 +0,0 @@
// Karma configuration
// Generated on Thu Feb 19 2015 19:57:47 GMT+0100 (W. Europe Standard Time)
module.exports = function (config) {
config.set({
// base path that will be used to resolve all patterns (eg. files, exclude)
basePath: '',
// level of logging
// possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
// Continuous Integration mode
// if true, Karma captures browsers, runs the tests and exits
singleRun: true, logLevel: config.LOG_INFO,
//singleRun: true, logLevel: config.LOG_DEBUG,
// frameworks to use
// available frameworks: https://npmjs.org/browse/keyword/karma-adapter
frameworks: ['browserify', 'mocha'],
// list of files / patterns to load in the browser
files: [
'test/*.js'
],
// list of files to exclude
exclude: [
],
client: {
mocha: {
//ui: 'tdd'
timeout: 5000 // especially for the post requests
}
},
browserify: {
bundleDelay: 750,
debug: true
// transform: [],
// //extensions: ['.js']
},
// preprocess matching files before serving them to the browser
// available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
preprocessors: {
'test/*.js': ['browserify']
},
// test results reporter to use
// possible values: 'dots', 'progress'
// available reporters: https://npmjs.org/browse/keyword/karma-reporter
reporters: ['dots'],
// web server port
port: 9876,
// enable / disable colors in the output (reporters and logs)
colors: true,
// enable / disable watching file and executing tests whenever any file changes
autoWatch: true,
// start these browsers
// available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
// // Chrome
// // PhantomJS
browsers: ['Chrome', 'Safari', 'Firefox'],
browserNoActivityTimeout: 10000,
browserDisconnectTimeout: 5000,
customLaunchers: {
chromeWithoutSecurity: {
base: 'Chrome',
flags: ['--disable-web-security']
},
IE9: {
base: 'IE',
'x-ua-compatible': 'IE=EmulateIE9'
},
IE8: {
base: 'IE',
'x-ua-compatible': 'IE=EmulateIE8'
}
}
});
};

7
libjsqrc/ethereumjs/lib/solidity/coder.js

@ -262,6 +262,13 @@ var coder = new SolidityCoder([
inputFormatter: f.formatInputBytes,
outputFormatter: f.formatOutputBytes
}),
new SolidityType({
name: 'string',
match: 'strict',
mode: 'bytes',
inputFormatter: f.formatInputString,
outputFormatter: f.formatOutputString
}),
new SolidityType({
name: 'real',
match: 'prefix',

56
libjsqrc/ethereumjs/lib/solidity/formatters.js

@ -43,26 +43,43 @@ var formatInputInt = function (value) {
};
/**
* Formats input value to byte representation of string
* Formats input bytes
*
* @method formatInputBytes
* @param {String}
* @returns {SolidityParam}
*/
var formatInputBytes = function (value) {
var result = utils.fromAscii(value, c.ETH_PADDING).substr(2);
var result = utils.padRight(utils.toHex(value).substr(2), 64);
return new SolidityParam(result);
};
/**
* Formats input value to byte representation of string
* Formats input bytes
*
* @method formatInputDynamicBytes
* @method formatDynamicInputBytes
* @param {String}
* @returns {SolidityParam}
*/
var formatInputDynamicBytes = function (value) {
var result = utils.fromAscii(value, c.ETH_PADDING).substr(2);
value = utils.toHex(value).substr(2);
var l = Math.floor((value.length + 63) / 64);
var result = utils.padRight(value, l * 64);
var length = Math.floor(value.length / 2);
return new SolidityParam(formatInputInt(length).value + result, 32);
};
/**
* Formats input value to byte representation of string
*
* @method formatInputString
* @param {String}
* @returns {SolidityParam}
*/
var formatInputString = function (value) {
var result = utils.fromAscii(value).substr(2);
var l = Math.floor((result.length + 63) / 64);
result = utils.padRight(result, l * 64);
return new SolidityParam(formatInputInt(value.length).value + result, 32);
};
@ -165,27 +182,38 @@ var formatOutputBool = function (param) {
};
/**
* Should be used to format output string
* Should be used to format output bytes
*
* @method formatOutputBytes
* @param {SolidityParam} left-aligned hex representation of string
* @returns {String} ascii string
* @returns {String} hex string
*/
var formatOutputBytes = function (param) {
// length might also be important!
return utils.toAscii(param.staticPart());
return '0x' + param.staticPart();
};
/**
* Should be used to format output string
* Should be used to format output bytes
*
* @method formatOutputDynamicBytes
* @param {SolidityParam} left-aligned hex representation of string
* @returns {String} ascii string
* @returns {String} hex string
*/
var formatOutputDynamicBytes = function (param) {
// length might also be important!
return utils.toAscii(param.dynamicPart().slice(64));
var length = (new BigNumber(param.dynamicPart().slice(0, 64), 16)).toNumber() * 2;
return '0x' + param.dynamicPart().substr(64, length);
};
/**
* Should be used to format output string
*
* @method formatOutputString
* @param {SolidityParam} left-aligned hex representation of string
* @returns {String} ascii string
*/
var formatOutputString = function (param) {
var length = (new BigNumber(param.dynamicPart().slice(0, 64), 16)).toNumber() * 2;
return utils.toAscii(param.dynamicPart().substr(64, length));
};
/**
@ -204,6 +232,7 @@ module.exports = {
formatInputInt: formatInputInt,
formatInputBytes: formatInputBytes,
formatInputDynamicBytes: formatInputDynamicBytes,
formatInputString: formatInputString,
formatInputBool: formatInputBool,
formatInputReal: formatInputReal,
formatOutputInt: formatOutputInt,
@ -213,6 +242,7 @@ module.exports = {
formatOutputBool: formatOutputBool,
formatOutputBytes: formatOutputBytes,
formatOutputDynamicBytes: formatOutputDynamicBytes,
formatOutputString: formatOutputString,
formatOutputAddress: formatOutputAddress
};

9
libjsqrc/ethereumjs/lib/solidity/param.js

@ -182,13 +182,14 @@ var getOffset = function (bytes, index) {
*/
SolidityParam.decodeBytes = function (bytes, index) {
index = index || 0;
//TODO add support for strings longer than 32 bytes
//var length = parseInt('0x' + bytes.substr(offset * 64, 64));
var offset = getOffset(bytes, index);
// 2 * , cause we also parse length
return new SolidityParam(bytes.substr(offset * 2, 2 * 64), 0);
var l = parseInt('0x' + bytes.substr(offset * 2, 64));
l = Math.floor((l + 31) / 32);
// (1 + l) * , cause we also parse length
return new SolidityParam(bytes.substr(offset * 2, (1 + l) * 64), 0);
};
/**

24
libjsqrc/ethereumjs/lib/utils/utils.js

@ -75,6 +75,19 @@ var padLeft = function (string, chars, sign) {
return new Array(chars - string.length + 1).join(sign ? sign : "0") + string;
};
/**
* Should be called to pad string to expected length
*
* @method padRight
* @param {String} string to be padded
* @param {Number} characters that result string should have
* @param {String} sign, by default 0
* @returns {String} right aligned string
*/
var padRight = function (string, chars, sign) {
return string + (new Array(chars - string.length + 1).join(sign ? sign : "0"));
};
/**
* Should be called to get sting from it's hex representation
*
@ -91,10 +104,6 @@ var toAscii = function(hex) {
}
for (; i < l; i+=2) {
var code = parseInt(hex.substr(i, 2), 16);
if (code === 0) {
break;
}
str += String.fromCharCode(code);
}
@ -204,7 +213,7 @@ var fromDecimal = function (value) {
* @return {String}
*/
var toHex = function (val) {
/*jshint maxcomplexity:7 */
/*jshint maxcomplexity: 8 */
if (isBoolean(val))
return fromDecimal(+val);
@ -218,9 +227,11 @@ var toHex = function (val) {
// if its a negative number, pass it through fromDecimal
if (isString(val)) {
if (val.indexOf('-0x') === 0)
return fromDecimal(val);
return fromDecimal(val);
else if (!isFinite(val))
return fromAscii(val);
else if(val.indexOf('0x') === 0)
return val;
}
return fromDecimal(val);
@ -471,6 +482,7 @@ var isIBAN = function (iban) {
module.exports = {
padLeft: padLeft,
padRight: padRight,
toHex: toHex,
toDecimal: toDecimal,
fromDecimal: fromDecimal,

2
libjsqrc/ethereumjs/lib/version.json

@ -1,3 +1,3 @@
{
"version": "0.6.0"
"version": "0.7.1"
}

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save