diff --git a/CMakeLists.txt b/CMakeLists.txt index 020e8dc9d..f6b5dbe33 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,7 +5,6 @@ set(CMAKE_AUTOMOC ON) cmake_policy(SET CMP0015 NEW) -set(ETH_VERSION 0.2.9) set(ETH_BUILD_TYPE ${CMAKE_BUILD_TYPE}) # Default HEADLESS to 0. @@ -36,6 +35,8 @@ if ("${TARGET_PLATFORM}" STREQUAL "w64") /usr/x86_64-w64-mingw32 ) + include_directories(/usr/x86_64-w64-mingw32/include/cryptopp) + set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) @@ -65,7 +66,6 @@ set(CMAKE_CXX_FLAGS_MINSIZEREL "-Os -DNDEBUG") set(CMAKE_CXX_FLAGS_RELEASE "-O4 -DNDEBUG") set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O2 -g") -add_definitions("-DETH_VERSION=${ETH_VERSION}") add_definitions("-DETH_BUILD_TYPE=${ETH_BUILD_TYPE}") add_definitions("-DETH_BUILD_PLATFORM=${ETH_BUILD_PLATFORM}") #set(CMAKE_CXX_FLAGS, "${CMAKE_CXX_FLAGS} -DETH_VERSION=${ETH_VERSION} -DETH_BUILD_TYPE=${ETH_BUILD_TYPE} -DETH_BUILD_PLATFORM=${ETH_BUILD_PLATFORM}") diff --git a/Ethereum.sln b/Ethereum.sln deleted file mode 100644 index b8f8fce1d..000000000 --- a/Ethereum.sln +++ /dev/null @@ -1,68 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Express 2013 for Windows Desktop -VisualStudioVersion = 12.0.21005.1 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "LibEthereum", "libethereum\LibEthereum.vcxproj", "{7050C7CF-7551-48BE-8E57-92235906C13A}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TestMining", "test\TestMining.vcxproj", "{02DC8A9B-DEF7-403B-8AE3-EF9680937D96}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Test", "test\Test.vcxproj", "{3F3E389B-88DE-41D5-A73B-4F6036E18B36}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cryptlib", "..\cryptopp562\cryptlib.vcxproj", "{3423EC9A-52E4-4A4D-9753-EDEBC38785EF}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Ethereum", "eth\Ethereum.vcxproj", "{C60C065C-2135-4B2B-AFD4-35FD7AC56B40}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Win32 = Debug|Win32 - Debug|x64 = Debug|x64 - Release|Win32 = Release|Win32 - Release|x64 = Release|x64 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {7050C7CF-7551-48BE-8E57-92235906C13A}.Debug|Win32.ActiveCfg = Debug|Win32 - {7050C7CF-7551-48BE-8E57-92235906C13A}.Debug|Win32.Build.0 = Debug|Win32 - {7050C7CF-7551-48BE-8E57-92235906C13A}.Debug|x64.ActiveCfg = Debug|x64 - {7050C7CF-7551-48BE-8E57-92235906C13A}.Debug|x64.Build.0 = Debug|x64 - {7050C7CF-7551-48BE-8E57-92235906C13A}.Release|Win32.ActiveCfg = Release|Win32 - {7050C7CF-7551-48BE-8E57-92235906C13A}.Release|Win32.Build.0 = Release|Win32 - {7050C7CF-7551-48BE-8E57-92235906C13A}.Release|x64.ActiveCfg = Release|x64 - {7050C7CF-7551-48BE-8E57-92235906C13A}.Release|x64.Build.0 = Release|x64 - {02DC8A9B-DEF7-403B-8AE3-EF9680937D96}.Debug|Win32.ActiveCfg = Debug|Win32 - {02DC8A9B-DEF7-403B-8AE3-EF9680937D96}.Debug|Win32.Build.0 = Debug|Win32 - {02DC8A9B-DEF7-403B-8AE3-EF9680937D96}.Debug|x64.ActiveCfg = Debug|x64 - {02DC8A9B-DEF7-403B-8AE3-EF9680937D96}.Debug|x64.Build.0 = Debug|x64 - {02DC8A9B-DEF7-403B-8AE3-EF9680937D96}.Release|Win32.ActiveCfg = Release|Win32 - {02DC8A9B-DEF7-403B-8AE3-EF9680937D96}.Release|Win32.Build.0 = Release|Win32 - {02DC8A9B-DEF7-403B-8AE3-EF9680937D96}.Release|x64.ActiveCfg = Release|x64 - {02DC8A9B-DEF7-403B-8AE3-EF9680937D96}.Release|x64.Build.0 = Release|x64 - {3F3E389B-88DE-41D5-A73B-4F6036E18B36}.Debug|Win32.ActiveCfg = Debug|Win32 - {3F3E389B-88DE-41D5-A73B-4F6036E18B36}.Debug|Win32.Build.0 = Debug|Win32 - {3F3E389B-88DE-41D5-A73B-4F6036E18B36}.Debug|x64.ActiveCfg = Debug|x64 - {3F3E389B-88DE-41D5-A73B-4F6036E18B36}.Debug|x64.Build.0 = Debug|x64 - {3F3E389B-88DE-41D5-A73B-4F6036E18B36}.Release|Win32.ActiveCfg = Release|Win32 - {3F3E389B-88DE-41D5-A73B-4F6036E18B36}.Release|Win32.Build.0 = Release|Win32 - {3F3E389B-88DE-41D5-A73B-4F6036E18B36}.Release|x64.ActiveCfg = Release|x64 - {3F3E389B-88DE-41D5-A73B-4F6036E18B36}.Release|x64.Build.0 = Release|x64 - {3423EC9A-52E4-4A4D-9753-EDEBC38785EF}.Debug|Win32.ActiveCfg = Debug|Win32 - {3423EC9A-52E4-4A4D-9753-EDEBC38785EF}.Debug|Win32.Build.0 = Debug|Win32 - {3423EC9A-52E4-4A4D-9753-EDEBC38785EF}.Debug|x64.ActiveCfg = Debug|x64 - {3423EC9A-52E4-4A4D-9753-EDEBC38785EF}.Debug|x64.Build.0 = Debug|x64 - {3423EC9A-52E4-4A4D-9753-EDEBC38785EF}.Release|Win32.ActiveCfg = Release|Win32 - {3423EC9A-52E4-4A4D-9753-EDEBC38785EF}.Release|Win32.Build.0 = Release|Win32 - {3423EC9A-52E4-4A4D-9753-EDEBC38785EF}.Release|x64.ActiveCfg = Release|x64 - {3423EC9A-52E4-4A4D-9753-EDEBC38785EF}.Release|x64.Build.0 = Release|x64 - {C60C065C-2135-4B2B-AFD4-35FD7AC56B40}.Debug|Win32.ActiveCfg = Debug|Win32 - {C60C065C-2135-4B2B-AFD4-35FD7AC56B40}.Debug|Win32.Build.0 = Debug|Win32 - {C60C065C-2135-4B2B-AFD4-35FD7AC56B40}.Debug|x64.ActiveCfg = Debug|x64 - {C60C065C-2135-4B2B-AFD4-35FD7AC56B40}.Debug|x64.Build.0 = Debug|x64 - {C60C065C-2135-4B2B-AFD4-35FD7AC56B40}.Release|Win32.ActiveCfg = Release|Win32 - {C60C065C-2135-4B2B-AFD4-35FD7AC56B40}.Release|Win32.Build.0 = Release|Win32 - {C60C065C-2135-4B2B-AFD4-35FD7AC56B40}.Release|x64.ActiveCfg = Release|x64 - {C60C065C-2135-4B2B-AFD4-35FD7AC56B40}.Release|x64.Build.0 = Release|x64 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal diff --git a/README.md b/README.md index cc227afe3..9f1276342 100644 --- a/README.md +++ b/README.md @@ -1,21 +1,27 @@ -# cpp-ethereum +## Ethereum C++ Client. -Ethereum C++ Client. +By Gav Wood, 2014. -Gav Wood, 2014. +Based on a design by Vitalik Buterin. -## Building +Contributors, builders and testers include Eric Lombrozo (cross-compilation), Tim Hughes (MSVC compilation & Dagger testing), Alex Leverington (Clang & Mac building), Marko Simovic (CI) and several others. + +### Building See [Build Instructions](https://github.com/ethereum/cpp-ethereum/wiki/Build-Instructions) and [Compatibility Info and Build Tips](https://github.com/ethereum/cpp-ethereum/wiki/Compatibility-Info-and-Build-Tips). -## Yet To Do +### Testing + +To run the tests, make sure you clone the tests repository from github.com/ethereum to tests is a sibling to cpp-ethereum-build. + +### Yet To Do See [TODO](TODO) -## License +### License See [LICENSE](LICENSE) -## Contributing +### Contributing Please read [CodingStandards.txt](CodingStandards.txt) thoroughly before making alterations to the code base. Please do *NOT* use an editor that automatically reformats whitespace away from astylerc or the formatting guidelines as described in [CodingStandards.txt](CodingStandards.txt). diff --git a/TODO b/TODO index ea09bbc3d..1004190e3 100644 --- a/TODO +++ b/TODO @@ -9,6 +9,7 @@ Crypto stuff: Network: - *** Exponential backoff on bad connection. +- *** Handle exception when no network. - NotInChain will be very bad for new peers - it'll run through until the genesis. - Check how many it has first. - Crypto on network - use id as public key? @@ -18,14 +19,15 @@ Network: - Solid communications? - Strategy for peer suggestion? +Cleanups & caching +- All caches should flush unused data (I'm looking at you, BlockChain) to avoid memory overload. +- State DB should keep only last few N blocks worth of nodes (except for restore points - configurable, defaults to every 30000th block - all blocks that are restore points should be stored so their stateRoots are known good). + THREAD-SAFETY - BlockChain - TransactionQueue - State -CLI client -- Implement CLI option "--help". - General: - Better logging. - Colours. @@ -43,11 +45,10 @@ GUI - Make address/block chain list model-based, JIT populated. - Make everything else model-based - Qt/QML class. +- Turn on/off debug channels. For PoC3: - Shared contract acceptence tests. -- Use mining state for nonce. -BUG: need to discard transactions if nonce too old, even when not mining. ### Marko diff --git a/alethzero/Main.ui b/alethzero/Main.ui index 690a0049f..16a2ad6c4 100644 --- a/alethzero/Main.ui +++ b/alethzero/Main.ui @@ -6,33 +6,38 @@ 0 0 - 1112 - 766 + 1504 + 798 AlethZero Ethereum Client + + true + - QMainWindow::AllowTabbedDocks|QMainWindow::VerticalTabs + QMainWindow::AllowNestedDocks|QMainWindow::AllowTabbedDocks|QMainWindow::VerticalTabs true - - - 4 + + + 0 + + + 0 + + + 0 - + 0 - - - QFrame::NoFrame - - + @@ -51,7 +56,7 @@ - + 1 block @@ -66,8 +71,8 @@ 0 0 - 1112 - 28 + 1504 + 20 @@ -81,14 +86,16 @@ &Network + - &Tools + T&ools + @@ -114,11 +121,23 @@ - + + 0 + + + 0 + + + 0 + + 0 + + Qt::NoFocus + QFrame::NoFrame @@ -139,7 +158,16 @@ - + + 0 + + + 0 + + + 0 + + 0 @@ -148,6 +176,9 @@ Qt::Vertical + + Qt::NoFocus + QFrame::NoFrame @@ -180,21 +211,30 @@ - Ideal Peers + Ideal &Peers + + + idealPeers - Client Name + &Client Name + + + clientName - Listen on + &Listen on + + + port @@ -224,7 +264,16 @@ - + + 0 + + + 0 + + + 0 + + 0 @@ -235,6 +284,9 @@ 12 + + Qt::NoFocus + QFrame::NoFrame @@ -274,11 +326,23 @@ 0 - + + 0 + + + 0 + + + 0 + + 0 + + Qt::NoFocus + QFrame::NoFrame @@ -299,6 +363,7 @@ + @@ -310,8 +375,8 @@ - 408 - 251 + 442 + 360 @@ -325,69 +390,82 @@ - - 0 - - - 4 - - - - - Amount - - - - - - - - - - + + + + + 0 + 0 + - - 430000000 + + &To - - 1000 + + destination - + - Data + &Amount - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + value - - + + - + 0 0 - To + &Data + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + data - - + + + + Qt::Vertical + + + + + Qt::NoFocus + + + true + + + - + - Send + &Send - + + + + + + + + @@ -400,7 +478,23 @@ - + + + + false + + + true + + + + + + + + + + @@ -410,11 +504,17 @@ - - - + + + + + 430000000 + + + 1000 + @@ -432,11 +532,23 @@ - + + 0 + + + 0 + + + 0 + + 0 + + Qt::NoFocus + QFrame::NoFrame @@ -451,6 +563,184 @@ + + + toolBar_2 + + + TopToolBarArea + + + false + + + + + QDockWidget::DockWidgetFeatureMask + + + Contracts + + + 2 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Qt::Horizontal + + + + + 0 + 0 + + + + Qt::NoFocus + + + QFrame::NoFrame + + + + + + 2 + 0 + + + + Qt::NoFocus + + + QFrame::NoFrame + + + true + + + + + + + + + + QDockWidget::DockWidgetFeatureMask + + + Block Chain + + + 2 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Qt::Horizontal + + + + + 0 + 0 + + + + Qt::NoFocus + + + QFrame::NoFrame + + + + + + 2 + 0 + + + + Qt::NoFocus + + + QFrame::NoFrame + + + true + + + + + + + + + + QDockWidget::DockWidgetFeatureMask + + + Settings + + + 2 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Name Registrar + + + + + + + + + &Quit @@ -498,8 +788,34 @@ &About... + + + true + + + &Preview + + + + destination + calculatedName + value + valueUnits + data + code + send + idealPeers + port + clientName + verbosity + transactionQueue + accounts + peers + log + ourAccounts + diff --git a/alethzero/MainWin.cpp b/alethzero/MainWin.cpp index 03aa49321..475c6cffd 100644 --- a/alethzero/MainWin.cpp +++ b/alethzero/MainWin.cpp @@ -1,16 +1,54 @@ #include -#include -#include +#include +#include +#include +#include #include #include +#include #include "MainWin.h" #include "ui_Main.h" using namespace std; -using namespace eth; + +// types +using eth::bytes; +using eth::bytesConstRef; +using eth::h160; +using eth::h256; +using eth::u160; +using eth::u256; +using eth::Address; +using eth::BlockInfo; +using eth::Client; +using eth::Instruction; +using eth::KeyPair; +using eth::NodeMode; +using eth::PeerInfo; +using eth::RLP; +using eth::Secret; +using eth::Transaction; + +// functions +using eth::asHex; +using eth::assemble; +using eth::compileLisp; +using eth::disassemble; +using eth::formatBalance; +using eth::fromUserHex; +using eth::right160; +using eth::simpleDebugOut; +using eth::toLog2; +using eth::toString; +using eth::units; + +// vars +using eth::g_logPost; +using eth::g_logVerbosity; +using eth::c_instructionInfo; static void initUnits(QComboBox* _b) { - for (int n = units().size() - 1; n >= 0; --n) + for (auto n = (::uint)units().size(); n-- != 0; ) _b->addItem(QString::fromStdString(units()[n].second), n); _b->setCurrentIndex(6); } @@ -25,14 +63,57 @@ Main::Main(QWidget *parent) : setWindowFlags(Qt::Window); ui->setupUi(this); g_logPost = [=](std::string const& s, char const* c) { simpleDebugOut(s, c); ui->log->addItem(QString::fromStdString(s)); }; - m_client = new Client("AlethZero"); + m_client.reset(new Client("AlethZero")); + + /* + ui->librariesView->setModel(m_libraryMan); + ui->graphsView->setModel(m_graphMan); + setWindowIcon(QIcon(":/Noted.png")); + + qmlRegisterSingletonType("com.llr", 1, 0, "Time", TimelineItem::constructTimeHelper); + qmlRegisterType("com.llr", 1, 0, "Graph"); + qmlRegisterType("com.llr", 1, 0, "CursorGraph"); + qmlRegisterType("com.llr", 1, 0, "Interval"); + qmlRegisterType("com.llr", 1, 0, "Cursor"); + qmlRegisterType("com.llr", 1, 0, "Timelines"); + qmlRegisterType("com.llr", 1, 0, "TimeLabels"); + qmlRegisterType("com.llr", 1, 0, "XLabels"); + qmlRegisterType("com.llr", 1, 0, "XScale"); + qmlRegisterType("com.llr", 1, 0, "YLabels"); + qmlRegisterType("com.llr", 1, 0, "YScale"); + + m_view = new QQuickView(); + QQmlContext* context = m_view->rootContext(); + context->setContextProperty("libs", libs()); + context->setContextProperty("compute", compute()); + context->setContextProperty("data", data()); + context->setContextProperty("graphs", graphs()); + context->setContextProperty("audio", audio()); + context->setContextProperty("view", view()); + m_view->setSource(QUrl("qrc:/Noted.qml")); + + QWidget* w = QWidget::createWindowContainer(m_view); + w->setAcceptDrops(true); + m_view->setResizeMode(QQuickView::SizeRootObjectToView); + ui->fullDisplay->insertWidget(0, w); + m_view->create(); + + m_timelinesItem = m_view->rootObject()->findChild("timelines"); + qDebug() << m_view->rootObject(); + */ + readSettings(); refresh(); m_refresh = new QTimer(this); connect(m_refresh, SIGNAL(timeout()), SLOT(refresh())); - m_refresh->start(1000); + m_refresh->start(100); + m_refreshNetwork = new QTimer(this); + connect(m_refreshNetwork, SIGNAL(timeout()), SLOT(refreshNetwork())); + m_refreshNetwork->start(1000); + + connect(ui->ourAccounts->model(), SIGNAL(rowsMoved(const QModelIndex &, int, int, const QModelIndex &, int)), SLOT(ourAccountsRowsMoved())); #if ETH_DEBUG m_servers.append("192.168.0.10:30301"); @@ -41,30 +122,67 @@ Main::Main(QWidget *parent) : { m_servers = QString::fromUtf8(_r->readAll()).split("\n", QString::SkipEmptyParts); }); - QNetworkRequest r(QUrl("http://www.ethereum.org/servers.poc2.txt")); + QNetworkRequest r(QUrl("http://www.ethereum.org/servers.poc3.txt")); r.setHeader(QNetworkRequest::UserAgentHeader, "Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1712.0 Safari/537.36"); m_webCtrl.get(r); srand(time(0)); #endif on_verbosity_sliderMoved(); - initUnits(ui->valueUnits); + on_destination_textChanged(); + statusBar()->addPermanentWidget(ui->balance); statusBar()->addPermanentWidget(ui->peerCount); - statusBar()->addPermanentWidget(ui->blockChain); + statusBar()->addPermanentWidget(ui->blockCount); } Main::~Main() { g_logPost = simpleDebugOut; writeSettings(); - delete ui; +} + +QString Main::pretty(eth::Address _a) const +{ + if (h256 n = state().contractMemory(m_nameReg, (h256)(u256)(u160)_a)) + { + std::string s((char const*)n.data(), 32); + if (s.find_first_of('\0') != string::npos) + s.resize(s.find_first_of('\0')); + return QString::fromStdString(s); + } + return QString(); +} + +QString Main::render(eth::Address _a) const +{ + QString p = pretty(_a); + if (!p.isNull()) + return p + " (" + QString::fromStdString(_a.abridged()) + ")"; + return QString::fromStdString(_a.abridged()); +} + +Address Main::fromString(QString const& _a) const +{ + string sn = _a.toStdString(); + if (sn.size() > 32) + sn.resize(32); + h256 n; + memcpy(n.data(), sn.data(), sn.size()); + memset(n.data() + sn.size(), 0, 32 - sn.size()); + if (_a.size()) + if (h256 a = state().contractMemory(m_nameReg, n)) + return right160(a); + if (_a.size() == 40) + return Address(fromUserHex(_a.toStdString())); + else + return Address(); } void Main::on_about_triggered() { - QMessageBox::about(this, "About AlethZero PoC-2", "AlethZero/v" ADD_QUOTES(ETH_VERSION) "/" ADD_QUOTES(ETH_BUILD_TYPE) "/" ADD_QUOTES(ETH_BUILD_PLATFORM) "\nBy Gav Wood, 2014.\nBased on a design by Vitalik Buterin.\n\nTeam Ethereum++ includes: Eric Lombrozo, Marko Simovic, Alex Leverington and several others."); + QMessageBox::about(this, "About AlethZero PoC-3", "AlethZero/v" ADD_QUOTES(ETH_VERSION) "/" ADD_QUOTES(ETH_BUILD_TYPE) "/" ADD_QUOTES(ETH_BUILD_PLATFORM) "\nBy Gav Wood, 2014.\nBased on a design by Vitalik Buterin.\n\nTeam Ethereum++ includes: Eric Lombrozo, Marko Simovic, Alex Leverington, Tim Hughes and several others."); } void Main::writeSettings() @@ -88,12 +206,14 @@ void Main::writeSettings() if (m_client->peerServer()) { bytes d = m_client->peerServer()->savePeers(); - m_peers = QByteArray((char*)d.data(), d.size()); + m_peers = QByteArray((char*)d.data(), (int)d.size()); } s.setValue("peers", m_peers); + s.setValue("nameReg", ui->nameReg->text()); s.setValue("geometry", saveGeometry()); + s.setValue("windowState", saveState()); } void Main::readSettings() @@ -101,6 +221,8 @@ void Main::readSettings() QSettings s("ethereum", "alethzero"); restoreGeometry(s.value("geometry").toByteArray()); + restoreState(s.value("windowState").toByteArray()); + QByteArray b = s.value("address").toByteArray(); if (b.isEmpty()) @@ -120,64 +242,255 @@ void Main::readSettings() ui->clientName->setText(s.value("clientName", "").toString()); ui->idealPeers->setValue(s.value("idealPeers", ui->idealPeers->value()).toInt()); ui->port->setValue(s.value("port", ui->port->value()).toInt()); + ui->nameReg->setText(s.value("nameReg", "11f62328e131dbb05ce4c73a3de3c7ab1c84a163").toString()); } -void Main::refresh() +void Main::on_nameReg_textChanged() { - m_client->lock(); - //if (m_client->changed()) + string s = ui->nameReg->text().toStdString(); + if (s.size() == 40) { - ui->peerCount->setText(QString::fromStdString(toString(m_client->peerCount())) + " peer(s)"); - ui->peers->clear(); - for (PeerInfo const& i: m_client->peers()) - ui->peers->addItem(QString("%3 ms - %1:%2 - %4").arg(i.host.c_str()).arg(i.port).arg(chrono::duration_cast(i.lastPing).count()).arg(i.clientVersion.c_str())); + m_nameReg = Address(fromUserHex(s)); + refresh(true); + } +} +void Main::refreshNetwork() +{ + auto ps = m_client->peers(); + + ui->peerCount->setText(QString::fromStdString(toString(ps.size())) + " peer(s)"); + ui->peers->clear(); + for (PeerInfo const& i: ps) + ui->peers->addItem(QString("%3 ms - %1:%2 - %4").arg(i.host.c_str()).arg(i.port).arg(chrono::duration_cast(i.lastPing).count()).arg(i.clientVersion.c_str())); +} + +eth::State const& Main::state() const +{ + return ui->preview->isChecked() ? m_client->postState() : m_client->state(); +} + +void Main::refresh(bool _override) +{ + m_client->lock(); + auto const& st = state(); + + bool c = m_client->changed(); + if (c || _override) + { auto d = m_client->blockChain().details(); auto diff = BlockInfo(m_client->blockChain().block()).difficulty; - ui->blockChain->setText(QString("#%1 @%3 T%2").arg(d.number).arg(toLog2(d.totalDifficulty)).arg(toLog2(diff))); + ui->blockCount->setText(QString("#%1 @%3 T%2").arg(d.number).arg(toLog2(d.totalDifficulty)).arg(toLog2(diff))); - auto acs = m_client->state().addresses(); + auto acs = st.addresses(); ui->accounts->clear(); - for (auto i: acs) - ui->accounts->addItem(QString("%1 @ %2").arg(formatBalance(i.second).c_str()).arg(asHex(i.first.asArray()).c_str())); + ui->contracts->clear(); + for (auto n = 0; n < 2; ++n) + for (auto i: acs) + { + auto r = render(i.first); + if (r.contains('(') == !n) + { + (new QListWidgetItem(QString("%2: %1 [%3]").arg(formatBalance(i.second).c_str()).arg(r).arg((unsigned)state().transactionsFrom(i.first)), ui->accounts)) + ->setData(Qt::UserRole, QByteArray((char const*)i.first.data(), Address::size)); + if (st.isContractAddress(i.first)) + (new QListWidgetItem(QString("%2: %1 [%3]").arg(formatBalance(i.second).c_str()).arg(r).arg((unsigned)st.transactionsFrom(i.first)), ui->contracts)) + ->setData(Qt::UserRole, QByteArray((char const*)i.first.data(), Address::size)); + } + } ui->transactionQueue->clear(); - for (pair const& i: m_client->pending()) + for (Transaction const& t: m_client->pending()) { - ui->transactionQueue->addItem(QString("%1 [%4] @ %2 <- %3") - .arg(formatBalance(i.second.value).c_str()) - .arg(asHex(i.second.receiveAddress.asArray()).c_str()) - .arg(asHex(i.second.sender().asArray()).c_str()) - .arg((unsigned)i.second.nonce)); + QString s = t.receiveAddress ? + QString("%2 %5> %3: %1 [%4]") + .arg(formatBalance(t.value).c_str()) + .arg(render(t.safeSender())) + .arg(render(t.receiveAddress)) + .arg((unsigned)t.nonce) + .arg(st.isContractAddress(t.receiveAddress) ? '*' : '-') : + QString("%2 +> %3: %1 [%4]") + .arg(formatBalance(t.value).c_str()) + .arg(render(t.safeSender())) + .arg(render(right160(t.sha3()))) + .arg((unsigned)t.nonce); + ui->transactionQueue->addItem(s); } - ui->transactions->clear(); + ui->blocks->clear(); auto const& bc = m_client->blockChain(); for (auto h = bc.currentHash(); h != bc.genesisHash(); h = bc.details(h).parent) { auto d = bc.details(h); - ui->transactions->addItem(QString("# %1 ==== %2").arg(d.number).arg(asHex(h.asArray()).c_str())); + QListWidgetItem* blockItem = new QListWidgetItem(QString("#%1 %2").arg(d.number).arg(h.abridged().c_str()), ui->blocks); + blockItem->setData(Qt::UserRole, QByteArray((char const*)h.data(), h.size)); + int n = 0; for (auto const& i: RLP(bc.block(h))[1]) { Transaction t(i.data()); - ui->transactions->addItem(QString("%1 [%4] @ %2 <- %3") - .arg(formatBalance(t.value).c_str()) - .arg(asHex(t.receiveAddress.asArray()).c_str()) - .arg(asHex(t.sender().asArray()).c_str()) - .arg((unsigned)t.nonce)); + QString s = t.receiveAddress ? + QString(" %2 %5> %3: %1 [%4]") + .arg(formatBalance(t.value).c_str()) + .arg(render(t.safeSender())) + .arg(render(t.receiveAddress)) + .arg((unsigned)t.nonce) + .arg(st.isContractAddress(t.receiveAddress) ? '*' : '-') : + QString(" %2 +> %3: %1 [%4]") + .arg(formatBalance(t.value).c_str()) + .arg(render(t.safeSender())) + .arg(render(right160(t.sha3()))) + .arg((unsigned)t.nonce); + QListWidgetItem* txItem = new QListWidgetItem(s, ui->blocks); + txItem->setData(Qt::UserRole, QByteArray((char const*)h.data(), h.size)); + txItem->setData(Qt::UserRole + 1, n); + n++; } } } - ui->ourAccounts->clear(); - u256 totalBalance = 0; - for (auto i: m_myKeys) + if (c || m_keysChanged || _override) + { + m_keysChanged = false; + ui->ourAccounts->clear(); + u256 totalBalance = 0; + for (auto i: m_myKeys) + { + u256 b = st.balance(i.address()); + (new QListWidgetItem(QString("%2: %1 [%3]").arg(formatBalance(b).c_str()).arg(render(i.address())).arg((unsigned)st.transactionsFrom(i.address())), ui->ourAccounts)) + ->setData(Qt::UserRole, QByteArray((char const*)i.address().data(), Address::size)); + totalBalance += b; + } + ui->balance->setText(QString::fromStdString(formatBalance(totalBalance))); + } + m_client->unlock(); +} + +void Main::ourAccountsRowsMoved() +{ + QVector myKeys; + for (int i = 0; i < ui->ourAccounts->count(); ++i) + { + auto hba = ui->ourAccounts->item(i)->data(Qt::UserRole).toByteArray(); + auto h = Address((byte const*)hba.data(), Address::ConstructFromPointer); + for (auto i: m_myKeys) + if (i.address() == h) + myKeys.push_back(i); + } + m_myKeys = myKeys; +} + +void Main::on_blocks_currentItemChanged() +{ + ui->info->clear(); + m_client->lock(); + if (auto item = ui->blocks->currentItem()) { - u256 b = m_client->state().balance(i.address()); - ui->ourAccounts->addItem(QString("%1 @ %2").arg(formatBalance(b).c_str()).arg(asHex(i.address().asArray()).c_str())); - totalBalance += b; + auto hba = item->data(Qt::UserRole).toByteArray(); + assert(hba.size() == 32); + auto h = h256((byte const*)hba.data(), h256::ConstructFromPointer); + auto details = m_client->blockChain().details(h); + auto blockData = m_client->blockChain().block(h); + auto block = RLP(blockData); + BlockInfo info(blockData); + + stringstream s; + + if (item->data(Qt::UserRole + 1).isNull()) + { + char timestamp[64]; + time_t rawTime = (time_t)(uint64_t)info.timestamp; + strftime(timestamp, 64, "%c", localtime(&rawTime)); + s << "

" << h << "

"; + s << "

#" << details.number; + s << "   " << timestamp << "

"; + s << "
D/TD: 2^" << log2((double)info.difficulty) << "/2^" << log2((double)details.totalDifficulty) << ""; + s << "   Children: " << details.children.size() << ""; + s << "
Coinbase: " << pretty(info.coinbaseAddress).toStdString() << " " << info.coinbaseAddress; + s << "
State: " << info.stateRoot << ""; + s << "
Nonce: " << info.nonce << ""; + s << "
Transactions: " << block[1].itemCount() << " @" << info.sha3Transactions << ""; + s << "
Uncles: " << block[2].itemCount() << " @" << info.sha3Uncles << ""; + } + else + { + unsigned txi = item->data(Qt::UserRole + 1).toInt(); + Transaction tx(block[1][txi].data()); + h256 th = tx.sha3(); + s << "

" << th << "

"; + s << "

" << h << "[" << txi << "]

"; + auto ss = tx.safeSender(); + s << "
From: " << pretty(ss).toStdString() << " " << ss; + if (tx.receiveAddress) + s << "
To: " << pretty(tx.receiveAddress).toStdString() << " " << tx.receiveAddress; + else + s << "
Creates: " << pretty(right160(th)).toStdString() << " " << right160(th); + s << "
Value: " << formatBalance(tx.value) << ""; + s << "   #" << tx.nonce << ""; + if (tx.data.size()) + { + s << "
Data:   "; +// for (auto i: tx.data) +// s << "0x" << hex << i << " "; + s << "
" << disassemble(tx.data); + } + } + + + ui->info->appendHtml(QString::fromStdString(s.str())); + } + m_client->unlock(); +} + +void Main::on_contracts_currentItemChanged() +{ + ui->contractInfo->clear(); + m_client->lock(); + if (auto item = ui->contracts->currentItem()) + { + auto hba = item->data(Qt::UserRole).toByteArray(); + assert(hba.size() == 20); + auto h = h160((byte const*)hba.data(), h160::ConstructFromPointer); + + stringstream s; + auto mem = state().contractMemory(h); + u256 next = 0; + unsigned numerics = 0; + bool unexpectedNumeric = false; + for (auto i: mem) + { + if (next < i.first) + { + unsigned j; + for (j = 0; j <= numerics && next + j < i.first; ++j) + s << (j < numerics || unexpectedNumeric ? " 0" : " STOP"); + unexpectedNumeric = false; + numerics -= min(numerics, j); + if (next + j < i.first) + s << " ...
@" << showbase << hex << i.first << "    "; + } + else if (!next) + { + s << "@" << showbase << hex << i.first << "    "; + } + auto iit = c_instructionInfo.find((Instruction)(unsigned)i.second); + if (numerics || iit == c_instructionInfo.end() || (u256)(unsigned)iit->first != i.second) // not an instruction or expecting an argument... + { + if (numerics) + numerics--; + else + unexpectedNumeric = true; + s << " " << showbase << hex << i.second; + } + else + { + auto const& ii = iit->second; + s << " " << ii.name << ""; + numerics = ii.additional; + } + next = i.first + 1; + } + ui->contractInfo->appendHtml(QString::fromStdString(s.str())); } - ui->balance->setText(QString::fromStdString(formatBalance(totalBalance))); m_client->unlock(); } @@ -189,7 +502,9 @@ void Main::on_idealPeers_valueChanged() void Main::on_ourAccounts_doubleClicked() { - qApp->clipboard()->setText(ui->ourAccounts->currentItem()->text().section(" @ ", 1)); + auto hba = ui->ourAccounts->currentItem()->data(Qt::UserRole).toByteArray(); + auto h = Address((byte const*)hba.data(), Address::ConstructFromPointer); + qApp->clipboard()->setText(QString::fromStdString(asHex(h.asArray()))); } void Main::on_log_doubleClicked() @@ -199,23 +514,41 @@ void Main::on_log_doubleClicked() void Main::on_accounts_doubleClicked() { - qApp->clipboard()->setText(ui->accounts->currentItem()->text().section(" @ ", 1)); + auto hba = ui->accounts->currentItem()->data(Qt::UserRole).toByteArray(); + auto h = Address((byte const*)hba.data(), Address::ConstructFromPointer); + qApp->clipboard()->setText(QString::fromStdString(asHex(h.asArray()))); +} + +void Main::on_contracts_doubleClicked() +{ + auto hba = ui->contracts->currentItem()->data(Qt::UserRole).toByteArray(); + auto h = Address((byte const*)hba.data(), Address::ConstructFromPointer); + qApp->clipboard()->setText(QString::fromStdString(asHex(h.asArray()))); } void Main::on_destination_textChanged() { + if (ui->destination->text().size()) + if (Address a = fromString(ui->destination->text())) + ui->calculatedName->setText(render(a)); + else + ui->calculatedName->setText("Unknown Address"); + else + ui->calculatedName->setText("Create Contract"); updateFee(); } void Main::on_data_textChanged() { - m_data = ui->data->toPlainText().split(QRegExp("[^0-9a-fA-Fx]+"), QString::SkipEmptyParts); + string code = ui->data->toPlainText().toStdString(); + m_data = code[0] == '(' ? compileLisp(code, true) : assemble(code, true); + ui->code->setPlainText(QString::fromStdString(disassemble(m_data))); updateFee(); } u256 Main::fee() const { - return (ui->destination->text().isEmpty() || !ui->destination->text().toInt()) ? m_client->state().fee(m_data.size()) : m_client->state().fee(); + return (ui->destination->text().isEmpty() || !ui->destination->text().toInt()) ? state().fee(m_data.size()) : state().fee(); } u256 Main::value() const @@ -236,7 +569,7 @@ void Main::updateFee() bool ok = false; for (auto i: m_myKeys) - if (m_client->state().balance(i.address()) >= totalReq) + if (state().balance(i.address()) >= totalReq) { ok = true; break; @@ -278,7 +611,7 @@ void Main::on_connect_triggered() if (ok && s.contains(":")) { string host = s.section(":", 0, 0).toStdString(); - short port = s.section(":", 1).toInt(); + unsigned short port = s.section(":", 1).toInt(); m_client->connect(host, port); } } @@ -304,24 +637,12 @@ void Main::on_send_clicked() u256 totalReq = value() + fee(); m_client->lock(); for (auto i: m_myKeys) - if (m_client->state().balance(i.address()) >= totalReq && i.address() != Address(fromUserHex(ui->destination->text().toStdString()))) + if (m_client->state().balance(i.address()) >= totalReq ) { m_client->unlock(); Secret s = i.secret(); - Address r = Address(fromUserHex(ui->destination->text().toStdString())); - u256s data; - data.reserve(m_data.size()); - for (QString const& i: m_data) - { - u256 d = 0; - try - { - d = u256(i.toStdString()); - } - catch (...) {} - data.push_back(d); - } - m_client->transact(s, r, value(), data); + Address r = fromString(ui->destination->text()); + m_client->transact(s, r, value(), m_data); refresh(); return; } @@ -332,4 +653,32 @@ void Main::on_send_clicked() void Main::on_create_triggered() { m_myKeys.append(KeyPair::create()); + m_keysChanged = true; } + +// extra bits needed to link on VS +#ifdef _MSC_VER + +// include moc file, ofuscated to hide from automoc +#include\ +"moc_MainWin.cpp" + +// specify library dependencies, it's easier to do here than in the project since we can control the "d" debug suffix +#ifdef _DEBUG +#define QTLIB(x) x"d.lib" +#else +#define QTLIB(x) x".lib" +#endif + +#pragma comment(lib, QTLIB("Qt5PlatformSupport")) +#pragma comment(lib, QTLIB("Qt5Core")) +#pragma comment(lib, QTLIB("Qt5GUI")) +#pragma comment(lib, QTLIB("Qt5Widgets")) +#pragma comment(lib, QTLIB("Qt5Network")) +#pragma comment(lib, QTLIB("qwindows")) +#pragma comment(lib, "Imm32.lib") +#pragma comment(lib, "opengl32.lib") +#pragma comment(lib, "winmm.lib") + + +#endif diff --git a/alethzero/MainWin.h b/alethzero/MainWin.h index 0a76c6c75..70b2d051c 100644 --- a/alethzero/MainWin.h +++ b/alethzero/MainWin.h @@ -2,9 +2,9 @@ #define MAIN_H #include -#include -#include -#include +#include +#include +#include #include namespace Ui { @@ -13,6 +13,7 @@ class Main; namespace eth { class Client; +class State; } class Main : public QMainWindow @@ -31,6 +32,7 @@ private slots: void on_net_triggered(); void on_verbosity_sliderMoved(); void on_ourAccounts_doubleClicked(); + void ourAccountsRowsMoved(); void on_accounts_doubleClicked(); void on_destination_textChanged(); void on_data_textChanged(); @@ -38,12 +40,25 @@ private slots: void on_value_valueChanged() { updateFee(); } void on_valueUnits_currentIndexChanged() { updateFee(); } void on_log_doubleClicked(); + void on_blocks_currentItemChanged(); + void on_contracts_doubleClicked(); + void on_contracts_currentItemChanged(); void on_about_triggered(); + void on_nameReg_textChanged(); + void on_preview_triggered() { refresh(true); } void on_quit_triggered() { close(); } - void refresh(); + void refresh(bool _override = false); + void refreshNetwork(); private: + QString pretty(eth::Address _a) const; + + QString render(eth::Address _a) const; + eth::Address fromString(QString const& _a) const; + + eth::State const& state() const; + void updateFee(); void readSettings(); void writeSettings(); @@ -52,16 +67,19 @@ private: eth::u256 total() const; eth::u256 value() const; - Ui::Main *ui; + std::unique_ptr ui; - eth::Client* m_client; + std::unique_ptr m_client; QByteArray m_peers; QMutex m_guiLock; QTimer* m_refresh; + QTimer* m_refreshNetwork; QStringList m_servers; QVector m_myKeys; - QStringList m_data; + bool m_keysChanged = false; + eth::u256s m_data; + eth::Address m_nameReg; QNetworkAccessManager m_webCtrl; }; diff --git a/alethzero/main.cpp b/alethzero/main.cpp index ce37f9905..42afd5e66 100644 --- a/alethzero/main.cpp +++ b/alethzero/main.cpp @@ -1,5 +1,5 @@ #include "MainWin.h" -#include +#include int main(int argc, char *argv[]) { diff --git a/eth/main.cpp b/eth/main.cpp index 37e643b68..d012961cd 100644 --- a/eth/main.cpp +++ b/eth/main.cpp @@ -35,26 +35,6 @@ using namespace eth; #define ADD_QUOTES_HELPER(s) #s #define ADD_QUOTES(s) ADD_QUOTES_HELPER(s) -bytes contents(std::string const& _file) -{ - std::ifstream is(_file, std::ifstream::binary); - if (!is) - return bytes(); - // get length of file: - is.seekg (0, is.end); - int length = is.tellg(); - is.seekg (0, is.beg); - bytes ret(length); - is.read((char*)ret.data(), length); - is.close(); - return ret; -} - -void writeFile(std::string const& _file, bytes const& _data) -{ - ofstream(_file, ios::trunc).write((char const*)_data.data(), _data.size()); -} - bool isTrue(std::string const& _m) { return _m == "on" || _m == "yes" || _m == "true" || _m == "1"; @@ -68,82 +48,26 @@ bool isFalse(std::string const& _m) void help() { cout - << "Usage eth [OPTIONS] " << endl - << "Options:" << endl - << " -l,--listen Listen on the given port for incoming connected (default: 30303)." << endl - << " -l,--listen Listen on the given port for incoming connected (default: 30303)." << endl - << " -l,--listen Listen on the given port for incoming connected (default: 30303)." << endl - ; -/* - if ((arg == "-l" || arg == "--listen" || arg == "--listen-port") && i + 1 < argc) - listenPort = atoi(argv[++i]); - else if ((arg == "-u" || arg == "--public-ip" || arg == "--public") && i + 1 < argc) - publicIP = argv[++i]; - else if ((arg == "-r" || arg == "--remote") && i + 1 < argc) - remoteHost = argv[++i]; - else if ((arg == "-p" || arg == "--port") && i + 1 < argc) - remotePort = atoi(argv[++i]); - else if ((arg == "-n" || arg == "--upnp") && i + 1 < argc) - { - string m = argv[++i]; - if (isTrue(m)) - upnp = true; - else if (isFalse(m)) - upnp = false; - else - { - cerr << "Invalid UPnP option: " << m << endl; - return -1; - } - } - else if ((arg == "-c" || arg == "--client-name") && i + 1 < argc) - clientName = argv[++i]; - else if ((arg == "-a" || arg == "--address" || arg == "--coinbase-address") && i + 1 < argc) - coinbase = h160(fromUserHex(argv[++i])); - else if ((arg == "-s" || arg == "--secret") && i + 1 < argc) - us = KeyPair(h256(fromUserHex(argv[++i]))); - else if (arg == "-i" || arg == "--interactive") - interactive = true; - else if ((arg == "-d" || arg == "--path" || arg == "--db-path") && i + 1 < argc) - dbPath = argv[++i]; - else if ((arg == "-m" || arg == "--mining") && i + 1 < argc) - { - string m = argv[++i]; - if (isTrue(m)) - mining = ~(eth::uint)0; - else if (isFalse(m)) - mining = 0; - else if (int i = stoi(m)) - mining = i; - else - { - cerr << "Unknown mining option: " << m << endl; - return -1; - } - } - else if ((arg == "-v" || arg == "--verbosity") && i + 1 < argc) - g_logVerbosity = atoi(argv[++i]); - else if ((arg == "-x" || arg == "--peers") && i + 1 < argc) - peers = atoi(argv[++i]); - else if ((arg == "-o" || arg == "--mode") && i + 1 < argc) - { - string m = argv[++i]; - if (m == "full") - mode = NodeMode::Full; - else if (m == "peer") - mode = NodeMode::PeerServer; - else - { - cerr << "Unknown mode: " << m << endl; - return -1; - } - } - else if (arg == "-h" || arg == "--help") - help(); - else if (arg == "-V" || arg == "--version") - version(); -*/ - exit(0); + << "Usage eth [OPTIONS] " << endl + << "Options:" << endl + << " -a,--address Set the coinbase (mining payout) address to addr (default: auto)." << endl + << " -c,--client-name Add a name to your client's version string (default: blank)." << endl + << " -d,--db-path Load database from path (default: ~/.ethereum " << endl + << " /Etherum or Library/Application Support/Ethereum)." << endl + << " -h,--help Show this help message and exit." << endl + << " -i,--interactive Enter interactive mode (default: non-interactive)." << endl + << " -l,--listen Listen on the given port for incoming connected (default: 30303)." << endl + << " -m,--mining Enable mining, optionally for a specified number of blocks (Default: off)" << endl + << " -n,--upnp Use upnp for NAT (default: on)." << endl + << " -o,--mode Start a full node or a peer node (Default: full)." << endl + << " -p,--port Connect to remote port (default: 30303)." << endl + << " -r,--remote Connect to remote host (default: none)." << endl + << " -s,--secret Set the secret key for use with send command (default: auto)." << endl + << " -u,--public-ip Force public ip to given (default; auto)." << endl + << " -v,--verbosity <0 - 9> Set the log verbosity from 0 to 9 (Default: 8)." << endl + << " -x,--peers Attempt to connect to given number of peers (Default: 5)." << endl + << " -V,--version Show the version and exit." << endl; + exit(0); } void version() @@ -155,9 +79,9 @@ void version() int main(int argc, char** argv) { - short listenPort = 30303; + unsigned short listenPort = 30303; string remoteHost; - short remotePort = 30303; + unsigned short remotePort = 30303; bool interactive = false; string dbPath; eth::uint mining = ~(eth::uint)0; @@ -193,13 +117,13 @@ int main(int argc, char** argv) { string arg = argv[i]; if ((arg == "-l" || arg == "--listen" || arg == "--listen-port") && i + 1 < argc) - listenPort = atoi(argv[++i]); + listenPort = (short)atoi(argv[++i]); else if ((arg == "-u" || arg == "--public-ip" || arg == "--public") && i + 1 < argc) publicIP = argv[++i]; else if ((arg == "-r" || arg == "--remote") && i + 1 < argc) remoteHost = argv[++i]; else if ((arg == "-p" || arg == "--port") && i + 1 < argc) - remotePort = atoi(argv[++i]); + remotePort = (short)atoi(argv[++i]); else if ((arg == "-n" || arg == "--upnp") && i + 1 < argc) { string m = argv[++i]; @@ -282,14 +206,14 @@ int main(int argc, char** argv) { eth::uint port; cin >> port; - c.startNetwork(port); + c.startNetwork((short)port); } else if (cmd == "connect") { string addr; eth::uint port; cin >> addr >> port; - c.connect(addr, port); + c.connect(addr, (short)port); } else if (cmd == "netstop") { diff --git a/json_spirit/CMakeLists.txt b/json_spirit/CMakeLists.txt new file mode 100644 index 000000000..19cfcf2ce --- /dev/null +++ b/json_spirit/CMakeLists.txt @@ -0,0 +1,10 @@ +SET(JSON_SPIRIT_SRCS +json_spirit_reader.cpp +json_spirit_value.cpp +json_spirit_writer.cpp) + +FIND_PACKAGE(Boost 1.34 REQUIRED) +INCLUDE_DIRECTORIES(${Boost_INCLUDE_DIR}) + +ADD_LIBRARY(json_spirit STATIC ${JSON_SPIRIT_SRCS}) + diff --git a/json_spirit/json_spirit.h b/json_spirit/json_spirit.h new file mode 100644 index 000000000..7dac05c36 --- /dev/null +++ b/json_spirit/json_spirit.h @@ -0,0 +1,18 @@ +#ifndef JSON_SPIRIT +#define JSON_SPIRIT + +// Copyright John W. Wilkinson 2007 - 2009. +// Distributed under the MIT License, see accompanying file LICENSE.txt + +// json spirit version 4.03 + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +# pragma once +#endif + +#include "json_spirit_value.h" +#include "json_spirit_reader.h" +#include "json_spirit_writer.h" +#include "json_spirit_utils.h" + +#endif diff --git a/json_spirit/json_spirit.vcproj b/json_spirit/json_spirit.vcproj new file mode 100644 index 000000000..ed0e5e0c6 --- /dev/null +++ b/json_spirit/json_spirit.vcproj @@ -0,0 +1,209 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/json_spirit/json_spirit_error_position.h b/json_spirit/json_spirit_error_position.h new file mode 100644 index 000000000..4a535ff51 --- /dev/null +++ b/json_spirit/json_spirit_error_position.h @@ -0,0 +1,54 @@ +#ifndef JSON_SPIRIT_ERROR_POSITION +#define JSON_SPIRIT_ERROR_POSITION + +// Copyright John W. Wilkinson 2007 - 2009. +// Distributed under the MIT License, see accompanying file LICENSE.txt + +// json spirit version 4.03 + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +# pragma once +#endif + +#include + +namespace json_spirit +{ + // An Error_position exception is thrown by the "read_or_throw" functions below on finding an error. + // Note the "read_or_throw" functions are around 3 times slower than the standard functions "read" + // functions that return a bool. + // + struct Error_position + { + Error_position(); + Error_position( unsigned int line, unsigned int column, const std::string& reason ); + bool operator==( const Error_position& lhs ) const; + unsigned int line_; + unsigned int column_; + std::string reason_; + }; + + inline Error_position::Error_position() + : line_( 0 ) + , column_( 0 ) + { + } + + inline Error_position::Error_position( unsigned int line, unsigned int column, const std::string& reason ) + : line_( line ) + , column_( column ) + , reason_( reason ) + { + } + + inline bool Error_position::operator==( const Error_position& lhs ) const + { + if( this == &lhs ) return true; + + return ( reason_ == lhs.reason_ ) && + ( line_ == lhs.line_ ) && + ( column_ == lhs.column_ ); +} +} + +#endif diff --git a/json_spirit/json_spirit_reader.cpp b/json_spirit/json_spirit_reader.cpp new file mode 100644 index 000000000..8e2fb5e2a --- /dev/null +++ b/json_spirit/json_spirit_reader.cpp @@ -0,0 +1,137 @@ +// Copyright John W. Wilkinson 2007 - 2009. +// Distributed under the MIT License, see accompanying file LICENSE.txt + +// json spirit version 4.03 + +#include "json_spirit_reader.h" +#include "json_spirit_reader_template.h" + +using namespace json_spirit; + +bool json_spirit::read( const std::string& s, Value& value ) +{ + return read_string( s, value ); +} + +void json_spirit::read_or_throw( const std::string& s, Value& value ) +{ + read_string_or_throw( s, value ); +} + +bool json_spirit::read( std::istream& is, Value& value ) +{ + return read_stream( is, value ); +} + +void json_spirit::read_or_throw( std::istream& is, Value& value ) +{ + read_stream_or_throw( is, value ); +} + +bool json_spirit::read( std::string::const_iterator& begin, std::string::const_iterator end, Value& value ) +{ + return read_range( begin, end, value ); +} + +void json_spirit::read_or_throw( std::string::const_iterator& begin, std::string::const_iterator end, Value& value ) +{ + begin = read_range_or_throw( begin, end, value ); +} + +#ifndef BOOST_NO_STD_WSTRING + +bool json_spirit::read( const std::wstring& s, wValue& value ) +{ + return read_string( s, value ); +} + +void json_spirit::read_or_throw( const std::wstring& s, wValue& value ) +{ + read_string_or_throw( s, value ); +} + +bool json_spirit::read( std::wistream& is, wValue& value ) +{ + return read_stream( is, value ); +} + +void json_spirit::read_or_throw( std::wistream& is, wValue& value ) +{ + read_stream_or_throw( is, value ); +} + +bool json_spirit::read( std::wstring::const_iterator& begin, std::wstring::const_iterator end, wValue& value ) +{ + return read_range( begin, end, value ); +} + +void json_spirit::read_or_throw( std::wstring::const_iterator& begin, std::wstring::const_iterator end, wValue& value ) +{ + begin = read_range_or_throw( begin, end, value ); +} + +#endif + +bool json_spirit::read( const std::string& s, mValue& value ) +{ + return read_string( s, value ); +} + +void json_spirit::read_or_throw( const std::string& s, mValue& value ) +{ + read_string_or_throw( s, value ); +} + +bool json_spirit::read( std::istream& is, mValue& value ) +{ + return read_stream( is, value ); +} + +void json_spirit::read_or_throw( std::istream& is, mValue& value ) +{ + read_stream_or_throw( is, value ); +} + +bool json_spirit::read( std::string::const_iterator& begin, std::string::const_iterator end, mValue& value ) +{ + return read_range( begin, end, value ); +} + +void json_spirit::read_or_throw( std::string::const_iterator& begin, std::string::const_iterator end, mValue& value ) +{ + begin = read_range_or_throw( begin, end, value ); +} + +#ifndef BOOST_NO_STD_WSTRING + +bool json_spirit::read( const std::wstring& s, wmValue& value ) +{ + return read_string( s, value ); +} + +void json_spirit::read_or_throw( const std::wstring& s, wmValue& value ) +{ + read_string_or_throw( s, value ); +} + +bool json_spirit::read( std::wistream& is, wmValue& value ) +{ + return read_stream( is, value ); +} + +void json_spirit::read_or_throw( std::wistream& is, wmValue& value ) +{ + read_stream_or_throw( is, value ); +} + +bool json_spirit::read( std::wstring::const_iterator& begin, std::wstring::const_iterator end, wmValue& value ) +{ + return read_range( begin, end, value ); +} + +void json_spirit::read_or_throw( std::wstring::const_iterator& begin, std::wstring::const_iterator end, wmValue& value ) +{ + begin = read_range_or_throw( begin, end, value ); +} + +#endif diff --git a/json_spirit/json_spirit_reader.h b/json_spirit/json_spirit_reader.h new file mode 100644 index 000000000..a58bfc10f --- /dev/null +++ b/json_spirit/json_spirit_reader.h @@ -0,0 +1,62 @@ +#ifndef JSON_SPIRIT_READER +#define JSON_SPIRIT_READER + +// Copyright John W. Wilkinson 2007 - 2009. +// Distributed under the MIT License, see accompanying file LICENSE.txt + +// json spirit version 4.03 + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +# pragma once +#endif + +#include "json_spirit_value.h" +#include "json_spirit_error_position.h" +#include + +namespace json_spirit +{ + // functions to reads a JSON values + + bool read( const std::string& s, Value& value ); + bool read( std::istream& is, Value& value ); + bool read( std::string::const_iterator& begin, std::string::const_iterator end, Value& value ); + + void read_or_throw( const std::string& s, Value& value ); + void read_or_throw( std::istream& is, Value& value ); + void read_or_throw( std::string::const_iterator& begin, std::string::const_iterator end, Value& value ); + +#ifndef BOOST_NO_STD_WSTRING + + bool read( const std::wstring& s, wValue& value ); + bool read( std::wistream& is, wValue& value ); + bool read( std::wstring::const_iterator& begin, std::wstring::const_iterator end, wValue& value ); + + void read_or_throw( const std::wstring& s, wValue& value ); + void read_or_throw( std::wistream& is, wValue& value ); + void read_or_throw( std::wstring::const_iterator& begin, std::wstring::const_iterator end, wValue& value ); + +#endif + + bool read( const std::string& s, mValue& value ); + bool read( std::istream& is, mValue& value ); + bool read( std::string::const_iterator& begin, std::string::const_iterator end, mValue& value ); + + void read_or_throw( const std::string& s, mValue& value ); + void read_or_throw( std::istream& is, mValue& value ); + void read_or_throw( std::string::const_iterator& begin, std::string::const_iterator end, mValue& value ); + +#ifndef BOOST_NO_STD_WSTRING + + bool read( const std::wstring& s, wmValue& value ); + bool read( std::wistream& is, wmValue& value ); + bool read( std::wstring::const_iterator& begin, std::wstring::const_iterator end, wmValue& value ); + + void read_or_throw( const std::wstring& s, wmValue& value ); + void read_or_throw( std::wistream& is, wmValue& value ); + void read_or_throw( std::wstring::const_iterator& begin, std::wstring::const_iterator end, wmValue& value ); + +#endif +} + +#endif diff --git a/json_spirit/json_spirit_reader_template.h b/json_spirit/json_spirit_reader_template.h new file mode 100644 index 000000000..81cded434 --- /dev/null +++ b/json_spirit/json_spirit_reader_template.h @@ -0,0 +1,612 @@ +#ifndef JSON_SPIRIT_READER_TEMPLATE +#define JSON_SPIRIT_READER_TEMPLATE + +// Copyright John W. Wilkinson 2007 - 2009. +// Distributed under the MIT License, see accompanying file LICENSE.txt + +// json spirit version 4.03 + +#include "json_spirit_value.h" +#include "json_spirit_error_position.h" + +//#define BOOST_SPIRIT_THREADSAFE // uncomment for multithreaded use, requires linking to boost.thread + +#include +#include +#include + +#if BOOST_VERSION >= 103800 + #include + #include + #include + #include + #include + #define spirit_namespace boost::spirit::classic +#else + #include + #include + #include + #include + #include + #define spirit_namespace boost::spirit +#endif + +namespace json_spirit +{ + const spirit_namespace::int_parser < boost::int64_t > int64_p = spirit_namespace::int_parser < boost::int64_t >(); + const spirit_namespace::uint_parser< boost::uint64_t > uint64_p = spirit_namespace::uint_parser< boost::uint64_t >(); + + template< class Iter_type > + bool is_eq( Iter_type first, Iter_type last, const char* c_str ) + { + for( Iter_type i = first; i != last; ++i, ++c_str ) + { + if( *c_str == 0 ) return false; + + if( *i != *c_str ) return false; + } + + return true; + } + + template< class Char_type > + Char_type hex_to_num( const Char_type c ) + { + if( ( c >= '0' ) && ( c <= '9' ) ) return c - '0'; + if( ( c >= 'a' ) && ( c <= 'f' ) ) return c - 'a' + 10; + if( ( c >= 'A' ) && ( c <= 'F' ) ) return c - 'A' + 10; + return 0; + } + + template< class Char_type, class Iter_type > + Char_type hex_str_to_char( Iter_type& begin ) + { + const Char_type c1( *( ++begin ) ); + const Char_type c2( *( ++begin ) ); + + return ( hex_to_num( c1 ) << 4 ) + hex_to_num( c2 ); + } + + template< class Char_type, class Iter_type > + Char_type unicode_str_to_char( Iter_type& begin ) + { + const Char_type c1( *( ++begin ) ); + const Char_type c2( *( ++begin ) ); + const Char_type c3( *( ++begin ) ); + const Char_type c4( *( ++begin ) ); + + return ( hex_to_num( c1 ) << 12 ) + + ( hex_to_num( c2 ) << 8 ) + + ( hex_to_num( c3 ) << 4 ) + + hex_to_num( c4 ); + } + + template< class String_type > + void append_esc_char_and_incr_iter( String_type& s, + typename String_type::const_iterator& begin, + typename String_type::const_iterator end ) + { + typedef typename String_type::value_type Char_type; + + const Char_type c2( *begin ); + + switch( c2 ) + { + case 't': s += '\t'; break; + case 'b': s += '\b'; break; + case 'f': s += '\f'; break; + case 'n': s += '\n'; break; + case 'r': s += '\r'; break; + case '\\': s += '\\'; break; + case '/': s += '/'; break; + case '"': s += '"'; break; + case 'x': + { + if( end - begin >= 3 ) // expecting "xHH..." + { + s += hex_str_to_char< Char_type >( begin ); + } + break; + } + case 'u': + { + if( end - begin >= 5 ) // expecting "uHHHH..." + { + s += unicode_str_to_char< Char_type >( begin ); + } + break; + } + } + } + + template< class String_type > + String_type substitute_esc_chars( typename String_type::const_iterator begin, + typename String_type::const_iterator end ) + { + typedef typename String_type::const_iterator Iter_type; + + if( end - begin < 2 ) return String_type( begin, end ); + + String_type result; + + result.reserve( end - begin ); + + const Iter_type end_minus_1( end - 1 ); + + Iter_type substr_start = begin; + Iter_type i = begin; + + for( ; i < end_minus_1; ++i ) + { + if( *i == '\\' ) + { + result.append( substr_start, i ); + + ++i; // skip the '\' + + append_esc_char_and_incr_iter( result, i, end ); + + substr_start = i + 1; + } + } + + result.append( substr_start, end ); + + return result; + } + + template< class String_type > + String_type get_str_( typename String_type::const_iterator begin, + typename String_type::const_iterator end ) + { + assert( end - begin >= 2 ); + + typedef typename String_type::const_iterator Iter_type; + + Iter_type str_without_quotes( ++begin ); + Iter_type end_without_quotes( --end ); + + return substitute_esc_chars< String_type >( str_without_quotes, end_without_quotes ); + } + + inline std::string get_str( std::string::const_iterator begin, std::string::const_iterator end ) + { + return get_str_< std::string >( begin, end ); + } + + inline std::wstring get_str( std::wstring::const_iterator begin, std::wstring::const_iterator end ) + { + return get_str_< std::wstring >( begin, end ); + } + + template< class String_type, class Iter_type > + String_type get_str( Iter_type begin, Iter_type end ) + { + const String_type tmp( begin, end ); // convert multipass iterators to string iterators + + return get_str( tmp.begin(), tmp.end() ); + } + + // this class's methods get called by the spirit parse resulting + // in the creation of a JSON object or array + // + // NB Iter_type could be a std::string iterator, wstring iterator, a position iterator or a multipass iterator + // + template< class Value_type, class Iter_type > + class Semantic_actions + { + public: + + typedef typename Value_type::Config_type Config_type; + typedef typename Config_type::String_type String_type; + typedef typename Config_type::Object_type Object_type; + typedef typename Config_type::Array_type Array_type; + typedef typename String_type::value_type Char_type; + + Semantic_actions( Value_type& value ) + : value_( value ) + , current_p_( 0 ) + { + } + + void begin_obj( Char_type c ) + { + assert( c == '{' ); + + begin_compound< Object_type >(); + } + + void end_obj( Char_type c ) + { + assert( c == '}' ); + + end_compound(); + } + + void begin_array( Char_type c ) + { + assert( c == '[' ); + + begin_compound< Array_type >(); + } + + void end_array( Char_type c ) + { + assert( c == ']' ); + + end_compound(); + } + + void new_name( Iter_type begin, Iter_type end ) + { + assert( current_p_->type() == obj_type ); + + name_ = get_str< String_type >( begin, end ); + } + + void new_str( Iter_type begin, Iter_type end ) + { + add_to_current( get_str< String_type >( begin, end ) ); + } + + void new_true( Iter_type begin, Iter_type end ) + { + assert( is_eq( begin, end, "true" ) ); + + add_to_current( true ); + } + + void new_false( Iter_type begin, Iter_type end ) + { + assert( is_eq( begin, end, "false" ) ); + + add_to_current( false ); + } + + void new_null( Iter_type begin, Iter_type end ) + { + assert( is_eq( begin, end, "null" ) ); + + add_to_current( Value_type() ); + } + + void new_int( boost::int64_t i ) + { + add_to_current( i ); + } + + void new_uint64( boost::uint64_t ui ) + { + add_to_current( ui ); + } + + void new_real( double d ) + { + add_to_current( d ); + } + + private: + + Semantic_actions& operator=( const Semantic_actions& ); + // to prevent "assignment operator could not be generated" warning + + Value_type* add_first( const Value_type& value ) + { + assert( current_p_ == 0 ); + + value_ = value; + current_p_ = &value_; + return current_p_; + } + + template< class Array_or_obj > + void begin_compound() + { + if( current_p_ == 0 ) + { + add_first( Array_or_obj() ); + } + else + { + stack_.push_back( current_p_ ); + + Array_or_obj new_array_or_obj; // avoid copy by building new array or object in place + + current_p_ = add_to_current( new_array_or_obj ); + } + } + + void end_compound() + { + if( current_p_ != &value_ ) + { + current_p_ = stack_.back(); + + stack_.pop_back(); + } + } + + Value_type* add_to_current( const Value_type& value ) + { + if( current_p_ == 0 ) + { + return add_first( value ); + } + else if( current_p_->type() == array_type ) + { + current_p_->get_array().push_back( value ); + + return ¤t_p_->get_array().back(); + } + + assert( current_p_->type() == obj_type ); + + return &Config_type::add( current_p_->get_obj(), name_, value ); + } + + Value_type& value_; // this is the object or array that is being created + Value_type* current_p_; // the child object or array that is currently being constructed + + std::vector< Value_type* > stack_; // previous child objects and arrays + + String_type name_; // of current name/value pair + }; + + template< typename Iter_type > + void throw_error( spirit_namespace::position_iterator< Iter_type > i, const std::string& reason ) + { + throw Error_position( i.get_position().line, i.get_position().column, reason ); + } + + template< typename Iter_type > + void throw_error( Iter_type i, const std::string& reason ) + { + throw reason; + } + + // the spirit grammer + // + template< class Value_type, class Iter_type > + class Json_grammer : public spirit_namespace::grammar< Json_grammer< Value_type, Iter_type > > + { + public: + + typedef Semantic_actions< Value_type, Iter_type > Semantic_actions_t; + + Json_grammer( Semantic_actions_t& semantic_actions ) + : actions_( semantic_actions ) + { + } + + static void throw_not_value( Iter_type begin, Iter_type end ) + { + throw_error( begin, "not a value" ); + } + + static void throw_not_array( Iter_type begin, Iter_type end ) + { + throw_error( begin, "not an array" ); + } + + static void throw_not_object( Iter_type begin, Iter_type end ) + { + throw_error( begin, "not an object" ); + } + + static void throw_not_pair( Iter_type begin, Iter_type end ) + { + throw_error( begin, "not a pair" ); + } + + static void throw_not_colon( Iter_type begin, Iter_type end ) + { + throw_error( begin, "no colon in pair" ); + } + + static void throw_not_string( Iter_type begin, Iter_type end ) + { + throw_error( begin, "not a string" ); + } + + template< typename ScannerT > + class definition + { + public: + + definition( const Json_grammer& self ) + { + using namespace spirit_namespace; + + typedef typename Value_type::String_type::value_type Char_type; + + // first we convert the semantic action class methods to functors with the + // parameter signature expected by spirit + + typedef boost::function< void( Char_type ) > Char_action; + typedef boost::function< void( Iter_type, Iter_type ) > Str_action; + typedef boost::function< void( double ) > Real_action; + typedef boost::function< void( boost::int64_t ) > Int_action; + typedef boost::function< void( boost::uint64_t ) > Uint64_action; + + Char_action begin_obj ( boost::bind( &Semantic_actions_t::begin_obj, &self.actions_, _1 ) ); + Char_action end_obj ( boost::bind( &Semantic_actions_t::end_obj, &self.actions_, _1 ) ); + Char_action begin_array( boost::bind( &Semantic_actions_t::begin_array, &self.actions_, _1 ) ); + Char_action end_array ( boost::bind( &Semantic_actions_t::end_array, &self.actions_, _1 ) ); + Str_action new_name ( boost::bind( &Semantic_actions_t::new_name, &self.actions_, _1, _2 ) ); + Str_action new_str ( boost::bind( &Semantic_actions_t::new_str, &self.actions_, _1, _2 ) ); + Str_action new_true ( boost::bind( &Semantic_actions_t::new_true, &self.actions_, _1, _2 ) ); + Str_action new_false ( boost::bind( &Semantic_actions_t::new_false, &self.actions_, _1, _2 ) ); + Str_action new_null ( boost::bind( &Semantic_actions_t::new_null, &self.actions_, _1, _2 ) ); + Real_action new_real ( boost::bind( &Semantic_actions_t::new_real, &self.actions_, _1 ) ); + Int_action new_int ( boost::bind( &Semantic_actions_t::new_int, &self.actions_, _1 ) ); + Uint64_action new_uint64 ( boost::bind( &Semantic_actions_t::new_uint64, &self.actions_, _1 ) ); + + // actual grammer + + json_ + = value_ | eps_p[ &throw_not_value ] + ; + + value_ + = string_[ new_str ] + | number_ + | object_ + | array_ + | str_p( "true" ) [ new_true ] + | str_p( "false" )[ new_false ] + | str_p( "null" ) [ new_null ] + ; + + object_ + = ch_p('{')[ begin_obj ] + >> !members_ + >> ( ch_p('}')[ end_obj ] | eps_p[ &throw_not_object ] ) + ; + + members_ + = pair_ >> *( ',' >> pair_ ) + ; + + pair_ + = string_[ new_name ] + >> ( ':' | eps_p[ &throw_not_colon ] ) + >> ( value_ | eps_p[ &throw_not_value ] ) + ; + + array_ + = ch_p('[')[ begin_array ] + >> !elements_ + >> ( ch_p(']')[ end_array ] | eps_p[ &throw_not_array ] ) + ; + + elements_ + = value_ >> *( ',' >> value_ ) + ; + + string_ + = lexeme_d // this causes white space inside a string to be retained + [ + confix_p + ( + '"', + *lex_escape_ch_p, + '"' + ) + ] + ; + + number_ + = strict_real_p[ new_real ] + | int64_p [ new_int ] + | uint64_p [ new_uint64 ] + ; + } + + spirit_namespace::rule< ScannerT > json_, object_, members_, pair_, array_, elements_, value_, string_, number_; + + const spirit_namespace::rule< ScannerT >& start() const { return json_; } + }; + + private: + + Json_grammer& operator=( const Json_grammer& ); // to prevent "assignment operator could not be generated" warning + + Semantic_actions_t& actions_; + }; + + template< class Iter_type, class Value_type > + Iter_type read_range_or_throw( Iter_type begin, Iter_type end, Value_type& value ) + { + Semantic_actions< Value_type, Iter_type > semantic_actions( value ); + + const spirit_namespace::parse_info< Iter_type > info = + spirit_namespace::parse( begin, end, + Json_grammer< Value_type, Iter_type >( semantic_actions ), + spirit_namespace::space_p ); + + if( !info.hit ) + { + assert( false ); // in theory exception should already have been thrown + throw_error( info.stop, "error" ); + } + + return info.stop; + } + + template< class Iter_type, class Value_type > + void add_posn_iter_and_read_range_or_throw( Iter_type begin, Iter_type end, Value_type& value ) + { + typedef spirit_namespace::position_iterator< Iter_type > Posn_iter_t; + + const Posn_iter_t posn_begin( begin, end ); + const Posn_iter_t posn_end( end, end ); + + read_range_or_throw( posn_begin, posn_end, value ); + } + + template< class Iter_type, class Value_type > + bool read_range( Iter_type& begin, Iter_type end, Value_type& value ) + { + try + { + begin = read_range_or_throw( begin, end, value ); + + return true; + } + catch( ... ) + { + return false; + } + } + + template< class String_type, class Value_type > + void read_string_or_throw( const String_type& s, Value_type& value ) + { + add_posn_iter_and_read_range_or_throw( s.begin(), s.end(), value ); + } + + template< class String_type, class Value_type > + bool read_string( const String_type& s, Value_type& value ) + { + typename String_type::const_iterator begin = s.begin(); + + return read_range( begin, s.end(), value ); + } + + template< class Istream_type > + struct Multi_pass_iters + { + typedef typename Istream_type::char_type Char_type; + typedef std::istream_iterator< Char_type, Char_type > istream_iter; + typedef spirit_namespace::multi_pass< istream_iter > Mp_iter; + + Multi_pass_iters( Istream_type& is ) + { + is.unsetf( std::ios::skipws ); + + begin_ = spirit_namespace::make_multi_pass( istream_iter( is ) ); + end_ = spirit_namespace::make_multi_pass( istream_iter() ); + } + + Mp_iter begin_; + Mp_iter end_; + }; + + template< class Istream_type, class Value_type > + bool read_stream( Istream_type& is, Value_type& value ) + { + Multi_pass_iters< Istream_type > mp_iters( is ); + + return read_range( mp_iters.begin_, mp_iters.end_, value ); + } + + template< class Istream_type, class Value_type > + void read_stream_or_throw( Istream_type& is, Value_type& value ) + { + const Multi_pass_iters< Istream_type > mp_iters( is ); + + add_posn_iter_and_read_range_or_throw( mp_iters.begin_, mp_iters.end_, value ); + } +} + +#endif diff --git a/json_spirit/json_spirit_stream_reader.h b/json_spirit/json_spirit_stream_reader.h new file mode 100644 index 000000000..a9ceeacf0 --- /dev/null +++ b/json_spirit/json_spirit_stream_reader.h @@ -0,0 +1,70 @@ +#ifndef JSON_SPIRIT_READ_STREAM +#define JSON_SPIRIT_READ_STREAM + +// Copyright John W. Wilkinson 2007 - 2009. +// Distributed under the MIT License, see accompanying file LICENSE.txt + +// json spirit version 4.03 + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +# pragma once +#endif + +#include "json_spirit_reader_template.h" + +namespace json_spirit +{ + // these classes allows you to read multiple top level contiguous values from a stream, + // the normal stream read functions have a bug that prevent multiple top level values + // from being read unless they are separated by spaces + + template< class Istream_type, class Value_type > + class Stream_reader + { + public: + + Stream_reader( Istream_type& is ) + : iters_( is ) + { + } + + bool read_next( Value_type& value ) + { + return read_range( iters_.begin_, iters_.end_, value ); + } + + private: + + typedef Multi_pass_iters< Istream_type > Mp_iters; + + Mp_iters iters_; + }; + + template< class Istream_type, class Value_type > + class Stream_reader_thrower + { + public: + + Stream_reader_thrower( Istream_type& is ) + : iters_( is ) + , posn_begin_( iters_.begin_, iters_.end_ ) + , posn_end_( iters_.end_, iters_.end_ ) + { + } + + void read_next( Value_type& value ) + { + posn_begin_ = read_range_or_throw( posn_begin_, posn_end_, value ); + } + + private: + + typedef Multi_pass_iters< Istream_type > Mp_iters; + typedef spirit_namespace::position_iterator< typename Mp_iters::Mp_iter > Posn_iter_t; + + Mp_iters iters_; + Posn_iter_t posn_begin_, posn_end_; + }; +} + +#endif diff --git a/json_spirit/json_spirit_utils.h b/json_spirit/json_spirit_utils.h new file mode 100644 index 000000000..7eb338e7c --- /dev/null +++ b/json_spirit/json_spirit_utils.h @@ -0,0 +1,61 @@ +#ifndef JSON_SPIRIT_UTILS +#define JSON_SPIRIT_UTILS + +// Copyright John W. Wilkinson 2007 - 2009. +// Distributed under the MIT License, see accompanying file LICENSE.txt + +// json spirit version 4.03 + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +# pragma once +#endif + +#include "json_spirit_value.h" +#include + +namespace json_spirit +{ + template< class Obj_t, class Map_t > + void obj_to_map( const Obj_t& obj, Map_t& mp_obj ) + { + mp_obj.clear(); + + for( typename Obj_t::const_iterator i = obj.begin(); i != obj.end(); ++i ) + { + mp_obj[ i->name_ ] = i->value_; + } + } + + template< class Obj_t, class Map_t > + void map_to_obj( const Map_t& mp_obj, Obj_t& obj ) + { + obj.clear(); + + for( typename Map_t::const_iterator i = mp_obj.begin(); i != mp_obj.end(); ++i ) + { + obj.push_back( typename Obj_t::value_type( i->first, i->second ) ); + } + } + + typedef std::map< std::string, Value > Mapped_obj; + +#ifndef BOOST_NO_STD_WSTRING + typedef std::map< std::wstring, wValue > wMapped_obj; +#endif + + template< class Object_type, class String_type > + const typename Object_type::value_type::Value_type& find_value( const Object_type& obj, const String_type& name ) + { + for( typename Object_type::const_iterator i = obj.begin(); i != obj.end(); ++i ) + { + if( i->name_ == name ) + { + return i->value_; + } + } + + return Object_type::value_type::Value_type::null; + } +} + +#endif diff --git a/json_spirit/json_spirit_value.cpp b/json_spirit/json_spirit_value.cpp new file mode 100644 index 000000000..dd5b50e5d --- /dev/null +++ b/json_spirit/json_spirit_value.cpp @@ -0,0 +1,8 @@ +/* Copyright (c) 2007 John W Wilkinson + + This source code can be used for any purpose as long as + this comment is retained. */ + +// json spirit version 2.00 + +#include "json_spirit_value.h" diff --git a/json_spirit/json_spirit_value.h b/json_spirit/json_spirit_value.h new file mode 100644 index 000000000..e8be35530 --- /dev/null +++ b/json_spirit/json_spirit_value.h @@ -0,0 +1,532 @@ +#ifndef JSON_SPIRIT_VALUE +#define JSON_SPIRIT_VALUE + +// Copyright John W. Wilkinson 2007 - 2009. +// Distributed under the MIT License, see accompanying file LICENSE.txt + +// json spirit version 4.03 + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +# pragma once +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace json_spirit +{ + enum Value_type{ obj_type, array_type, str_type, bool_type, int_type, real_type, null_type }; + + template< class Config > // Config determines whether the value uses std::string or std::wstring and + // whether JSON Objects are represented as vectors or maps + class Value_impl + { + public: + + typedef Config Config_type; + typedef typename Config::String_type String_type; + typedef typename Config::Object_type Object; + typedef typename Config::Array_type Array; + typedef typename String_type::const_pointer Const_str_ptr; // eg const char* + + Value_impl(); // creates null value + Value_impl( Const_str_ptr value ); + Value_impl( const String_type& value ); + Value_impl( const Object& value ); + Value_impl( const Array& value ); + Value_impl( bool value ); + Value_impl( int value ); + Value_impl( boost::int64_t value ); + Value_impl( boost::uint64_t value ); + Value_impl( double value ); + + Value_impl( const Value_impl& other ); + + bool operator==( const Value_impl& lhs ) const; + + Value_impl& operator=( const Value_impl& lhs ); + + Value_type type() const; + + bool is_uint64() const; + bool is_null() const; + + const String_type& get_str() const; + const Object& get_obj() const; + const Array& get_array() const; + bool get_bool() const; + int get_int() const; + boost::int64_t get_int64() const; + boost::uint64_t get_uint64() const; + double get_real() const; + + Object& get_obj(); + Array& get_array(); + + template< typename T > T get_value() const; // example usage: int i = value.get_value< int >(); + // or double d = value.get_value< double >(); + + static const Value_impl null; + + private: + + void check_type( const Value_type vtype ) const; + + typedef boost::variant< String_type, + boost::recursive_wrapper< Object >, boost::recursive_wrapper< Array >, + bool, boost::int64_t, double > Variant; + + Value_type type_; + Variant v_; + bool is_uint64_; + }; + + // vector objects + + template< class Config > + struct Pair_impl + { + typedef typename Config::String_type String_type; + typedef typename Config::Value_type Value_type; + + Pair_impl( const String_type& name, const Value_type& value ); + + bool operator==( const Pair_impl& lhs ) const; + + String_type name_; + Value_type value_; + }; + + template< class String > + struct Config_vector + { + typedef String String_type; + typedef Value_impl< Config_vector > Value_type; + typedef Pair_impl < Config_vector > Pair_type; + typedef std::vector< Value_type > Array_type; + typedef std::vector< Pair_type > Object_type; + + static Value_type& add( Object_type& obj, const String_type& name, const Value_type& value ) + { + obj.push_back( Pair_type( name , value ) ); + + return obj.back().value_; + } + + static String_type get_name( const Pair_type& pair ) + { + return pair.name_; + } + + static Value_type get_value( const Pair_type& pair ) + { + return pair.value_; + } + }; + + // typedefs for ASCII + + typedef Config_vector< std::string > Config; + + typedef Config::Value_type Value; + typedef Config::Pair_type Pair; + typedef Config::Object_type Object; + typedef Config::Array_type Array; + + // typedefs for Unicode + +#ifndef BOOST_NO_STD_WSTRING + + typedef Config_vector< std::wstring > wConfig; + + typedef wConfig::Value_type wValue; + typedef wConfig::Pair_type wPair; + typedef wConfig::Object_type wObject; + typedef wConfig::Array_type wArray; +#endif + + // map objects + + template< class String > + struct Config_map + { + typedef String String_type; + typedef Value_impl< Config_map > Value_type; + typedef std::vector< Value_type > Array_type; + typedef std::map< String_type, Value_type > Object_type; + typedef typename Object_type::value_type Pair_type; + + static Value_type& add( Object_type& obj, const String_type& name, const Value_type& value ) + { + return obj[ name ] = value; + } + + static String_type get_name( const Pair_type& pair ) + { + return pair.first; + } + + static Value_type get_value( const Pair_type& pair ) + { + return pair.second; + } + }; + + // typedefs for ASCII + + typedef Config_map< std::string > mConfig; + + typedef mConfig::Value_type mValue; + typedef mConfig::Object_type mObject; + typedef mConfig::Array_type mArray; + + // typedefs for Unicode + +#ifndef BOOST_NO_STD_WSTRING + + typedef Config_map< std::wstring > wmConfig; + + typedef wmConfig::Value_type wmValue; + typedef wmConfig::Object_type wmObject; + typedef wmConfig::Array_type wmArray; + +#endif + + /////////////////////////////////////////////////////////////////////////////////////////////// + // + // implementation + + template< class Config > + const Value_impl< Config > Value_impl< Config >::null; + + template< class Config > + Value_impl< Config >::Value_impl() + : type_( null_type ) + , is_uint64_( false ) + { + } + + template< class Config > + Value_impl< Config >::Value_impl( const Const_str_ptr value ) + : type_( str_type ) + , v_( String_type( value ) ) + , is_uint64_( false ) + { + } + + template< class Config > + Value_impl< Config >::Value_impl( const String_type& value ) + : type_( str_type ) + , v_( value ) + , is_uint64_( false ) + { + } + + template< class Config > + Value_impl< Config >::Value_impl( const Object& value ) + : type_( obj_type ) + , v_( value ) + , is_uint64_( false ) + { + } + + template< class Config > + Value_impl< Config >::Value_impl( const Array& value ) + : type_( array_type ) + , v_( value ) + , is_uint64_( false ) + { + } + + template< class Config > + Value_impl< Config >::Value_impl( bool value ) + : type_( bool_type ) + , v_( value ) + , is_uint64_( false ) + { + } + + template< class Config > + Value_impl< Config >::Value_impl( int value ) + : type_( int_type ) + , v_( static_cast< boost::int64_t >( value ) ) + , is_uint64_( false ) + { + } + + template< class Config > + Value_impl< Config >::Value_impl( boost::int64_t value ) + : type_( int_type ) + , v_( value ) + , is_uint64_( false ) + { + } + + template< class Config > + Value_impl< Config >::Value_impl( boost::uint64_t value ) + : type_( int_type ) + , v_( static_cast< boost::int64_t >( value ) ) + , is_uint64_( true ) + { + } + + template< class Config > + Value_impl< Config >::Value_impl( double value ) + : type_( real_type ) + , v_( value ) + , is_uint64_( false ) + { + } + + template< class Config > + Value_impl< Config >::Value_impl( const Value_impl< Config >& other ) + : type_( other.type() ) + , v_( other.v_ ) + , is_uint64_( other.is_uint64_ ) + { + } + + template< class Config > + Value_impl< Config >& Value_impl< Config >::operator=( const Value_impl& lhs ) + { + Value_impl tmp( lhs ); + + std::swap( type_, tmp.type_ ); + std::swap( v_, tmp.v_ ); + std::swap( is_uint64_, tmp.is_uint64_ ); + + return *this; + } + + template< class Config > + bool Value_impl< Config >::operator==( const Value_impl& lhs ) const + { + if( this == &lhs ) return true; + + if( type() != lhs.type() ) return false; + + return v_ == lhs.v_; + } + + template< class Config > + Value_type Value_impl< Config >::type() const + { + return type_; + } + + template< class Config > + bool Value_impl< Config >::is_uint64() const + { + return is_uint64_; + } + + template< class Config > + bool Value_impl< Config >::is_null() const + { + return type() == null_type; + } + + template< class Config > + void Value_impl< Config >::check_type( const Value_type vtype ) const + { + if( type() != vtype ) + { + std::ostringstream os; + + os << "value type is " << type() << " not " << vtype; + + throw std::runtime_error( os.str() ); + } + } + + template< class Config > + const typename Config::String_type& Value_impl< Config >::get_str() const + { + check_type( str_type ); + + return *boost::get< String_type >( &v_ ); + } + + template< class Config > + const typename Value_impl< Config >::Object& Value_impl< Config >::get_obj() const + { + check_type( obj_type ); + + return *boost::get< Object >( &v_ ); + } + + template< class Config > + const typename Value_impl< Config >::Array& Value_impl< Config >::get_array() const + { + check_type( array_type ); + + return *boost::get< Array >( &v_ ); + } + + template< class Config > + bool Value_impl< Config >::get_bool() const + { + check_type( bool_type ); + + return boost::get< bool >( v_ ); + } + + template< class Config > + int Value_impl< Config >::get_int() const + { + check_type( int_type ); + + return static_cast< int >( get_int64() ); + } + + template< class Config > + boost::int64_t Value_impl< Config >::get_int64() const + { + check_type( int_type ); + + return boost::get< boost::int64_t >( v_ ); + } + + template< class Config > + boost::uint64_t Value_impl< Config >::get_uint64() const + { + check_type( int_type ); + + return static_cast< boost::uint64_t >( get_int64() ); + } + + template< class Config > + double Value_impl< Config >::get_real() const + { + if( type() == int_type ) + { + return is_uint64() ? static_cast< double >( get_uint64() ) + : static_cast< double >( get_int64() ); + } + + check_type( real_type ); + + return boost::get< double >( v_ ); + } + + template< class Config > + typename Value_impl< Config >::Object& Value_impl< Config >::get_obj() + { + check_type( obj_type ); + + return *boost::get< Object >( &v_ ); + } + + template< class Config > + typename Value_impl< Config >::Array& Value_impl< Config >::get_array() + { + check_type( array_type ); + + return *boost::get< Array >( &v_ ); + } + + template< class Config > + Pair_impl< Config >::Pair_impl( const String_type& name, const Value_type& value ) + : name_( name ) + , value_( value ) + { + } + + template< class Config > + bool Pair_impl< Config >::operator==( const Pair_impl< Config >& lhs ) const + { + if( this == &lhs ) return true; + + return ( name_ == lhs.name_ ) && ( value_ == lhs.value_ ); + } + + // converts a C string, ie. 8 bit char array, to a string object + // + template < class String_type > + String_type to_str( const char* c_str ) + { + String_type result; + + for( const char* p = c_str; *p != 0; ++p ) + { + result += *p; + } + + return result; + } + + // + + namespace internal_ + { + template< typename T > + struct Type_to_type + { + }; + + template< class Value > + int get_value( const Value& value, Type_to_type< int > ) + { + return value.get_int(); + } + + template< class Value > + boost::int64_t get_value( const Value& value, Type_to_type< boost::int64_t > ) + { + return value.get_int64(); + } + + template< class Value > + boost::uint64_t get_value( const Value& value, Type_to_type< boost::uint64_t > ) + { + return value.get_uint64(); + } + + template< class Value > + double get_value( const Value& value, Type_to_type< double > ) + { + return value.get_real(); + } + + template< class Value > + typename Value::String_type get_value( const Value& value, Type_to_type< typename Value::String_type > ) + { + return value.get_str(); + } + + template< class Value > + typename Value::Array get_value( const Value& value, Type_to_type< typename Value::Array > ) + { + return value.get_array(); + } + + template< class Value > + typename Value::Object get_value( const Value& value, Type_to_type< typename Value::Object > ) + { + return value.get_obj(); + } + + template< class Value > + bool get_value( const Value& value, Type_to_type< bool > ) + { + return value.get_bool(); + } + } + + template< class Config > + template< typename T > + T Value_impl< Config >::get_value() const + { + return internal_::get_value( *this, internal_::Type_to_type< T >() ); + } +} + +#endif diff --git a/json_spirit/json_spirit_writer.cpp b/json_spirit/json_spirit_writer.cpp new file mode 100644 index 000000000..f3367b68d --- /dev/null +++ b/json_spirit/json_spirit_writer.cpp @@ -0,0 +1,95 @@ +// Copyright John W. Wilkinson 2007 - 2009. +// Distributed under the MIT License, see accompanying file LICENSE.txt + +// json spirit version 4.03 + +#include "json_spirit_writer.h" +#include "json_spirit_writer_template.h" + +void json_spirit::write( const Value& value, std::ostream& os ) +{ + write_stream( value, os, false ); +} + +void json_spirit::write_formatted( const Value& value, std::ostream& os ) +{ + write_stream( value, os, true ); +} + +std::string json_spirit::write( const Value& value ) +{ + return write_string( value, false ); +} + +std::string json_spirit::write_formatted( const Value& value ) +{ + return write_string( value, true ); +} + +#ifndef BOOST_NO_STD_WSTRING + +void json_spirit::write( const wValue& value, std::wostream& os ) +{ + write_stream( value, os, false ); +} + +void json_spirit::write_formatted( const wValue& value, std::wostream& os ) +{ + write_stream( value, os, true ); +} + +std::wstring json_spirit::write( const wValue& value ) +{ + return write_string( value, false ); +} + +std::wstring json_spirit::write_formatted( const wValue& value ) +{ + return write_string( value, true ); +} + +#endif + +void json_spirit::write( const mValue& value, std::ostream& os ) +{ + write_stream( value, os, false ); +} + +void json_spirit::write_formatted( const mValue& value, std::ostream& os ) +{ + write_stream( value, os, true ); +} + +std::string json_spirit::write( const mValue& value ) +{ + return write_string( value, false ); +} + +std::string json_spirit::write_formatted( const mValue& value ) +{ + return write_string( value, true ); +} + +#ifndef BOOST_NO_STD_WSTRING + +void json_spirit::write( const wmValue& value, std::wostream& os ) +{ + write_stream( value, os, false ); +} + +void json_spirit::write_formatted( const wmValue& value, std::wostream& os ) +{ + write_stream( value, os, true ); +} + +std::wstring json_spirit::write( const wmValue& value ) +{ + return write_string( value, false ); +} + +std::wstring json_spirit::write_formatted( const wmValue& value ) +{ + return write_string( value, true ); +} + +#endif diff --git a/json_spirit/json_spirit_writer.h b/json_spirit/json_spirit_writer.h new file mode 100644 index 000000000..1b67b512d --- /dev/null +++ b/json_spirit/json_spirit_writer.h @@ -0,0 +1,50 @@ +#ifndef JSON_SPIRIT_WRITER +#define JSON_SPIRIT_WRITER + +// Copyright John W. Wilkinson 2007 - 2009. +// Distributed under the MIT License, see accompanying file LICENSE.txt + +// json spirit version 4.03 + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +# pragma once +#endif + +#include "json_spirit_value.h" +#include + +namespace json_spirit +{ + // functions to convert JSON Values to text, + // the "formatted" versions add whitespace to format the output nicely + + void write ( const Value& value, std::ostream& os ); + void write_formatted( const Value& value, std::ostream& os ); + std::string write ( const Value& value ); + std::string write_formatted( const Value& value ); + +#ifndef BOOST_NO_STD_WSTRING + + void write ( const wValue& value, std::wostream& os ); + void write_formatted( const wValue& value, std::wostream& os ); + std::wstring write ( const wValue& value ); + std::wstring write_formatted( const wValue& value ); + +#endif + + void write ( const mValue& value, std::ostream& os ); + void write_formatted( const mValue& value, std::ostream& os ); + std::string write ( const mValue& value ); + std::string write_formatted( const mValue& value ); + +#ifndef BOOST_NO_STD_WSTRING + + void write ( const wmValue& value, std::wostream& os ); + void write_formatted( const wmValue& value, std::wostream& os ); + std::wstring write ( const wmValue& value ); + std::wstring write_formatted( const wmValue& value ); + +#endif +} + +#endif diff --git a/json_spirit/json_spirit_writer_template.h b/json_spirit/json_spirit_writer_template.h new file mode 100644 index 000000000..dbd0f45da --- /dev/null +++ b/json_spirit/json_spirit_writer_template.h @@ -0,0 +1,248 @@ +#ifndef JSON_SPIRIT_WRITER_TEMPLATE +#define JSON_SPIRIT_WRITER_TEMPLATE + +// Copyright John W. Wilkinson 2007 - 2009. +// Distributed under the MIT License, see accompanying file LICENSE.txt + +// json spirit version 4.03 + +#include "json_spirit_value.h" + +#include +#include +#include + +namespace json_spirit +{ + inline char to_hex_char( unsigned int c ) + { + assert( c <= 0xF ); + + const char ch = static_cast< char >( c ); + + if( ch < 10 ) return '0' + ch; + + return 'A' - 10 + ch; + } + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-local-typedefs") + template< class String_type > + String_type non_printable_to_string( unsigned int c ) + { + typedef typename String_type::value_type Char_type; + + String_type result( 6, '\\' ); + + result[1] = 'u'; + + result[ 5 ] = to_hex_char( c & 0x000F ); c >>= 4; + result[ 4 ] = to_hex_char( c & 0x000F ); c >>= 4; + result[ 3 ] = to_hex_char( c & 0x000F ); c >>= 4; + result[ 2 ] = to_hex_char( c & 0x000F ); + + return result; + } +#pragma GCC diagnostic pop + + template< typename Char_type, class String_type > + bool add_esc_char( Char_type c, String_type& s ) + { + switch( c ) + { + case '"': s += to_str< String_type >( "\\\"" ); return true; + case '\\': s += to_str< String_type >( "\\\\" ); return true; + case '\b': s += to_str< String_type >( "\\b" ); return true; + case '\f': s += to_str< String_type >( "\\f" ); return true; + case '\n': s += to_str< String_type >( "\\n" ); return true; + case '\r': s += to_str< String_type >( "\\r" ); return true; + case '\t': s += to_str< String_type >( "\\t" ); return true; + } + + return false; + } + + template< class String_type > + String_type add_esc_chars( const String_type& s ) + { + typedef typename String_type::const_iterator Iter_type; + typedef typename String_type::value_type Char_type; + + String_type result; + + const Iter_type end( s.end() ); + + for( Iter_type i = s.begin(); i != end; ++i ) + { + const Char_type c( *i ); + + if( add_esc_char( c, result ) ) continue; + + const wint_t unsigned_c( ( c >= 0 ) ? c : 256 + c ); + + if( iswprint( unsigned_c ) ) + { + result += c; + } + else + { + result += non_printable_to_string< String_type >( unsigned_c ); + } + } + + return result; + } + + // this class generates the JSON text, + // it keeps track of the indentation level etc. + // + template< class Value_type, class Ostream_type > + class Generator + { + typedef typename Value_type::Config_type Config_type; + typedef typename Config_type::String_type String_type; + typedef typename Config_type::Object_type Object_type; + typedef typename Config_type::Array_type Array_type; + typedef typename String_type::value_type Char_type; + typedef typename Object_type::value_type Obj_member_type; + + public: + + Generator( const Value_type& value, Ostream_type& os, bool pretty ) + : os_( os ) + , indentation_level_( 0 ) + , pretty_( pretty ) + { + output( value ); + } + + private: + + void output( const Value_type& value ) + { + switch( value.type() ) + { + case obj_type: output( value.get_obj() ); break; + case array_type: output( value.get_array() ); break; + case str_type: output( value.get_str() ); break; + case bool_type: output( value.get_bool() ); break; + case int_type: output_int( value ); break; + case real_type: os_ << std::showpoint << std::setprecision( 16 ) + << value.get_real(); break; + case null_type: os_ << "null"; break; + default: assert( false ); + } + } + + void output( const Object_type& obj ) + { + output_array_or_obj( obj, '{', '}' ); + } + + void output( const Array_type& arr ) + { + output_array_or_obj( arr, '[', ']' ); + } + + void output( const Obj_member_type& member ) + { + output( Config_type::get_name( member ) ); space(); + os_ << ':'; space(); + output( Config_type::get_value( member ) ); + } + + void output_int( const Value_type& value ) + { + if( value.is_uint64() ) + { + os_ << value.get_uint64(); + } + else + { + os_ << value.get_int64(); + } + } + + void output( const String_type& s ) + { + os_ << '"' << add_esc_chars( s ) << '"'; + } + + void output( bool b ) + { + os_ << to_str< String_type >( b ? "true" : "false" ); + } + + template< class T > + void output_array_or_obj( const T& t, Char_type start_char, Char_type end_char ) + { + os_ << start_char; new_line(); + + ++indentation_level_; + + for( typename T::const_iterator i = t.begin(); i != t.end(); ++i ) + { + indent(); output( *i ); + + typename T::const_iterator next = i; + + if( ++next != t.end()) + { + os_ << ','; + } + + new_line(); + } + + --indentation_level_; + + indent(); os_ << end_char; + } + + void indent() + { + if( !pretty_ ) return; + + for( int i = 0; i < indentation_level_; ++i ) + { + os_ << " "; + } + } + + void space() + { + if( pretty_ ) os_ << ' '; + } + + void new_line() + { + if( pretty_ ) os_ << '\n'; + } + + Generator& operator=( const Generator& ); // to prevent "assignment operator could not be generated" warning + + Ostream_type& os_; + int indentation_level_; + bool pretty_; + }; + + template< class Value_type, class Ostream_type > + void write_stream( const Value_type& value, Ostream_type& os, bool pretty ) + { + Generator< Value_type, Ostream_type >( value, os, pretty ); + } + + template< class Value_type > + typename Value_type::String_type write_string( const Value_type& value, bool pretty ) + { + typedef typename Value_type::String_type::value_type Char_type; + + std::basic_ostringstream< Char_type > os; + + write_stream( value, os, pretty ); + + return os.str(); + } +} + +#endif diff --git a/libethereum/AddressState.h b/libethereum/AddressState.h index 6aec2aaac..9475f6959 100644 --- a/libethereum/AddressState.h +++ b/libethereum/AddressState.h @@ -37,9 +37,24 @@ enum class AddressType class AddressState { public: - AddressState(): m_type(AddressType::Dead), m_balance(0), m_nonce(0) {} - AddressState(u256 _balance, u256 _nonce): m_type(AddressType::Normal), m_balance(_balance), m_nonce(_nonce) {} - AddressState(u256 _balance, u256 _nonce, h256 _contractRoot): m_type(AddressType::Contract), m_balance(_balance), m_nonce(_nonce), m_contractRoot(_contractRoot) {} + AddressState(): m_type(AddressType::Dead), m_balance(0), m_nonce(0), m_haveMemory(false) {} + AddressState(u256 _balance, u256 _nonce, AddressType _type = AddressType::Normal): m_type(_type), m_balance(_balance), m_nonce(_nonce), m_haveMemory(true) {} + AddressState(u256 _balance, u256 _nonce, h256 _contractRoot): m_type(AddressType::Contract), m_balance(_balance), m_nonce(_nonce), m_haveMemory(false), m_contractRoot(_contractRoot) {} + AddressState(u256 _balance, u256 _nonce, u256s _memory): m_type(AddressType::Contract), m_balance(_balance), m_nonce(_nonce), m_haveMemory(true) + { + for (unsigned i = 0; i < _memory.size(); ++i) +#ifdef __clang__ + { + auto mFinder = m_memory.find((u256)i); + if (mFinder == m_memory.end()) + m_memory.insert(std::make_pair((u256)i,_memory[i])); + else + mFinder->second = _memory[i]; + } +#else + m_memory[(u256)i] = _memory[i]; +#endif + } void incNonce() { m_nonce++; } void addBalance(bigint _i) { m_balance = (u256)((bigint)m_balance + _i); } @@ -50,15 +65,17 @@ public: u256 const& balance() const { return m_balance; } u256& nonce() { return m_nonce; } u256 const& nonce() const { return m_nonce; } - bool haveMemory() const { return m_memory.empty() && m_contractRoot != h256(); } // TODO: best to switch to m_haveMemory flag rather than try to infer. + bool haveMemory() const { return m_haveMemory; } + std::map& setHaveMemory() { assert(m_type == AddressType::Contract); m_haveMemory = true; m_contractRoot = h256(); return m_memory; } h256 oldRoot() const { assert(!haveMemory()); return m_contractRoot; } - std::map& takeMemory() { assert(m_type == AddressType::Contract && haveMemory()); m_contractRoot = h256(); return m_memory; } + std::map& memory() { assert(m_type == AddressType::Contract && haveMemory()); return m_memory; } std::map const& memory() const { assert(m_type == AddressType::Contract && haveMemory()); return m_memory; } private: AddressType m_type; u256 m_balance; u256 m_nonce; + bool m_haveMemory; h256 m_contractRoot; // TODO: change to unordered_map. std::map m_memory; diff --git a/libethereum/BlockChain.cpp b/libethereum/BlockChain.cpp index 991839557..7bf7606da 100644 --- a/libethereum/BlockChain.cpp +++ b/libethereum/BlockChain.cpp @@ -33,6 +33,8 @@ using namespace std; using namespace eth; +#define ETH_CATCH 1 + namespace eth { std::ostream& operator<<(std::ostream& _out, BlockChain const& _bc) @@ -116,20 +118,41 @@ bool contains(T const& _t, V const& _v) return false; } +bool BlockChain::attemptImport(bytes const& _block, Overlay const& _stateDB) +{ +#if ETH_CATCH + try +#endif + { + import(_block, _stateDB); + return true; + } +#if ETH_CATCH + catch (...) + { + return false; + } +#endif +} + + void BlockChain::import(bytes const& _block, Overlay const& _db) { // VERIFY: populates from the block and checks the block is internally coherent. BlockInfo bi(&_block); +#if ETH_CATCH try +#endif { bi.verifyInternals(&_block); } +#if ETH_CATCH catch (Exception const& _e) { clog(BlockChainNote) << " Malformed block (" << _e.description() << ")."; throw; } - +#endif auto newHash = eth::sha3(_block); // Check block doesn't already exist first! @@ -151,7 +174,9 @@ void BlockChain::import(bytes const& _block, Overlay const& _db) clog(BlockChainNote) << "Attempting import of " << newHash << "..."; u256 td; +#if ETH_CATCH try +#endif { // Check family: BlockInfo biParent(block(bi.parentHash)); @@ -168,7 +193,7 @@ void BlockChain::import(bytes const& _block, Overlay const& _db) auto tdIncrease = s.playback(&_block, bi, biParent, biGrandParent, true); td = pd.totalDifficulty + tdIncrease; -#if PARANOIA +#if ETH_PARANOIA checkConsistency(); #endif // All ok - insert into DB @@ -182,15 +207,17 @@ void BlockChain::import(bytes const& _block, Overlay const& _db) m_detailsDB->Put(m_writeOptions, ldb::Slice((char const*)&bi.parentHash, 32), (ldb::Slice)eth::ref(m_details[bi.parentHash].rlp())); m_db->Put(m_writeOptions, ldb::Slice((char const*)&newHash, 32), (ldb::Slice)ref(_block)); -#if PARANOIA +#if ETH_PARANOIA checkConsistency(); #endif } - catch (...) +#if ETH_CATCH + catch (Exception const& _e) { - clog(BlockChainNote) << " Malformed block."; + clog(BlockChainNote) << " Malformed block (" << _e.description() << ")."; throw; } +#endif // cnote << "Parent " << bi.parentHash << " has " << details(bi.parentHash).children.size() << " children."; @@ -244,7 +271,7 @@ bytesConstRef BlockChain::block(h256 _hash) const BlockDetails const& BlockChain::details(h256 _h) const { - std::map::const_iterator it; + BlockDetailsHash::const_iterator it; bool fetchRequired; { lock_guard l(m_lock); @@ -263,7 +290,7 @@ BlockDetails const& BlockChain::details(h256 _h) const { lock_guard l(m_lock); bool ok; - tie(it, ok) = m_details.insert(make_pair(_h, BlockDetails(RLP(s)))); + tie(it, ok) = m_details.insert(std::make_pair(_h, BlockDetails(RLP(s)))); } } return it->second; diff --git a/libethereum/BlockChain.h b/libethereum/BlockChain.h index 8ad0cb8b8..ee208e508 100644 --- a/libethereum/BlockChain.h +++ b/libethereum/BlockChain.h @@ -48,6 +48,8 @@ struct BlockDetails h256s children; }; +typedef std::map BlockDetailsHash; + static const BlockDetails NullBlockDetails; static const h256s NullH256s; @@ -56,8 +58,8 @@ class Overlay; class AlreadyHaveBlock: public std::exception {}; class UnknownParent: public std::exception {}; -struct BlockChainChat: public LogChannel { static const char constexpr* name = "-B-"; static const int verbosity = 7; }; -struct BlockChainNote: public LogChannel { static const char constexpr* name = "=B="; static const int verbosity = 4; }; +struct BlockChainChat: public LogChannel { static const char* name() { return "-B-"; } static const int verbosity = 7; }; +struct BlockChainNote: public LogChannel { static const char* name() { return "=B="; } static const int verbosity = 4; }; /** * @brief Implements the blockchain database. All data this gives is disk-backed. @@ -72,9 +74,9 @@ public: /// (Potentially) renders invalid existing bytesConstRef returned by lastBlock. /// To be called from main loop every 100ms or so. void process(); - + /// Attempt to import the given block. - bool attemptImport(bytes const& _block, Overlay const& _stateDB) { try { import(_block, _stateDB); return true; } catch (...) { return false; } } + bool attemptImport(bytes const& _block, Overlay const& _stateDB); /// Import block into disk-backed DB void import(bytes const& _block, Overlay const& _stateDB); @@ -101,7 +103,7 @@ private: void checkConsistency(); /// Get fully populated from disk DB. - mutable std::map m_details; + mutable BlockDetailsHash m_details; mutable std::map m_cache; mutable std::mutex m_lock; diff --git a/libethereum/BlockInfo.cpp b/libethereum/BlockInfo.cpp index c5f00eab4..9e9ca48c8 100644 --- a/libethereum/BlockInfo.cpp +++ b/libethereum/BlockInfo.cpp @@ -101,7 +101,7 @@ void BlockInfo::populateFromHeader(RLP const& _header) extraData = _header[field = 7].toBytes(); nonce = _header[field = 8].toHash(); } - catch (RLP::BadCast) + catch (RLPException const&) { throw InvalidBlockHeaderFormat(field, _header[field].data()); } @@ -152,6 +152,6 @@ void BlockInfo::verifyParent(BlockInfo const& _parent) const throw InvalidDifficulty(); // Check timestamp is after previous timestamp. - if (parentHash && _parent.timestamp >= timestamp) + if (parentHash && _parent.timestamp > timestamp) throw InvalidTimestamp(); } diff --git a/libethereum/Client.cpp b/libethereum/Client.cpp index faeabb04e..c30de604f 100644 --- a/libethereum/Client.cpp +++ b/libethereum/Client.cpp @@ -32,63 +32,63 @@ Client::Client(std::string const& _clientVersion, Address _us, std::string const m_clientVersion(_clientVersion), m_bc(_dbPath), m_stateDB(State::openDB(_dbPath)), - m_s(_us, m_stateDB), - m_mined(_us, m_stateDB) + m_preMine(_us, m_stateDB), + m_postMine(_us, m_stateDB), + m_workState(Active) { Defaults::setDBPath(_dbPath); - // Synchronise the state according to the block chain - i.e. replay all transactions in block chain, in order. - // In practise this won't need to be done since the State DB will contain the keys for the tries for most recent (and many old) blocks. + // Synchronise the state according to the head of the block chain. // TODO: currently it contains keys for *all* blocks. Make it remove old ones. - m_s.sync(m_bc); - m_s.sync(m_tq); - m_mined = m_s; + m_preMine.sync(m_bc); + m_postMine = m_preMine; m_changed = true; static const char* c_threadName = "eth"; - m_work = new thread([&](){ + m_work.reset(new thread([&](){ setThreadName(c_threadName); - - while (m_workState != Deleting) work(); m_workState = Deleted; - }); + while (m_workState.load(std::memory_order_acquire) != Deleting) + work(); + m_workState.store(Deleted, std::memory_order_release); + })); } Client::~Client() { - if (m_workState == Active) - m_workState = Deleting; - while (m_workState != Deleted) + if (m_workState.load(std::memory_order_acquire) == Active) + m_workState.store(Deleting, std::memory_order_release); + while (m_workState.load(std::memory_order_acquire) != Deleted) this_thread::sleep_for(chrono::milliseconds(10)); + m_work->join(); } -void Client::startNetwork(short _listenPort, std::string const& _seedHost, short _port, NodeMode _mode, unsigned _peers, string const& _publicIP, bool _upnp) +void Client::startNetwork(unsigned short _listenPort, std::string const& _seedHost, unsigned short _port, NodeMode _mode, unsigned _peers, string const& _publicIP, bool _upnp) { - if (m_net) + if (m_net.get()) return; - m_net = new PeerServer(m_clientVersion, m_bc, 0, _listenPort, _mode, _publicIP, _upnp); + m_net.reset(new PeerServer(m_clientVersion, m_bc, 0, _listenPort, _mode, _publicIP, _upnp)); m_net->setIdealPeerCount(_peers); if (_seedHost.size()) connect(_seedHost, _port); } -void Client::connect(std::string const& _seedHost, short _port) +void Client::connect(std::string const& _seedHost, unsigned short _port) { - if (!m_net) + if (!m_net.get()) return; m_net->connect(_seedHost, _port); } void Client::stopNetwork() { - delete m_net; - m_net = nullptr; + m_net.reset(nullptr); } void Client::startMining() { m_doMine = true; - m_miningStarted = true; + m_restartMining = true; } void Client::stopMining() @@ -98,9 +98,9 @@ void Client::stopMining() void Client::transact(Secret _secret, Address _dest, u256 _amount, u256s _data) { - lock_guard l(m_lock); + lock_guard l(m_lock); Transaction t; - t.nonce = m_mined.transactionsFrom(toAddress(_secret)); + t.nonce = m_postMine.transactionsFrom(toAddress(_secret)); t.receiveAddress = _dest; t.value = _amount; t.data = _data; @@ -121,7 +121,7 @@ void Client::work() { m_net->process(); - lock_guard l(m_lock); + lock_guard l(m_lock); if (m_net->sync(m_bc, m_tq, m_stateDB)) changed = true; } @@ -134,36 +134,36 @@ void Client::work() // all blocks. // Resynchronise state with block chain & trans { - lock_guard l(m_lock); - if (m_s.sync(m_bc)) + lock_guard l(m_lock); + if (m_preMine.sync(m_bc) || m_postMine.address() != m_preMine.address()) { if (m_doMine) - cnote << "Externally mined block: Restarting mining operation."; + cnote << "New block on chain: Restarting mining operation."; changed = true; - m_miningStarted = true; // need to re-commit to mine. - m_mined = m_s; + m_restartMining = true; // need to re-commit to mine. + m_postMine = m_preMine; } - if (m_mined.sync(m_tq)) + if (m_postMine.sync(m_tq)) { if (m_doMine) cnote << "Additional transaction ready: Restarting mining operation."; changed = true; - m_miningStarted = true; + m_restartMining = true; } } if (m_doMine) { - if (m_miningStarted) + if (m_restartMining) { - lock_guard l(m_lock); - m_mined.commitToMine(m_bc); + lock_guard l(m_lock); + m_postMine.commitToMine(m_bc); } - m_miningStarted = false; + m_restartMining = false; // Mine for a while. - MineInfo mineInfo = m_mined.mine(100); + MineInfo mineInfo = m_postMine.mine(100); m_mineProgress.best = max(m_mineProgress.best, mineInfo.best); m_mineProgress.current = mineInfo.best; m_mineProgress.requirement = mineInfo.requirement; @@ -171,11 +171,10 @@ void Client::work() if (mineInfo.completed) { // Import block. - lock_guard l(m_lock); - m_bc.attemptImport(m_mined.blockData(), m_stateDB); + lock_guard l(m_lock); + m_bc.attemptImport(m_postMine.blockData(), m_stateDB); m_mineProgress.best = 0; m_changed = true; - m_miningStarted = true; // need to re-commit to mine. } } else diff --git a/libethereum/Client.h b/libethereum/Client.h index fb5718dbd..bf1964fe0 100644 --- a/libethereum/Client.h +++ b/libethereum/Client.h @@ -23,6 +23,7 @@ #include #include +#include #include "Common.h" #include "BlockChain.h" #include "TransactionQueue.h" @@ -52,6 +53,13 @@ private: Client* m_client; }; +enum ClientWorkState +{ + Active = 0, + Deleting, + Deleted +}; + class Client { public: @@ -90,11 +98,13 @@ public: bool changed() const { auto ret = m_changed; m_changed = false; return ret; } /// Get the object representing the current state of Ethereum. - State const& state() const { return m_s; } + State const& state() const { return m_preMine; } + /// Get the object representing the current state of Ethereum. + State const& postState() const { return m_postMine; } /// Get the object representing the current canonical blockchain. BlockChain const& blockChain() const { return m_bc; } /// Get a map containing each of the pending transactions. - std::map const& pending() const { return m_mined.pending(); } + Transactions const& pending() const { return m_postMine.pending(); } void setClientVersion(std::string const& _name) { m_clientVersion = _name; } @@ -103,23 +113,23 @@ public: /// Get information on the current peer set. std::vector peers() { return m_net ? m_net->peers() : std::vector(); } /// Same as peers().size(), but more efficient. - unsigned peerCount() const { return m_net ? m_net->peerCount() : 0; } + size_t peerCount() const { return m_net ? m_net->peerCount() : 0; } /// Start the network subsystem. - void startNetwork(short _listenPort = 30303, std::string const& _seedHost = std::string(), short _port = 30303, NodeMode _mode = NodeMode::Full, unsigned _peers = 5, std::string const& _publicIP = std::string(), bool _upnp = true); + void startNetwork(unsigned short _listenPort = 30303, std::string const& _remoteHost = std::string(), unsigned short _remotePort = 30303, NodeMode _mode = NodeMode::Full, unsigned _peers = 5, std::string const& _publicIP = std::string(), bool _upnp = true); /// Connect to a particular peer. - void connect(std::string const& _seedHost, short _port = 30303); + void connect(std::string const& _seedHost, unsigned short _port = 30303); /// Stop the network subsystem. void stopNetwork(); /// Get access to the peer server object. This will be null if the network isn't online. - PeerServer* peerServer() const { return m_net; } + PeerServer* peerServer() const { return m_net.get(); } // Mining stuff: /// Set the coinbase address. - void setAddress(Address _us) { m_s.setAddress(_us); } + void setAddress(Address _us) { m_preMine.setAddress(_us); } /// Get the coinbase address. - Address address() const { return m_s.address(); } + Address address() const { return m_preMine.address(); } /// Start mining. void startMining(); /// Stop mining. @@ -134,17 +144,17 @@ private: BlockChain m_bc; ///< Maintains block database. TransactionQueue m_tq; ///< Maintains list of incoming transactions not yet on the block chain. Overlay m_stateDB; ///< Acts as the central point for the state database, so multiple States can share it. - State m_s; ///< The present state of the client. - State m_mined; ///< The state of the client which we're mining (i.e. it'll have all the rewards added). - PeerServer* m_net = nullptr; ///< Should run in background and send us events when blocks found and allow us to send blocks as required. + State m_preMine; ///< The present state of the client. + State m_postMine; ///< The state of the client which we're mining (i.e. it'll have all the rewards added). + std::unique_ptr m_net; ///< Should run in background and send us events when blocks found and allow us to send blocks as required. - std::thread* m_work; ///< The work thread. + std::unique_ptr m_work;///< The work thread. - std::mutex m_lock; - enum { Active = 0, Deleting, Deleted } m_workState = Active; + std::recursive_mutex m_lock; + std::atomic m_workState; bool m_doMine = false; ///< Are we supposed to be mining? MineProgress m_mineProgress; - mutable bool m_miningStarted = false; + mutable bool m_restartMining = false; mutable bool m_changed; }; diff --git a/libethereum/Common.cpp b/libethereum/Common.cpp index 639f14d47..2b08c84fd 100644 --- a/libethereum/Common.cpp +++ b/libethereum/Common.cpp @@ -21,6 +21,7 @@ #include "Common.h" +#include #include #if WIN32 #pragma warning(push) @@ -191,13 +192,13 @@ KeyPair KeyPair::create() { secp256k1_start(); static std::mt19937_64 s_eng(time(0)); - std::uniform_int_distribution d(0, 255); + std::uniform_int_distribution d(0, 255); for (int i = 0; i < 100; ++i) { h256 sec; - for (uint i = 0; i < 32; ++i) - sec[i] = d(s_eng); + for (unsigned i = 0; i < 32; ++i) + sec[i] = (byte)d(s_eng); KeyPair ret(sec); if (ret.address()) @@ -281,3 +282,24 @@ std::string eth::formatBalance(u256 _b) ret << _b << " wei"; return ret.str(); } + +bytes eth::contents(std::string const& _file) +{ + std::ifstream is(_file, std::ifstream::binary); + if (!is) + return bytes(); + // get length of file: + is.seekg (0, is.end); + streamoff length = is.tellg(); + is.seekg (0, is.beg); + bytes ret(length); + is.read((char*)ret.data(), length); + is.close(); + return ret; +} + +void eth::writeFile(std::string const& _file, bytes const& _data) +{ + ofstream(_file, ios::trunc).write((char const*)_data.data(), _data.size()); +} + diff --git a/libethereum/Common.h b/libethereum/Common.h index 20e2d94b6..608ebc7e5 100644 --- a/libethereum/Common.h +++ b/libethereum/Common.h @@ -23,15 +23,27 @@ #pragma once +// define version +#define ETH_VERSION 0.3.9 + // way to many uint to size_t warnings in 32 bit build #ifdef _M_IX86 #pragma warning(disable:4244) #endif +#ifdef _MSC_VER +#define _ALLOW_KEYWORD_MACROS +#define noexcept throw() +#endif + #include #include #include #include +#include +#include +#include +#include #include #include #include @@ -42,11 +54,13 @@ #include #include "vector_ref.h" +// CryptoPP defines byte in the global namespace, so so must we. +using byte = uint8_t; + namespace eth { // Binary data types. -using byte = uint8_t; using bytes = std::vector; using bytesRef = vector_ref; using bytesConstRef = vector_ref; @@ -79,6 +93,12 @@ std::string asHex(_T const& _data, int _w = 2) return ret.str(); } +/// Converts a (printable) ASCII hex string into the corresponding byte stream. +/// @example fromUserHex("41626261") == asBytes("Abba") +bytes fromUserHex(std::string const& _s); + +template class UnitTest {}; + template class FixedHash { @@ -90,8 +110,9 @@ public: FixedHash() { m_data.fill(0); } FixedHash(Arith const& _arith) { toBigEndian(_arith, m_data); } - explicit FixedHash(bytes const& _b) { memcpy(m_data.data(), _b.data(), std::min(_b.size(), N)); } + explicit FixedHash(bytes const& _b) { if (_b.size() == N) memcpy(m_data.data(), _b.data(), std::min(_b.size(), N)); } explicit FixedHash(byte const* _bs, ConstructFromPointerType) { memcpy(m_data.data(), _bs, N); } + explicit FixedHash(std::string const& _user): FixedHash(fromUserHex(_user)) {} operator Arith() const { return fromBigEndian(m_data); } @@ -109,7 +130,7 @@ public: FixedHash operator&(FixedHash const& _c) const { return FixedHash(*this) &= _c; } FixedHash& operator~() { for (auto i = 0; i < N; ++i) m_data[i] = ~m_data[i]; return *this; } - std::string abridged() const { return asHex(ref().cropped(0, 4)); } + std::string abridged() const { return asHex(ref().cropped(0, 4)) + ".."; } byte& operator[](unsigned _i) { return m_data[_i]; } byte operator[](unsigned _i) const { return m_data[_i]; } @@ -124,10 +145,42 @@ public: std::array& asArray() { return m_data; } std::array const& asArray() const { return m_data; } + // generic std::hash compatible function object + struct hash + { + size_t operator()(FixedHash const& value) const + { + size_t h = 0; + for (auto i: value.m_data) + h = (h << 5 - h) + i; + return h; + } + }; + private: std::array m_data; }; + +// fast equality for h256 +template<> inline bool FixedHash<32>::operator==(FixedHash<32> const& _other) const +{ + const uint64_t* hash1 = (const uint64_t*)this->data(); + const uint64_t* hash2 = (const uint64_t*)_other.data(); + return (hash1[0] == hash2[0]) && (hash1[1] == hash2[1]) && (hash1[2] == hash2[2]) && (hash1[3] == hash2[3]); +} + +// fast std::hash compatible hash function object for h256 +template<> inline size_t FixedHash<32>::hash::operator()(FixedHash<32> const& value) const +{ + const uint64_t*data = (const uint64_t*)value.data(); + uint64_t hash = data[0]; + hash ^= data[1]; + hash ^= data[2]; + hash ^= data[3]; + return (size_t)hash; +} + template inline std::ostream& operator<<(std::ostream& _out, FixedHash const& _h) { @@ -160,7 +213,6 @@ using HexMap = std::map; static const u256 Invalid256 = ~(u256)0; static const bytes NullBytes; - /// Logging class NullOutputStream { @@ -179,12 +231,12 @@ struct ThreadLocalLogName extern ThreadLocalLogName t_logThreadName; inline void setThreadName(char const* _n) { t_logThreadName.m_name.reset(new std::string(_n)); } -struct LogChannel { static const char constexpr* name = " "; static const int verbosity = 1; }; -struct LeftChannel: public LogChannel { static const char constexpr* name = "<<<"; }; -struct RightChannel: public LogChannel { static const char constexpr* name = ">>>"; }; -struct WarnChannel: public LogChannel { static const char constexpr* name = "!!!"; static const int verbosity = 0; }; -struct NoteChannel: public LogChannel { static const char constexpr* name = "***"; }; -struct DebugChannel: public LogChannel { static const char constexpr* name = "---"; static const int verbosity = 7; }; +struct LogChannel { static const char* name() { return " "; } static const int verbosity = 1; }; +struct LeftChannel: public LogChannel { static const char* name() { return "<<<"; } }; +struct RightChannel: public LogChannel { static const char* name() { return ">>>"; } }; +struct WarnChannel: public LogChannel { static const char* name() { return "!!!"; } static const int verbosity = 0; }; +struct NoteChannel: public LogChannel { static const char* name() { return "***"; } }; +struct DebugChannel: public LogChannel { static const char* name() { return "---"; } static const int verbosity = 0; }; extern int g_logVerbosity; extern std::function g_logPost; @@ -203,12 +255,12 @@ public: { time_t rawTime = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()); char buf[24]; - if (strftime(buf, 24, "%X", localtime(&rawTime)) == 0) - buf[0] = '\0'; // empty if case strftime fails - sstr << Id::name << " [ " << buf << " | " << *(t_logThreadName.m_name.get()) << (_term ? " ] " : ""); + if (strftime(buf, 24, "%X", localtime(&rawTime)) == 0) + buf[0] = '\0'; // empty if case strftime fails + sstr << Id::name() << " [ " << buf << " | " << *(t_logThreadName.m_name.get()) << (_term ? " ] " : ""); } } - ~LogOutputStream() { if (Id::verbosity <= g_logVerbosity) g_logPost(sstr.str(), Id::name); } + ~LogOutputStream() { if (Id::verbosity <= g_logVerbosity) g_logPost(sstr.str(), Id::name()); } template LogOutputStream& operator<<(T const& _t) { if (Id::verbosity <= g_logVerbosity) { if (_AutoSpacing && sstr.str().size() && sstr.str().back() != ' ') sstr << " "; sstr << _t; } return *this; } std::stringstream sstr; }; @@ -298,10 +350,6 @@ std::string escaped(std::string const& _s, bool _all = true); /// @example fromHex('A') == 10 && fromHex('f') == 15 && fromHex('5') == 5 int fromHex(char _i); -/// Converts a (printable) ASCII hex string into the corresponding byte stream. -/// @example fromUserHex("41626261") == asBytes("Abba") -bytes fromUserHex(std::string const& _s); - /// Converts a string into the big-endian base-16 stream of integers (NOT ASCII). /// @example toHex("A")[0] == 4 && toHex("A")[1] == 1 bytes toHex(std::string const& _s); @@ -451,4 +499,189 @@ private: Address m_address; }; + +static const u256 Uether = ((((u256(1000000000) * 1000000000) * 1000000000) * 1000000000) * 1000000000) * 1000000000; +static const u256 Vether = ((((u256(1000000000) * 1000000000) * 1000000000) * 1000000000) * 1000000000) * 1000000; +static const u256 Dether = ((((u256(1000000000) * 1000000000) * 1000000000) * 1000000000) * 1000000000) * 1000; +static const u256 Nether = (((u256(1000000000) * 1000000000) * 1000000000) * 1000000000) * 1000000000; +static const u256 Yether = (((u256(1000000000) * 1000000000) * 1000000000) * 1000000000) * 1000000; +static const u256 Zether = (((u256(1000000000) * 1000000000) * 1000000000) * 1000000000) * 1000; +static const u256 Eether = ((u256(1000000000) * 1000000000) * 1000000000) * 1000000000; +static const u256 Pether = ((u256(1000000000) * 1000000000) * 1000000000) * 1000000; +static const u256 Tether = ((u256(1000000000) * 1000000000) * 1000000000) * 1000; +static const u256 Gether = (u256(1000000000) * 1000000000) * 1000000000; +static const u256 Mether = (u256(1000000000) * 1000000000) * 1000000; +static const u256 Kether = (u256(1000000000) * 1000000000) * 1000; +static const u256 ether = u256(1000000000) * 1000000000; +static const u256 finney = u256(1000000000) * 1000000; +static const u256 szabo = u256(1000000000) * 1000; +static const u256 Gwei = u256(1000000000); +static const u256 Mwei = u256(1000000); +static const u256 Kwei = u256(1000); +static const u256 wei = u256(1); + + +// Stream IO + + + +template struct StreamOut { static S& bypass(S& _out, T const& _t) { _out << _t; return _out; } }; +template struct StreamOut { static S& bypass(S& _out, uint8_t const& _t) { _out << (int)_t; return _out; } }; + +template +inline S& streamout(S& _out, std::vector const& _e) +{ + _out << "["; + if (!_e.empty()) + { + StreamOut::bypass(_out, _e.front()); + for (auto i = ++_e.begin(); i != _e.end(); ++i) + StreamOut::bypass(_out << ",", *i); + } + _out << "]"; + return _out; +} + +template inline std::ostream& operator<<(std::ostream& _out, std::vector const& _e) { streamout(_out, _e); return _out; } + +template +inline S& streamout(S& _out, std::array const& _e) +{ + _out << "["; + if (!_e.empty()) + { + StreamOut::bypass(_out, _e.front()); + auto i = _e.begin(); + for (++i; i != _e.end(); ++i) + StreamOut::bypass(_out << ",", *i); + } + _out << "]"; + return _out; +} +template inline std::ostream& operator<<(std::ostream& _out, std::array const& _e) { streamout(_out, _e); return _out; } + +template +inline S& streamout(S& _out, std::array const& _e) +{ + _out << "["; + if (!_e.empty()) + { + StreamOut::bypass(_out, _e.front()); + auto i = _e.begin(); + for (++i; i != _e.end(); ++i) + StreamOut::bypass(_out << ",", *i); + } + _out << "]"; + return _out; +} +template inline std::ostream& operator<<(std::ostream& _out, std::array const& _e) { streamout(_out, _e); return _out; } + +template +inline S& streamout(S& _out, std::list const& _e) +{ + _out << "["; + if (!_e.empty()) + { + _out << _e.front(); + for (auto i = ++_e.begin(); i != _e.end(); ++i) + _out << "," << *i; + } + _out << "]"; + return _out; +} +template inline std::ostream& operator<<(std::ostream& _out, std::list const& _e) { streamout(_out, _e); return _out; } + +template +inline S& streamout(S& _out, std::pair const& _e) +{ + _out << "(" << _e.first << "," << _e.second << ")"; + return _out; +} +template inline std::ostream& operator<<(std::ostream& _out, std::pair const& _e) { streamout(_out, _e); return _out; } + +template +inline S& streamout(S& _out, std::tuple const& _t) +{ + _out << "(" << std::get<0>(_t) << "," << std::get<1>(_t) << "," << std::get<2>(_t) << ")"; + return _out; +} +template inline std::ostream& operator<<(std::ostream& _out, std::tuple const& _e) { streamout(_out, _e); return _out; } + +template +S& streamout(S& _out, std::map const& _v) +{ + if (_v.empty()) + return _out << "{}"; + int i = 0; + for (auto p: _v) + _out << (!(i++) ? "{ " : "; ") << p.first << " => " << p.second; + return _out << " }"; +} +template inline std::ostream& operator<<(std::ostream& _out, std::map const& _e) { streamout(_out, _e); return _out; } + +template +S& streamout(S& _out, std::unordered_map const& _v) +{ + if (_v.empty()) + return _out << "{}"; + int i = 0; + for (auto p: _v) + _out << (!(i++) ? "{ " : "; ") << p.first << " => " << p.second; + return _out << " }"; +} +template inline std::ostream& operator<<(std::ostream& _out, std::unordered_map const& _e) { streamout(_out, _e); return _out; } + +template +S& streamout(S& _out, std::set const& _v) +{ + if (_v.empty()) + return _out << "{}"; + int i = 0; + for (auto p: _v) + _out << (!(i++) ? "{ " : ", ") << p; + return _out << " }"; +} +template inline std::ostream& operator<<(std::ostream& _out, std::set const& _e) { streamout(_out, _e); return _out; } + +template +S& streamout(S& _out, std::multiset const& _v) +{ + if (_v.empty()) + return _out << "{}"; + int i = 0; + for (auto p: _v) + _out << (!(i++) ? "{ " : ", ") << p; + return _out << " }"; +} +template inline std::ostream& operator<<(std::ostream& _out, std::multiset const& _e) { streamout(_out, _e); return _out; } + +template +S& streamout(S& _out, std::multimap const& _v) +{ + if (_v.empty()) + return _out << "{}"; + T l; + int i = 0; + for (auto p: _v) + if (!(i++)) + _out << "{ " << (l = p.first) << " => " << p.second; + else if (l == p.first) + _out << ", " << p.second; + else + _out << "; " << (l = p.first) << " => " << p.second; + return _out << " }"; +} +template inline std::ostream& operator<<(std::ostream& _out, std::multimap const& _e) { streamout(_out, _e); return _out; } + +template _S& operator<<(_S& _out, std::shared_ptr<_T> const& _p) { if (_p) _out << "@" << (*_p); else _out << "nullptr"; return _out; } + +bytes contents(std::string const& _file); +void writeFile(std::string const& _file, bytes const& _data); + +} + +namespace std +{ + // forward std::hash to eth::h256::hash + template<> struct hash: eth::h256::hash {}; } diff --git a/libethereum/Dagger.cpp b/libethereum/Dagger.cpp index 813c6d604..ee5599da9 100644 --- a/libethereum/Dagger.cpp +++ b/libethereum/Dagger.cpp @@ -1,3 +1,24 @@ +/* + 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 . +*/ +/** @file Dagger.cpp + * @author Gav Wood + * @date 2014 + */ + #include #include #include diff --git a/libethereum/Dagger.h b/libethereum/Dagger.h index 375ce245e..3f89b293b 100644 --- a/libethereum/Dagger.h +++ b/libethereum/Dagger.h @@ -1,3 +1,26 @@ +/* + 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 . +*/ +/** @file Dagger.h + * @author Gav Wood + * @date 2014 + * + * Dagger algorithm. Or not. + */ + #pragma once #include "Common.h" diff --git a/libethereum/Exceptions.h b/libethereum/Exceptions.h index 023362554..2857102c9 100644 --- a/libethereum/Exceptions.h +++ b/libethereum/Exceptions.h @@ -15,10 +15,18 @@ public: class BadHexCharacter: public Exception {}; class NotEnoughCash: public Exception {}; -class BadInstruction: public Exception {}; -class StackTooSmall: public Exception { public: StackTooSmall(u256 _req, u256 _got): req(_req), got(_got) {} u256 req; u256 got; }; -class OperandOutOfRange: public Exception { public: OperandOutOfRange(u256 _min, u256 _max, u256 _got): mn(_min), mx(_max), got(_got) {} u256 mn; u256 mx; u256 got; }; -class ExecutionException: public Exception {}; + +class RLPException: public Exception {}; +class BadCast: public RLPException {}; +class BadRLP: public RLPException {}; + +class VMException: public Exception {}; +class StepsDone: public VMException {}; +class BreakPointHit: public VMException {}; +class BadInstruction: public VMException {}; +class StackTooSmall: public VMException { public: StackTooSmall(u256 _req, u256 _got): req(_req), got(_got) {} u256 req; u256 got; }; +class OperandOutOfRange: public VMException { public: OperandOutOfRange(u256 _min, u256 _max, u256 _got): mn(_min), mx(_max), got(_got) {} u256 mn; u256 mx; u256 got; }; + class NoSuchContract: public Exception {}; class ContractAddressCollision: public Exception {}; class FeeTooSmall: public Exception {}; diff --git a/libethereum/ExtVMFace.h b/libethereum/ExtVMFace.h new file mode 100644 index 000000000..ccb49c48b --- /dev/null +++ b/libethereum/ExtVMFace.h @@ -0,0 +1,76 @@ +/* + 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 . +*/ +/** @file ExtVMFace.h + * @author Gav Wood + * @date 2014 + */ + +#pragma once + +#include "Common.h" +#include "FeeStructure.h" +#include "BlockInfo.h" + +namespace eth +{ + +struct Transaction; + +class ExtVMFace +{ +public: + ExtVMFace() {} + + ExtVMFace(FeeStructure const& _fees, BlockInfo const& _previousBlock, BlockInfo const& _currentBlock, uint _currentNumber): + fees(_fees), + previousBlock(_previousBlock), + currentBlock(_currentBlock), + currentNumber(_currentNumber) + {} + + ExtVMFace(Address _myAddress, Address _txSender, u256 _txValue, u256s const& _txData, FeeStructure const& _fees, BlockInfo const& _previousBlock, BlockInfo const& _currentBlock, uint _currentNumber): + myAddress(_myAddress), + txSender(_txSender), + txValue(_txValue), + txData(_txData), + fees(_fees), + previousBlock(_previousBlock), + currentBlock(_currentBlock), + currentNumber(_currentNumber) + {} + + u256 store(u256 _n) { return 0; } + void setStore(u256 _n, u256 _v) {} + void mktx(Transaction& _t) {} + u256 balance(Address _a) { return 0; } + void payFee(bigint _fee) {} + u256 txCount(Address _a) { return 0; } + u256 extro(Address _a, u256 _pos) { return 0; } + u256 extroPrice(Address _a) { return 0; } + void suicide(Address _a) {} + + Address myAddress; + Address txSender; + u256 txValue; + u256s txData; + FeeStructure fees; + BlockInfo previousBlock; ///< The current block's information. + BlockInfo currentBlock; ///< The current block's information. + uint currentNumber; +}; + +} diff --git a/libethereum/FeeStructure.cpp b/libethereum/FeeStructure.cpp new file mode 100644 index 000000000..df8afb21b --- /dev/null +++ b/libethereum/FeeStructure.cpp @@ -0,0 +1,49 @@ +/* + 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 . +*/ +/** @file FeeStructure.cpp + * @author Gav Wood + * @date 2014 + */ + +#include "FeeStructure.h" + +using namespace std; +using namespace eth; + +u256 const c_stepFee = 1; +u256 const c_dataFee = 20; +u256 const c_memoryFee = 5; +u256 const c_extroFee = 40; +u256 const c_cryptoFee = 20; +u256 const c_newContractFee = 100; +u256 const c_txFee = 100; + +void FeeStructure::setMultiplier(u256 _x) +{ + m_stepFee = c_stepFee * _x; + m_dataFee = c_dataFee * _x; + m_memoryFee = c_memoryFee * _x; + m_extroFee = c_extroFee * _x; + m_cryptoFee = c_cryptoFee * _x; + m_newContractFee = c_newContractFee * _x; + m_txFee = c_txFee * _x; +} + +u256 FeeStructure::multiplier() const +{ + return m_stepFee / c_stepFee; +} diff --git a/libethereum/FeeStructure.h b/libethereum/FeeStructure.h new file mode 100644 index 000000000..56b8e9c0b --- /dev/null +++ b/libethereum/FeeStructure.h @@ -0,0 +1,43 @@ +/* + 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 . +*/ +/** @file FeeStructure.h + * @author Gav Wood + * @date 2014 + */ + +#pragma once + +#include "Common.h" + +namespace eth +{ + +struct FeeStructure +{ + /// The fee structure. Values yet to be agreed on... + void setMultiplier(u256 _x); ///< The current block multiplier. + u256 multiplier() const; + u256 m_stepFee; + u256 m_dataFee; + u256 m_memoryFee; + u256 m_extroFee; + u256 m_cryptoFee; + u256 m_newContractFee; + u256 m_txFee; +}; + +} diff --git a/libethereum/FileSystem.cpp b/libethereum/FileSystem.cpp index f345641e1..559a58997 100644 --- a/libethereum/FileSystem.cpp +++ b/libethereum/FileSystem.cpp @@ -39,7 +39,9 @@ std::string eth::getDataDir() return (boost::filesystem::path(path) / "Ethereum").string(); else { + #ifndef _MSC_VER // todo? cwarn << "getDataDir(): SHGetSpecialFolderPathA() failed."; + #endif throw std::runtime_error("getDataDir() - SHGetSpecialFolderPathA() failed."); } #else diff --git a/libethereum/Instruction.cpp b/libethereum/Instruction.cpp new file mode 100644 index 000000000..660747cfe --- /dev/null +++ b/libethereum/Instruction.cpp @@ -0,0 +1,577 @@ +/* + 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 . +*/ +/** @file Instruction.cpp + * @author Gav Wood + * @date 2014 + */ + +#include "Instruction.h" + +#include +#include "Common.h" +using namespace std; +using namespace eth; + +static string readQuoted(char const*& o_d, char const* _e) +{ + string ret; + bool escaped = 0; + for (++o_d; o_d != _e && (escaped || *o_d != '"'); ++o_d) + if (!escaped && *o_d == '\\') + escaped = true; + else + ret.push_back(*o_d); + if (o_d != _e) + ++o_d; // skip last " + return ret; +} + +static u256 readNumeric(string _v, bool _quiet) +{ + u256 x = 1; + for (auto const& i: units()) + if (boost::algorithm::ends_with(_v, i.second)) + { + _v = _v.substr(0, _v.size() - i.second.size()); + x = i.first; + break; + } + try + { + return x * u256(_v); + } + catch (...) + { + if (!_quiet) + cwarn << "Invalid numeric" << _v; + } + return 0; +} + +u256s eth::assemble(std::string const& _code, bool _quiet) +{ + u256s ret; + map known; + map req; + char const* d = _code.data(); + char const* e = _code.data() + _code.size(); + while (d != e) + { + // skip to next token + for (; d != e && !isalnum(*d) && *d != '_' && *d != ':' && *d != '"'; ++d) {} + if (d == e) + break; + + if (*d == '"') + { + string s = readQuoted(d, e); + if (s.size() > 32) + { + if (!_quiet) + cwarn << "String literal > 32 characters. Cropping."; + s.resize(32); + } + h256 valHash; + memcpy(valHash.data(), s.data(), s.size()); + memset(valHash.data() + s.size(), 0, 32 - s.size()); + ret.push_back((u256)valHash); + } + else + { + char const* s = d; + for (; d != e && (isalnum(*d) || *d == '_' || *d == ':' || *d == '"'); ++d) {} + + string t = string(s, d - s); + if (isdigit(t[0])) + ret.push_back(readNumeric(t, _quiet)); + else if (t.back() == ':') + known[t.substr(0, t.size() - 1)] = (unsigned)ret.size(); + else + { + auto it = c_instructions.find(boost::algorithm::to_upper_copy(t)); + if (it != c_instructions.end()) + ret.push_back((u256)it->second); + else + { + req[(unsigned)ret.size()] = t; + ret.push_back(0); + } + } + } + } + for (auto i: req) + if (known.count(i.second)) + ret[i.first] = known[i.second]; + else + if (!_quiet) + cwarn << "Unknown assembler token" << i.second << "at address" << i.first; + + return ret; +} + +static void appendCode(u256s& o_code, vector& o_locs, u256s _code, vector& _locs) +{ + o_locs.reserve(o_locs.size() + _locs.size()); + for (auto i: _locs) + { + _code[i] += (u256)o_code.size(); + o_locs.push_back(i + (unsigned)o_code.size()); + } + o_code.reserve(o_code.size() + _code.size()); + for (auto i: _code) + o_code.push_back(i); +} + +static bool compileLispFragment(char const*& d, char const* e, bool _quiet, u256s& o_code, vector& o_locs) +{ + std::map const c_arith = { { "+", Instruction::ADD }, { "-", Instruction::SUB }, { "*", Instruction::MUL }, { "/", Instruction::DIV }, { "%", Instruction::MOD } }; + std::map const c_binary = { { "<", Instruction::LT }, { "<=", Instruction::LE }, { ">", Instruction::GT }, { ">=", Instruction::GE }, { "=", Instruction::EQ }, { "!=", Instruction::NOT } }; + std::map const c_unary = { { "!", Instruction::NOT } }; + std::set const c_allowed = { '+', '-', '*', '/', '%', '<', '>', '=', '!' }; + + bool exec = false; + + while (d != e) + { + // skip to next token + for (; d != e && !isalnum(*d) && *d != '(' && *d != ')' && *d != '_' && *d != '"' && !c_allowed.count(*d) && *d != ';'; ++d) {} + if (d == e) + break; + + switch (*d) + { + case ';': + for (; d != e && *d != '\n'; ++d) {} + break; + case '(': + exec = true; + ++d; + break; + case ')': + if (exec) + { + ++d; + return true; + } + else + // unexpected - return false as we don't know what to do with it. + return false; + default: + { + bool haveLiteral = false; + u256 literalValue = 0; + string t; + + if (*d == '"') + { + string s = readQuoted(d, e); + if (s.size() > 32) + { + if (!_quiet) + cwarn << "String literal > 32 characters. Cropping."; + s.resize(32); + } + h256 valHash; + memcpy(valHash.data(), s.data(), s.size()); + memset(valHash.data() + s.size(), 0, 32 - s.size()); + literalValue = (u256)valHash; + haveLiteral = true; + } + else + { + char const* s = d; + for (; d != e && (isalnum(*d) || *d == '_' || c_allowed.count(*d)); ++d) {} + t = string(s, d - s); + if (isdigit(t[0])) + { + literalValue = readNumeric(t, _quiet); + haveLiteral = true; + } + } + + if (haveLiteral) + { + bool bareLoad = true; + if (exec) + { + u256s codes; + vector locs; + if (compileLispFragment(d, e, _quiet, codes, locs)) + { + appendCode(o_code, o_locs, codes, locs); + while (compileLispFragment(d, e, _quiet, codes, locs)) + if (!_quiet) + cwarn << "Additional items in bare store. Ignoring."; + bareLoad = false; + } + } + o_code.push_back(Instruction::PUSH); + o_code.push_back(literalValue); + if (exec) + o_code.push_back(bareLoad ? Instruction::SLOAD : Instruction::SSTORE); + } + else + { + boost::algorithm::to_upper(t); + if (t == "IF") + { + // Compile all the code... + u256s codes[4]; + vector locs[4]; + for (int i = 0; i < 3; ++i) + if (!compileLispFragment(d, e, _quiet, codes[i], locs[i])) + return false; + if (compileLispFragment(d, e, _quiet, codes[3], locs[3])) + return false; + + // Push the positive location. + o_code.push_back(Instruction::PUSH); + unsigned posLocation = (unsigned)o_code.size(); + o_locs.push_back(posLocation); + o_code.push_back(0); + + // First fragment - predicate + appendCode(o_code, o_locs, codes[0], locs[0]); + + // Jump to positive if true. + o_code.push_back(Instruction::JMPI); + + // Second fragment - negative. + appendCode(o_code, o_locs, codes[2], locs[2]); + + // Jump to end after negative. + o_code.push_back(Instruction::PUSH); + unsigned endLocation = (unsigned)o_code.size(); + o_locs.push_back(endLocation); + o_code.push_back(0); + o_code.push_back(Instruction::JMP); + + // Third fragment - positive. + o_code[posLocation] = o_code.size(); + appendCode(o_code, o_locs, codes[1], locs[1]); + + // At end now. + o_code[endLocation] = o_code.size(); + } + else if (t == "WHEN" || t == "UNLESS") + { + // Compile all the code... + u256s codes[3]; + vector locs[3]; + for (int i = 0; i < 2; ++i) + if (!compileLispFragment(d, e, _quiet, codes[i], locs[i])) + return false; + if (compileLispFragment(d, e, _quiet, codes[2], locs[2])) + return false; + + // Push the positive location. + o_code.push_back(Instruction::PUSH); + unsigned endLocation = (unsigned)o_code.size(); + o_locs.push_back(endLocation); + o_code.push_back(0); + + // First fragment - predicate + appendCode(o_code, o_locs, codes[0], locs[0]); + + // Jump to end... + if (t == "WHEN") + o_code.push_back(Instruction::NOT); + o_code.push_back(Instruction::JMPI); + + // Second fragment - negative. + appendCode(o_code, o_locs, codes[1], locs[1]); + + // At end now. + o_code[endLocation] = o_code.size(); + } + else if (t == "FOR") + { + // Compile all the code... + u256s codes[3]; + vector locs[3]; + for (int i = 0; i < 2; ++i) + if (!compileLispFragment(d, e, _quiet, codes[i], locs[i])) + return false; + if (compileLispFragment(d, e, _quiet, codes[2], locs[2])) + return false; + + unsigned startLocation = (unsigned)o_code.size(); + + // Push the positive location. + o_code.push_back(Instruction::PUSH); + unsigned endInsertion = (unsigned)o_code.size(); + o_locs.push_back(endInsertion); + o_code.push_back(0); + + // First fragment - predicate + appendCode(o_code, o_locs, codes[0], locs[0]); + + // Jump to positive if true. + o_code.push_back(Instruction::NOT); + o_code.push_back(Instruction::JMPI); + + // Second fragment - negative. + appendCode(o_code, o_locs, codes[1], locs[1]); + + // Jump to end after negative. + o_code.push_back(Instruction::PUSH); + o_locs.push_back((unsigned)o_code.size()); + o_code.push_back(startLocation); + o_code.push_back(Instruction::JMP); + + // At end now. + o_code[endInsertion] = o_code.size(); + } + else if (t == "SEQ") + { + while (d != e) + { + u256s codes; + vector locs; + if (compileLispFragment(d, e, _quiet, codes, locs)) + appendCode(o_code, o_locs, codes, locs); + else + break; + } + } + else if (t == "AND") + { + vector codes; + vector> locs; + while (d != e) + { + codes.resize(codes.size() + 1); + locs.resize(locs.size() + 1); + if (!compileLispFragment(d, e, _quiet, codes.back(), locs.back())) + break; + } + + // last one is empty. + if (codes.size() < 2) + return false; + + codes.pop_back(); + locs.pop_back(); + + vector ends; + + if (codes.size() > 1) + { + o_code.push_back(Instruction::PUSH); + o_code.push_back(0); + + for (unsigned i = 1; i < codes.size(); ++i) + { + // Push the false location. + o_code.push_back(Instruction::PUSH); + ends.push_back((unsigned)o_code.size()); + o_locs.push_back(ends.back()); + o_code.push_back(0); + + // Check if true - predicate + appendCode(o_code, o_locs, codes[i - 1], locs[i - 1]); + + // Jump to end... + o_code.push_back(Instruction::NOT); + o_code.push_back(Instruction::JMPI); + } + o_code.push_back(Instruction::POP); + } + + // Check if true - predicate + appendCode(o_code, o_locs, codes.back(), locs.back()); + + // At end now. + for (auto i: ends) + o_code[i] = o_code.size(); + } + else if (t == "OR") + { + vector codes; + vector> locs; + while (d != e) + { + codes.resize(codes.size() + 1); + locs.resize(locs.size() + 1); + if (!compileLispFragment(d, e, _quiet, codes.back(), locs.back())) + break; + } + + // last one is empty. + if (codes.size() < 2) + return false; + + codes.pop_back(); + locs.pop_back(); + + vector ends; + + if (codes.size() > 1) + { + o_code.push_back(Instruction::PUSH); + o_code.push_back(1); + + for (unsigned i = 1; i < codes.size(); ++i) + { + // Push the false location. + o_code.push_back(Instruction::PUSH); + ends.push_back((unsigned)o_code.size()); + o_locs.push_back(ends.back()); + o_code.push_back(0); + + // Check if true - predicate + appendCode(o_code, o_locs, codes[i - 1], locs[i - 1]); + + // Jump to end... + o_code.push_back(Instruction::JMPI); + } + o_code.push_back(Instruction::POP); + } + + // Check if true - predicate + appendCode(o_code, o_locs, codes.back(), locs.back()); + + // At end now. + for (auto i: ends) + o_code[i] = o_code.size(); + } + else + { + auto it = c_instructions.find(t); + if (it != c_instructions.end()) + { + if (exec) + { + vector>> codes(1); + while (d != e && compileLispFragment(d, e, _quiet, codes.back().first, codes.back().second)) + codes.push_back(pair>()); + for (auto it = codes.rbegin(); it != codes.rend(); ++it) + appendCode(o_code, o_locs, it->first, it->second); + o_code.push_back((u256)it->second); + } + else + { + o_code.push_back(Instruction::PUSH); + o_code.push_back(it->second); + } + } + else + { + auto it = c_arith.find(t); + if (it != c_arith.end()) + { + int i = 0; + while (d != e) + { + u256s codes; + vector locs; + if (compileLispFragment(d, e, _quiet, codes, locs)) + { + appendCode(o_code, o_locs, codes, locs); + if (i) + o_code.push_back((u256)it->second); + ++i; + } + else + break; + } + } + else + { + auto it = c_binary.find(t); + if (it != c_binary.end()) + { + vector>> codes(1); + while (d != e && compileLispFragment(d, e, _quiet, codes.back().first, codes.back().second)) + codes.push_back(pair>()); + codes.pop_back(); + int i = (int)codes.size(); + if (i > 2) + cwarn << "Greater than two arguments given to binary operator" << t << "; using first two only."; + for (auto it = codes.rbegin(); it != codes.rend(); ++it) + if (--i < 2) + appendCode(o_code, o_locs, it->first, it->second); + if (it->second == Instruction::NOT) + o_code.push_back(Instruction::EQ); + o_code.push_back((u256)it->second); + } + else + { + auto it = c_unary.find(t); + if (it != c_unary.end()) + { + vector>> codes(1); + while (d != e && compileLispFragment(d, e, _quiet, codes.back().first, codes.back().second)) + codes.push_back(pair>()); + codes.pop_back(); + int i = (int)codes.size(); + if (i > 1) + cwarn << "Greater than one argument given to unary operator" << t << "; using first only."; + for (auto it = codes.rbegin(); it != codes.rend(); ++it) + if (--i < 1) + appendCode(o_code, o_locs, it->first, it->second); + o_code.push_back(it->second); + } + else if (!_quiet) + cwarn << "Unknown assembler token" << t; + } + } + } + } + } + + if (!exec) + return true; + } + } + } + return false; +} + +u256s eth::compileLisp(std::string const& _code, bool _quiet) +{ + char const* d = _code.data(); + char const* e = _code.data() + _code.size(); + u256s ret; + vector locs; + compileLispFragment(d, e, _quiet, ret, locs); + return ret; +} + +string eth::disassemble(u256s const& _mem) +{ + stringstream ret; + uint numerics = 0; + for (auto it = _mem.begin(); it != _mem.end(); ++it) + { + u256 n = *it; + auto iit = c_instructionInfo.find((Instruction)(uint)n); + if (numerics || iit == c_instructionInfo.end() || (u256)(uint)iit->first != n) // not an instruction or expecting an argument... + { + if (numerics) + numerics--; + ret << "0x" << hex << n << " "; + } + else + { + auto const& ii = iit->second; + ret << ii.name << " "; + numerics = ii.additional; + } + } + return ret.str(); +} diff --git a/libethereum/Instruction.h b/libethereum/Instruction.h index 833b22997..1c17e61a6 100644 --- a/libethereum/Instruction.h +++ b/libethereum/Instruction.h @@ -1,5 +1,28 @@ +/* + 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 . +*/ +/** @file Instruction.h + * @author Gav Wood + * @date 2014 + */ + #pragma once +#include "Common.h" + namespace eth { @@ -10,8 +33,8 @@ enum class Instruction: uint8_t { STOP = 0x00, ///< halts execution ADD, - SUB, MUL, + SUB, DIV, SDIV, MOD, @@ -24,10 +47,9 @@ enum class Instruction: uint8_t GE, EQ, NOT, - MYADDRESS = 0x10, + MYADDRESS, ///< pushes the transaction sender TXSENDER, ///< pushes the transaction sender TXVALUE , ///< pushes the transaction value - TXFEE, ///< pushes the transaction fee TXDATAN, ///< pushes the number of data items TXDATA, ///< pops one item and pushes data item S[-1], or zero if index out of range BLK_PREVHASH, ///< pushes the hash of the previous block (NOT the current one since that's impossible!) @@ -35,6 +57,8 @@ enum class Instruction: uint8_t BLK_TIMESTAMP, ///< pushes the timestamp of the current block BLK_NUMBER, ///< pushes the current block number BLK_DIFFICULTY, ///< pushes the difficulty of the current block + BLK_NONCE, + BASEFEE, SHA256 = 0x20, RIPEMD160, ECMUL, @@ -46,18 +70,140 @@ enum class Instruction: uint8_t PUSH = 0x30, POP, DUP, - DUPN, SWAP, - SWAPN, - LOAD, - STORE, - JMP = 0x40, + MLOAD, + MSTORE, + SLOAD, + SSTORE, + JMP, JMPI, IND, - EXTRO = 0x50, + EXTRO, BALANCE, - MKTX = 0x60, - SUICIDE = 0xff + MKTX, + SUICIDE = 0x3f }; +struct InstructionInfo +{ + std::string name; + int additional; + int args; + int ret; +}; + +static const std::map c_instructionInfo = +{ + { Instruction::STOP, { "STOP", 0, 0, 0 } }, + { Instruction::ADD, { "ADD", 0, 2, 1 } }, + { Instruction::SUB, { "SUB", 0, 2, 1 } }, + { Instruction::MUL, { "MUL", 0, 2, 1 } }, + { Instruction::DIV, { "DIV", 0, 2, 1 } }, + { Instruction::SDIV, { "SDIV", 0, 2, 1 } }, + { Instruction::MOD, { "MOD", 0, 2, 1 } }, + { Instruction::SMOD, { "SMOD", 0, 2, 1 } }, + { Instruction::EXP, { "EXP", 0, 2, 1 } }, + { Instruction::NEG, { "NEG", 0, 1, 1 } }, + { Instruction::LT, { "LT", 0, 2, 1 } }, + { Instruction::LE, { "LE", 0, 2, 1 } }, + { Instruction::GT, { "GT", 0, 2, 1 } }, + { Instruction::GE, { "GE", 0, 2, 1 } }, + { Instruction::EQ, { "EQ", 0, 2, 1 } }, + { Instruction::NOT, { "NOT", 0, 1, 1 } }, + { Instruction::MYADDRESS, { "MYADDRESS", 0, 0, 1 } }, + { Instruction::TXSENDER, { "TXSENDER", 0, 0, 1 } }, + { Instruction::TXVALUE, { "TXVALUE", 0, 0, 1 } }, + { Instruction::TXDATAN, { "TXDATAN", 0, 0, 1 } }, + { Instruction::TXDATA, { "TXDATA", 0, 1, 1 } }, + { Instruction::BLK_PREVHASH, { "BLK_PREVHASH", 0, 0, 1 } }, + { Instruction::BLK_COINBASE, { "BLK_COINBASE", 0, 0, 1 } }, + { Instruction::BLK_TIMESTAMP, { "BLK_TIMESTAMP", 0, 0, 1 } }, + { Instruction::BLK_NUMBER, { "BLK_NUMBER", 0, 0, 1 } }, + { Instruction::BLK_DIFFICULTY, { "BLK_DIFFICULTY", 0, 0, 1 } }, + { Instruction::BLK_NONCE, { "BLK_NONCE", 0, 0, 1 } }, + { Instruction::BASEFEE, { "BASEFEE", 0, 0, 1 } }, + { Instruction::SHA256, { "SHA256", 0, -1, 1 } }, + { Instruction::RIPEMD160, { "RIPEMD160", 0, -1, 1 } }, + { Instruction::ECMUL, { "ECMUL", 0, 3, 1 } }, + { Instruction::ECADD, { "ECADD", 0, 4, 1 } }, + { Instruction::ECSIGN, { "ECSIGN", 0, 2, 1 } }, + { Instruction::ECRECOVER, { "ECRECOVER", 0, 4, 1 } }, + { Instruction::ECVALID, { "ECVALID", 0, 2, 1 } }, + { Instruction::SHA3, { "SHA3", 0, -1, 1 } }, + { Instruction::PUSH, { "PUSH", 1, 0, 1 } }, + { Instruction::POP, { "POP", 0, 1, 0 } }, + { Instruction::DUP, { "DUP", 0, 1, 2 } }, + { Instruction::SWAP, { "SWAP", 0, 2, 2 } }, + { Instruction::MLOAD, { "MLOAD", 0, 1, 1 } }, + { Instruction::MSTORE, { "MSTORE", 0, 2, 0 } }, + { Instruction::SLOAD, { "SLOAD", 0, 1, 1 } }, + { Instruction::SSTORE, { "SSTORE", 0, 2, 0 } }, + { Instruction::JMP, { "JMP", 0, 1, 0 } }, + { Instruction::JMPI, { "JMPI", 0, 2, 0 } }, + { Instruction::IND, { "IND", 0, 0, 1 } }, + { Instruction::EXTRO, { "EXTRO", 0, 2, 1 } }, + { Instruction::BALANCE, { "BALANCE", 0, 1, 1 } }, + { Instruction::MKTX, { "MKTX", 0, -3, 0 } }, + { Instruction::SUICIDE, { "SUICIDE", 0, 1, 0} } +}; + +static const std::map c_instructions = +{ + { "STOP", Instruction::STOP }, + { "ADD", Instruction::ADD }, + { "SUB", Instruction::SUB }, + { "MUL", Instruction::MUL }, + { "DIV", Instruction::DIV }, + { "SDIV", Instruction::SDIV }, + { "MOD", Instruction::MOD }, + { "SMOD", Instruction::SMOD }, + { "EXP", Instruction::EXP }, + { "NEG", Instruction::NEG }, + { "LT", Instruction::LT }, + { "LE", Instruction::LE }, + { "GT", Instruction::GT }, + { "GE", Instruction::GE }, + { "EQ", Instruction::EQ }, + { "NOT", Instruction::NOT }, + { "MYADDRESS", Instruction::MYADDRESS }, + { "TXSENDER", Instruction::TXSENDER }, + { "TXVALUE", Instruction::TXVALUE }, + { "TXDATAN", Instruction::TXDATAN }, + { "TXDATA", Instruction::TXDATA }, + { "BLK_PREVHASH", Instruction::BLK_PREVHASH }, + { "BLK_COINBASE", Instruction::BLK_COINBASE }, + { "BLK_TIMESTAMP", Instruction::BLK_TIMESTAMP }, + { "BLK_NUMBER", Instruction::BLK_NUMBER }, + { "BLK_DIFFICULTY", Instruction::BLK_DIFFICULTY }, + { "BLK_NONCE", Instruction::BLK_NONCE }, + { "BASEFEE", Instruction::BASEFEE }, + { "SHA256", Instruction::SHA256 }, + { "RIPEMD160", Instruction::RIPEMD160 }, + { "ECMUL", Instruction::ECMUL }, + { "ECADD", Instruction::ECADD }, + { "ECSIGN", Instruction::ECSIGN }, + { "ECRECOVER", Instruction::ECRECOVER }, + { "ECVALID", Instruction::ECVALID }, + { "SHA3", Instruction::SHA3 }, + { "PUSH", Instruction::PUSH }, + { "POP", Instruction::POP }, + { "DUP", Instruction::DUP }, + { "SWAP", Instruction::SWAP }, + { "MLOAD", Instruction::MLOAD }, + { "MSTORE", Instruction::MSTORE }, + { "SLOAD", Instruction::SLOAD }, + { "SSTORE", Instruction::SSTORE }, + { "JMP", Instruction::JMP }, + { "JMPI", Instruction::JMPI }, + { "IND", Instruction::IND }, + { "EXTRO", Instruction::EXTRO }, + { "BALANCE", Instruction::BALANCE }, + { "MKTX", Instruction::MKTX }, + { "SUICIDE", Instruction::SUICIDE } +}; + +u256s assemble(std::string const& _code, bool _quiet = false); +std::string disassemble(u256s const& _mem); +u256s compileLisp(std::string const& _code, bool _quiet = false); + } diff --git a/libethereum/LibEthereum.props b/libethereum/LibEthereum.props deleted file mode 100644 index be1925b9a..000000000 --- a/libethereum/LibEthereum.props +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - $(IncludePath);../../boost_1_55_0;../../leveldb-1.15.0/include;../../cryptopp562;../../secp256k1/include - ..\build\$(ProjectName)\$(Platform)_$(Configuration)\ - ..\build\$(ProjectName)\$(Platform)_$(Configuration)\ - - - - 4100;4127;4505;4512; - Level4 - true - false - - - - \ No newline at end of file diff --git a/libethereum/LibEthereum.vcxproj.filters b/libethereum/LibEthereum.vcxproj.filters deleted file mode 100644 index 17a7b35f1..000000000 --- a/libethereum/LibEthereum.vcxproj.filters +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/libethereum/PeerNetwork.cpp b/libethereum/PeerNetwork.cpp index 89787aa9d..c77af38eb 100644 --- a/libethereum/PeerNetwork.cpp +++ b/libethereum/PeerNetwork.cpp @@ -43,7 +43,7 @@ using namespace eth; #define clogS(X) eth::LogOutputStream(false) << "| " << std::setw(2) << m_socket.native_handle() << "] " -static const int c_protocolVersion = 4; +static const int c_protocolVersion = 7; static const eth::uint c_maxHashes = 32; ///< Maximum number of hashes GetChain will ever send. static const eth::uint c_maxBlocks = 32; ///< Maximum number of blocks Blocks will ever send. BUG: if this gets too big (e.g. 2048) stuff starts going wrong. @@ -80,7 +80,7 @@ bool eth::isPrivateAddress(bi::address _addressToCheck) return false; } -PeerSession::PeerSession(PeerServer* _s, bi::tcp::socket _socket, uint _rNId, bi::address _peerAddress, short _peerPort): +PeerSession::PeerSession(PeerServer* _s, bi::tcp::socket _socket, uint _rNId, bi::address _peerAddress, unsigned short _peerPort): m_server(_s), m_socket(std::move(_socket)), m_reqNetworkId(_rNId), @@ -137,7 +137,7 @@ bool PeerSession::interpret(RLP const& _r) m_networkId = _r[2].toInt(); auto clientVersion = _r[3].toString(); m_caps = _r[4].toInt(); - m_listenPort = _r[5].toInt(); + m_listenPort = _r[5].toInt(); m_id = _r[6].toHash(); clogS(NetMessageSummary) << "Hello: " << clientVersion << "V[" << m_protocolVersion << "/" << m_networkId << "]" << m_id.abridged() << showbase << hex << m_caps << dec << m_listenPort; @@ -170,12 +170,12 @@ bool PeerSession::interpret(RLP const& _r) // Grab their block chain off them. { clogS(NetAllDetail) << "Want chain. Latest:" << m_server->m_latestBlockSent << ", number:" << m_server->m_chain->details(m_server->m_latestBlockSent).number; - unsigned count = std::min(c_maxHashes, m_server->m_chain->details(m_server->m_latestBlockSent).number + 1); + uint count = std::min(c_maxHashes, m_server->m_chain->details(m_server->m_latestBlockSent).number + 1); RLPStream s; prep(s).appendList(2 + count); s << GetChainPacket; auto h = m_server->m_latestBlockSent; - for (unsigned i = 0; i < count; ++i, h = m_server->m_chain->details(h).parent) + for (uint i = 0; i < count; ++i, h = m_server->m_chain->details(h).parent) { clogS(NetAllDetail) << " " << i << ":" << h; s << h; @@ -407,12 +407,12 @@ bool PeerSession::interpret(RLP const& _r) } else { - unsigned count = std::min(c_maxHashes, m_server->m_chain->details(noGood).number); + uint count = std::min(c_maxHashes, m_server->m_chain->details(noGood).number); RLPStream s; prep(s).appendList(2 + count); s << GetChainPacket; auto h = m_server->m_chain->details(noGood).parent; - for (unsigned i = 0; i < count; ++i, h = m_server->m_chain->details(h).parent) + for (uint i = 0; i < count; ++i, h = m_server->m_chain->details(h).parent) s << h; s << c_maxBlocksAsk; sealAndSend(s); @@ -450,7 +450,7 @@ void PeerServer::seal(bytes& _b) _b[1] = 0x40; _b[2] = 0x08; _b[3] = 0x91; - uint32_t len = _b.size() - 8; + uint32_t len = (uint32_t)_b.size() - 8; _b[4] = (len >> 24) & 0xff; _b[5] = (len >> 16) & 0xff; _b[6] = (len >> 8) & 0xff; @@ -483,19 +483,21 @@ bool PeerSession::checkPacket(bytesConstRef _msg) void PeerSession::sendDestroy(bytes& _msg) { clogS(NetLeft) << RLP(bytesConstRef(&_msg).cropped(8)); - std::shared_ptr buffer = std::make_shared(); - swap(*buffer, _msg); - if (!checkPacket(bytesConstRef(&*buffer))) + + if (!checkPacket(bytesConstRef(&_msg))) { cwarn << "INVALID PACKET CONSTRUCTED!"; - } - ba::async_write(m_socket, ba::buffer(*buffer), [=](boost::system::error_code ec, std::size_t length) + + auto self(shared_from_this()); + bytes* buffer = new bytes(std::move(_msg)); + ba::async_write(m_socket, ba::buffer(*buffer), [self,buffer](boost::system::error_code ec, std::size_t length) { + delete buffer; if (ec) { cwarn << "Error sending: " << ec.message(); - dropped(); + self->dropped(); } // cbug << length << " bytes written (EC: " << ec << ")"; }); @@ -504,17 +506,21 @@ void PeerSession::sendDestroy(bytes& _msg) void PeerSession::send(bytesConstRef _msg) { clogS(NetLeft) << RLP(_msg.cropped(8)); - std::shared_ptr buffer = std::make_shared(_msg.toBytes()); + if (!checkPacket(_msg)) { cwarn << "INVALID PACKET CONSTRUCTED!"; } - ba::async_write(m_socket, ba::buffer(*buffer), [=](boost::system::error_code ec, std::size_t length) + + auto self(shared_from_this()); + bytes* buffer = new bytes(_msg.toBytes()); + ba::async_write(m_socket, ba::buffer(*buffer), [self,buffer](boost::system::error_code ec, std::size_t length) { + delete buffer; if (ec) { cwarn << "Error sending: " << ec.message(); - dropped(); + self->dropped(); } // cbug << length << " bytes written (EC: " << ec << ")"; }); @@ -568,7 +574,7 @@ void PeerSession::start() void PeerSession::doRead() { auto self(shared_from_this()); - m_socket.async_read_some(boost::asio::buffer(m_data), [this, self](boost::system::error_code ec, std::size_t length) + m_socket.async_read_some(boost::asio::buffer(m_data), [this,self](boost::system::error_code ec, std::size_t length) { if (ec) { @@ -635,7 +641,7 @@ void PeerSession::doRead() }); } -PeerServer::PeerServer(std::string const& _clientVersion, BlockChain const& _ch, uint _networkId, short _port, NodeMode _m, string const& _publicAddress, bool _upnp): +PeerServer::PeerServer(std::string const& _clientVersion, BlockChain const& _ch, uint _networkId, unsigned short _port, NodeMode _m, string const& _publicAddress, bool _upnp): m_clientVersion(_clientVersion), m_mode(_m), m_listenPort(_port), @@ -654,7 +660,7 @@ PeerServer::PeerServer(std::string const& _clientVersion, BlockChain const& _ch, PeerServer::PeerServer(std::string const& _clientVersion, uint _networkId, NodeMode _m): m_clientVersion(_clientVersion), m_mode(_m), - m_listenPort(-1), + m_listenPort(0), m_acceptor(m_ioService, bi::tcp::endpoint(bi::tcp::v4(), 0)), m_socket(m_ioService), m_key(KeyPair::create()), @@ -698,10 +704,10 @@ void PeerServer::determinePublic(string const& _publicAddress, bool _upnp) auto eip = m_upnp->externalIP(); if (eip == string("0.0.0.0") && _publicAddress.empty()) - m_public = bi::tcp::endpoint(bi::address(), p); + m_public = bi::tcp::endpoint(bi::address(), (unsigned short)p); else { - m_public = bi::tcp::endpoint(bi::address::from_string(_publicAddress.empty() ? eip : _publicAddress), p); + m_public = bi::tcp::endpoint(bi::address::from_string(_publicAddress.empty() ? eip : _publicAddress), (unsigned short)p); m_addresses.push_back(m_public.address().to_v4()); } } @@ -746,7 +752,7 @@ void PeerServer::populateAddresses() bi::address ad(bi::address::from_string(addrStr)); m_addresses.push_back(ad.to_v4()); bool isLocal = std::find(c_rejectAddresses.begin(), c_rejectAddresses.end(), ad) != c_rejectAddresses.end(); - if (isLocal) + if (!isLocal) m_peerAddresses.push_back(ad.to_v4()); clog(NetNote) << "Address: " << ac << " = " << m_addresses.back() << (isLocal ? " [LOCAL]" : " [PEER]"); } @@ -768,6 +774,7 @@ void PeerServer::populateAddresses() char host[NI_MAXHOST]; if (getnameinfo(ifa->ifa_addr, sizeof(struct sockaddr_in), host, NI_MAXHOST, NULL, 0, NI_NUMERICHOST)) continue; + // TODO: Make exception safe when no internet. auto it = r.resolve({host, "30303"}); bi::tcp::endpoint ep = it->endpoint(); bi::address ad = ep.address(); @@ -831,7 +838,7 @@ void PeerServer::ensureAccepting() } } -void PeerServer::connect(std::string const& _addr, uint _port) noexcept +void PeerServer::connect(std::string const& _addr, unsigned short _port) noexcept { try { @@ -866,7 +873,7 @@ void PeerServer::connect(bi::tcp::endpoint const& _ep) else { auto p = make_shared(this, std::move(*s), m_requiredNetworkId, _ep.address(), _ep.port()); - clog(NetNote) << "Connected to " << p->endpoint(); + clog(NetNote) << "Connected to " << _ep; p->start(); } delete s; @@ -924,6 +931,9 @@ bool PeerServer::sync(BlockChain& _bc, TransactionQueue& _tq, Overlay& _o) m_transactionsSent.insert(sha3(*it)); // if we already had the transaction, then don't bother sending it on. m_incomingTransactions.clear(); + auto h = _bc.currentHash(); + bool resendAll = (h != m_latestBlockSent); + // Send any new transactions. for (auto j: m_peers) if (auto p = j.second.lock()) @@ -931,7 +941,7 @@ bool PeerServer::sync(BlockChain& _bc, TransactionQueue& _tq, Overlay& _o) bytes b; uint n = 0; for (auto const& i: _tq.transactions()) - if ((!m_transactionsSent.count(i.first) && !p->m_knownTransactions.count(i.first)) || p->m_requireTransactions) + if ((!m_transactionsSent.count(i.first) && !p->m_knownTransactions.count(i.first)) || p->m_requireTransactions || resendAll) { b += i.second; ++n; @@ -951,7 +961,6 @@ bool PeerServer::sync(BlockChain& _bc, TransactionQueue& _tq, Overlay& _o) } // Send any new blocks. - auto h = _bc.currentHash(); if (h != m_latestBlockSent) { // TODO: find where they diverge and send complete new branch. diff --git a/libethereum/PeerNetwork.h b/libethereum/PeerNetwork.h index 9edfe0cb6..80c6b054c 100644 --- a/libethereum/PeerNetwork.h +++ b/libethereum/PeerNetwork.h @@ -40,13 +40,13 @@ bool isPrivateAddress(bi::address _addressToCheck); class BlockChain; class TransactionQueue; -struct NetWarn: public LogChannel { static const char constexpr* name = "!N!"; static const int verbosity = 0; }; -struct NetNote: public LogChannel { static const char constexpr* name = "*N*"; static const int verbosity = 1; }; -struct NetMessageSummary: public LogChannel { static const char constexpr* name = "-N-"; static const int verbosity = 2; }; -struct NetMessageDetail: public LogChannel { static const char constexpr* name = "=N="; static const int verbosity = 3; }; -struct NetAllDetail: public LogChannel { static const char constexpr* name = "=N="; static const int verbosity = 6; }; -struct NetRight: public LogChannel { static const char constexpr* name = ">N>"; static const int verbosity = 8; }; -struct NetLeft: public LogChannel { static const char constexpr* name = "N>"; } static const int verbosity = 8; }; +struct NetLeft: public LogChannel { static const char* name() { return " friend class PeerServer; public: - PeerSession(PeerServer* _server, bi::tcp::socket _socket, uint _rNId, bi::address _peerAddress, short _peerPort = 0); + PeerSession(PeerServer* _server, bi::tcp::socket _socket, uint _rNId, bi::address _peerAddress, unsigned short _peerPort = 0); ~PeerSession(); void start(); @@ -127,14 +127,14 @@ private: uint m_protocolVersion; uint m_networkId; uint m_reqNetworkId; - short m_listenPort; ///< Port that the remote client is listening on for connections. Useful for giving to peers. + unsigned short m_listenPort; ///< Port that the remote client is listening on for connections. Useful for giving to peers. uint m_caps; std::chrono::steady_clock::time_point m_ping; std::chrono::steady_clock::time_point m_connect; std::chrono::steady_clock::time_point m_disconnect; - unsigned m_rating; + uint m_rating; bool m_requireTransactions; std::set m_knownBlocks; @@ -147,7 +147,7 @@ enum class NodeMode PeerServer }; -struct UPnP; +class UPnP; class PeerServer { @@ -155,13 +155,13 @@ class PeerServer public: /// Start server, listening for connections on the given port. - PeerServer(std::string const& _clientVersion, BlockChain const& _ch, uint _networkId, short _port, NodeMode _m = NodeMode::Full, std::string const& _publicAddress = std::string(), bool _upnp = true); + PeerServer(std::string const& _clientVersion, BlockChain const& _ch, uint _networkId, unsigned short _port, NodeMode _m = NodeMode::Full, std::string const& _publicAddress = std::string(), bool _upnp = true); /// Start server, but don't listen. PeerServer(std::string const& _clientVersion, uint _networkId, NodeMode _m = NodeMode::Full); ~PeerServer(); /// Connect to a peer explicitly. - void connect(std::string const& _addr, uint _port = 30303) noexcept; + void connect(std::string const& _addr, unsigned short _port = 30303) noexcept; void connect(bi::tcp::endpoint const& _ep); /// Sync with the BlockChain. It might contain one of our mined blocks, we might have new candidates from the network. @@ -182,13 +182,13 @@ public: std::vector peers() const; /// Get number of peers connected; equivalent to, but faster than, peers().size(). - unsigned peerCount() const { return m_peers.size(); } + size_t peerCount() const { return m_peers.size(); } /// Ping the peers, to update the latency information. void pingAll(); /// Get the port we're listening on currently. - short listenPort() const { return m_public.port(); } + unsigned short listenPort() const { return m_public.port(); } bytes savePeers() const; void restorePeers(bytesConstRef _b); @@ -209,7 +209,7 @@ private: std::string m_clientVersion; NodeMode m_mode = NodeMode::Full; - short m_listenPort; + unsigned short m_listenPort; BlockChain const* m_chain = nullptr; ba::io_service m_ioService; diff --git a/libethereum/RLP.cpp b/libethereum/RLP.cpp index 3cc0cb5eb..e8647e8de 100644 --- a/libethereum/RLP.cpp +++ b/libethereum/RLP.cpp @@ -102,9 +102,17 @@ bool RLP::isInt() const else if (n == c_rlpDataImmLenStart) return true; else if (n <= c_rlpDataIndLenZero) - return m_data[1]; + { + if (m_data.size() <= 1) + throw BadRLP(); + return m_data[1] != 0; + } else if (n < c_rlpListStart) - return m_data[1 + n - c_rlpDataIndLenZero]; + { + if ((int)m_data.size() <= 1 + n - c_rlpDataIndLenZero) + throw BadRLP(); + return m_data[1 + n - c_rlpDataIndLenZero] != 0; + } else return false; return false; @@ -121,13 +129,21 @@ eth::uint RLP::length() const else if (n <= c_rlpDataIndLenZero) return n - c_rlpDataImmLenStart; else if (n < c_rlpListStart) + { + if ((int)m_data.size() <= n - c_rlpDataIndLenZero) + throw BadRLP(); for (int i = 0; i < n - c_rlpDataIndLenZero; ++i) ret = (ret << 8) | m_data[i + 1]; + } else if (n <= c_rlpListIndLenZero) return n - c_rlpListStart; else + { + if ((int)m_data.size() <= n - c_rlpListIndLenZero) + throw BadRLP(); for (int i = 0; i < n - c_rlpListIndLenZero; ++i) ret = (ret << 8) | m_data[i + 1]; + } return ret; } @@ -176,10 +192,10 @@ void RLPStream::noteAppended(uint _itemCount) m_out.resize(os + encodeSize); memmove(m_out.data() + p + encodeSize, m_out.data() + p, os - p); if (s < c_rlpListImmLenCount) - m_out[p] = c_rlpListStart + s; + m_out[p] = (byte)(c_rlpListStart + s); else { - m_out[p] = c_rlpListIndLenZero + brs; + m_out[p] = (byte)(c_rlpListIndLenZero + brs); byte* b = &(m_out[p + brs]); for (; s; s >>= 8) *(b--) = (byte)s; @@ -189,7 +205,7 @@ void RLPStream::noteAppended(uint _itemCount) } } -RLPStream& RLPStream::appendList(unsigned _items) +RLPStream& RLPStream::appendList(uint _items) { // cdebug << "appendList(" << _items << ")"; if (_items) diff --git a/libethereum/RLP.h b/libethereum/RLP.h index df047dfb4..9a4448524 100644 --- a/libethereum/RLP.h +++ b/libethereum/RLP.h @@ -30,6 +30,7 @@ #include #include "vector_ref.h" #include "Common.h" +#include "Exceptions.h" namespace eth { @@ -60,8 +61,6 @@ static const byte c_rlpListIndLenZero = c_rlpListStart + c_rlpListImmLenCount - class RLP { public: - class BadCast: public std::exception {}; - /// Construct a null node. RLP() {} @@ -175,7 +174,7 @@ public: std::string toStringStrict() const { if (!isData()) throw BadCast(); return payload().cropped(0, length()).toString(); } template std::vector toVector() const { std::vector ret; if (isList()) { ret.reserve(itemCount()); for (auto const& i: *this) ret.push_back((T)i); } return ret; } - template std::array toArray() const { std::array ret; if (itemCount() != N) throw BadCast(); if (isList()) for (uint i = 0; i < N; ++i) ret[i] = (T)operator[](i); return ret; } + template std::array toArray() const { if (itemCount() != N || !isList()) throw BadCast(); std::array ret; for (uint i = 0; i < N; ++i) ret[i] = (T)operator[](i); return ret; } /// Int conversion flags enum @@ -288,7 +287,7 @@ public: template RLPStream& append(std::array<_T, S> const& _s) { appendList(_s.size()); for (auto const& i: _s) append(i); return *this; } /// Appends a list. - RLPStream& appendList(unsigned _items); + RLPStream& appendList(uint _items); RLPStream& appendList(bytesConstRef _rlp); RLPStream& appendList(bytes const& _rlp) { return appendList(&_rlp); } RLPStream& appendList(RLPStream const& _s) { return appendList(&_s.out()); } diff --git a/libethereum/State.cpp b/libethereum/State.cpp index d18ea4662..74db7aeb4 100644 --- a/libethereum/State.cpp +++ b/libethereum/State.cpp @@ -23,19 +23,6 @@ #include #include -#if WIN32 -#pragma warning(push) -#pragma warning(disable:4244) -#else -#pragma GCC diagnostic ignored "-Wunused-function" -#endif -#include -#include -#include -#if WIN32 -#pragma warning(pop) -#else -#endif #include #include #include "BlockChain.h" @@ -43,18 +30,11 @@ #include "Exceptions.h" #include "Dagger.h" #include "Defaults.h" +#include "VM.h" using namespace std; using namespace eth; -u256 const c_stepFee = 1; -u256 const c_dataFee = 20; -u256 const c_memoryFee = 5; -u256 const c_extroFee = 40; -u256 const c_cryptoFee = 20; -u256 const c_newContractFee = 100; -u256 const c_txFee = 100; - -u256 const eth::c_genesisDifficulty = (u256)1 << 22; +u256 eth::c_genesisDifficulty = (u256)1 << 22; std::map const& eth::genesisState() { @@ -62,10 +42,10 @@ std::map const& eth::genesisState() if (s_ret.empty()) { // Initialise. - s_ret[Address(fromUserHex("8a40bfaa73256b60764c1bf40675a99083efb075"))] = AddressState(u256(1) << 200, 0); - s_ret[Address(fromUserHex("93658b04240e4bd4046fd2d6d417d20f146f4b43"))] = AddressState(u256(1) << 200, 0); - s_ret[Address(fromUserHex("1e12515ce3e0f817a4ddef9ca55788a1d66bd2df"))] = AddressState(u256(1) << 200, 0); - s_ret[Address(fromUserHex("80c01a26338f0d905e295fccb71fa9ea849ffa12"))] = AddressState(u256(1) << 200, 0); + s_ret[Address(fromUserHex("8a40bfaa73256b60764c1bf40675a99083efb075"))] = AddressState(u256(1) << 200, 0, AddressType::Normal); + s_ret[Address(fromUserHex("e6716f9544a56c530d868e4bfbacb172315bdead"))] = AddressState(u256(1) << 200, 0, AddressType::Normal); + s_ret[Address(fromUserHex("1e12515ce3e0f817a4ddef9ca55788a1d66bd2df"))] = AddressState(u256(1) << 200, 0, AddressType::Normal); + s_ret[Address(fromUserHex("1a26338f0d905e295fccb71fa9ea849ffa12aaf4"))] = AddressState(u256(1) << 200, 0, AddressType::Normal); } return s_ret; } @@ -90,8 +70,8 @@ State::State(Address _coinbaseAddress, Overlay const& _db): m_state(&m_db), m_ourAddress(_coinbaseAddress) { - m_blockReward = u256(15000000000) * 100000000; - m_fees.setMultiplier(u256(100000) * 1000000000); + m_blockReward = 1500 * finney; + m_fees.setMultiplier(100 * szabo); secp256k1_start(); @@ -111,6 +91,7 @@ State::State(State const& _s): m_db(_s.m_db), m_state(&m_db, _s.m_state.root()), m_transactions(_s.m_transactions), + m_transactionSet(_s.m_transactionSet), m_cache(_s.m_cache), m_previousBlock(_s.m_previousBlock), m_currentBlock(_s.m_currentBlock), @@ -126,6 +107,7 @@ State& State::operator=(State const& _s) m_db = _s.m_db; m_state.open(&m_db, _s.m_state.root()); m_transactions = _s.m_transactions; + m_transactionSet = _s.m_transactionSet; m_cache = _s.m_cache; m_previousBlock = _s.m_previousBlock; m_currentBlock = _s.m_currentBlock; @@ -136,17 +118,6 @@ State& State::operator=(State const& _s) return *this; } -void FeeStructure::setMultiplier(u256 _x) -{ - m_stepFee = c_stepFee * _x; - m_dataFee = c_dataFee * _x; - m_memoryFee = c_memoryFee * _x; - m_extroFee = c_extroFee * _x; - m_cryptoFee = c_cryptoFee * _x; - m_newContractFee = c_newContractFee * _x; - m_txFee = c_txFee * _x; -} - void State::ensureCached(Address _a, bool _requireMemory, bool _forceCreate) const { auto it = m_cache.find(_a); @@ -171,13 +142,17 @@ void State::ensureCached(Address _a, bool _requireMemory, bool _forceCreate) con { // Populate memory. assert(it->second.type() == AddressType::Contract); - TrieDB memdb(const_cast(&m_db), it->second.oldRoot()); // promise we won't alter the overlay! :) - map& mem = it->second.takeMemory(); + TrieDB memdb(const_cast(&m_db), it->second.oldRoot()); // promise we won't alter the overlay! :) + map& mem = it->second.setHaveMemory(); for (auto const& i: memdb) +#ifdef __clang__ if (mem.find(i.first) == mem.end()) mem.insert(make_pair(i.first, RLP(i.second).toInt())); else mem.at(i.first) = RLP(i.second).toInt(); +#else + mem[i.first] = RLP(i.second).toInt(); +#endif } } @@ -266,11 +241,18 @@ map State::addresses() const void State::resetCurrent() { m_transactions.clear(); + m_transactionSet.clear(); m_cache.clear(); m_currentBlock = BlockInfo(); m_currentBlock.coinbaseAddress = m_ourAddress; m_currentBlock.stateRoot = m_previousBlock.stateRoot; m_currentBlock.parentHash = m_previousBlock.hash; + m_currentBlock.sha3Transactions = h256(); + m_currentBlock.sha3Uncles = h256(); + + // Update timestamp according to clock. + m_currentBlock.timestamp = time(0); + m_state.setRoot(m_currentBlock.stateRoot); } @@ -279,7 +261,9 @@ bool State::cull(TransactionQueue& _tq) const bool ret = false; auto ts = _tq.transactions(); for (auto const& i: ts) - if (!m_transactions.count(i.first)) + { + if (!m_transactionSet.count(i.first)) + { try { Transaction t(i.second); @@ -294,6 +278,8 @@ bool State::cull(TransactionQueue& _tq) const _tq.drop(i.first); ret = true; } + } + } return ret; } @@ -303,7 +289,9 @@ bool State::sync(TransactionQueue& _tq) bool ret = false; auto ts = _tq.transactions(); for (auto const& i: ts) - if (!m_transactions.count(i.first)) + { + if (!m_transactionSet.count(i.first)) + { // don't have it yet! Execute it now. try { @@ -325,6 +313,8 @@ bool State::sync(TransactionQueue& _tq) _tq.drop(i.first); ret = true; } + } + } return ret; } @@ -356,6 +346,9 @@ u256 State::playback(bytesConstRef _block, BlockInfo const& _grandParent, bool _ if (m_currentBlock.parentHash != m_previousBlock.hash) throw InvalidParentHash(); +// cnote << "playback begins:" << m_state.root(); +// cnote << m_state; + // All ok with the block generally. Play back the transactions now... for (auto const& i: RLP(_block)[1]) execute(i.data()); @@ -385,9 +378,10 @@ u256 State::playback(bytesConstRef _block, BlockInfo const& _grandParent, bool _ { cwarn << "Bad state root!"; cnote << "Given to be:" << m_currentBlock.stateRoot; + cnote << TrieDB(&m_db, m_currentBlock.stateRoot); cnote << "Calculated to be:" << rootHash(); cnote << m_state; - cnote << TrieDB(&m_db, m_currentBlock.stateRoot); + cnote << *this; // Rollback the trie. m_db.rollback(); throw InvalidStateRoot(); @@ -414,14 +408,15 @@ u256 State::playback(bytesConstRef _block, BlockInfo const& _grandParent, bool _ // (i.e. all the transactions we executed). void State::commitToMine(BlockChain const& _bc) { - if (m_previousBlock.hash != m_committedPreviousHash) + if (m_currentBlock.sha3Transactions != h256() || m_currentBlock.sha3Uncles != h256()) { - m_committedPreviousHash = m_previousBlock.hash; - cnote << "Commiting to mine on" << m_previousBlock.hash; + Addresses uncleAddresses; + for (auto i: RLP(m_currentUncles)) + uncleAddresses.push_back(i[2].toHash
()); + unapplyRewards(uncleAddresses); } - if (m_currentBlock.sha3Transactions != h256() || m_currentBlock.sha3Uncles != h256()) - return; + cnote << "Commiting to mine on" << m_previousBlock.hash; RLPStream uncles; Addresses uncleAddresses; @@ -448,7 +443,7 @@ void State::commitToMine(BlockChain const& _bc) RLPStream txs(m_transactions.size()); for (auto const& i: m_transactions) - i.second.fillStream(txs); + i.fillStream(txs); txs.swapOut(m_currentTxs); uncles.swapOut(m_currentUncles); @@ -459,15 +454,16 @@ void State::commitToMine(BlockChain const& _bc) // Commit any and all changes to the trie that are in the cache, then update the state root accordingly. commit(); + cnote << "stateRoot:" << m_state.root(); +// cnote << m_state; +// cnote << *this; + m_currentBlock.stateRoot = m_state.root(); m_currentBlock.parentHash = m_previousBlock.hash; } MineInfo State::mine(uint _msTimeout) { - // Update timestamp according to clock. - m_currentBlock.timestamp = time(0); - // Update difficulty according to timestamp. m_currentBlock.difficulty = m_currentBlock.calculateDifficulty(m_previousBlock); @@ -577,8 +573,17 @@ u256 State::contractMemory(Address _id, u256 _memory) const return mit->second; } // Memory not cached - just grab one item from the DB rather than cache the lot. - TrieDB memdb(const_cast(&m_db), it->second.oldRoot()); // promise we won't change the overlay! :) - return RLP(memdb.at(_memory)).toInt(); // TODO: CHECK: check if this is actually an RLP decode + TrieDB memdb(const_cast(&m_db), it->second.oldRoot()); // promise we won't change the overlay! :) + string ret = memdb.at(_memory); + return ret.size() ? RLP(ret).toInt() : 0; +} + +map const& State::contractMemory(Address _contract) const +{ + if (!isContractAddress(_contract)) + return EmptyMapU256U256; + ensureCached(_contract, true, true); + return m_cache[_contract].memory(); } void State::execute(bytesConstRef _rlp) @@ -591,7 +596,8 @@ void State::execute(bytesConstRef _rlp) // NOTE: Here, contract-originated transactions will not get added to the transaction list. // If this is wrong, move this line into execute(Transaction const& _t, Address _sender) and // don't forget to allow unsigned transactions in the tx list if they concur with the script execution. - m_transactions.insert(make_pair(t.sha3(), t)); + m_transactions.push_back(t); + m_transactionSet.insert(t.sha3()); } void State::applyRewards(Addresses const& _uncleAddresses) @@ -599,579 +605,125 @@ void State::applyRewards(Addresses const& _uncleAddresses) u256 r = m_blockReward; for (auto const& i: _uncleAddresses) { - addBalance(i, m_blockReward * 4 / 3); + addBalance(i, m_blockReward * 3 / 4); r += m_blockReward / 8; } addBalance(m_currentBlock.coinbaseAddress, r); } +void State::unapplyRewards(Addresses const& _uncleAddresses) +{ + u256 r = m_blockReward; + for (auto const& i: _uncleAddresses) + { + subBalance(i, m_blockReward * 3 / 4); + r += m_blockReward / 8; + } + subBalance(m_currentBlock.coinbaseAddress, r); +} + void State::executeBare(Transaction const& _t, Address _sender) { +#if ETH_DEBUG + commit(); + clog(StateChat) << "State:" << rootHash(); + clog(StateChat) << "Executing TX:" << _t; +#endif + // Entry point for a contract-originated transaction. // Ignore invalid transactions. auto nonceReq = transactionsFrom(_sender); if (_t.nonce != nonceReq) + { + clog(StateChat) << "Invalid Nonce."; throw InvalidNonce(nonceReq, _t.nonce); + } + + unsigned nonZeroData = 0; + for (auto i: _t.data) + if (i) + nonZeroData++; + u256 fee = _t.receiveAddress ? m_fees.m_txFee : (nonZeroData * m_fees.m_memoryFee + m_fees.m_newContractFee); // Not considered invalid - just pointless. - u256 fee = _t.receiveAddress ? m_fees.m_txFee : (_t.data.size() * m_fees.m_memoryFee + m_fees.m_newContractFee); if (balance(_sender) < _t.value + fee) + { + clog(StateChat) << "Not enough cash."; throw NotEnoughCash(); - - // Increment associated nonce for sender. - noteSending(_sender); + } if (_t.receiveAddress) { + // Increment associated nonce for sender. + noteSending(_sender); + + // Pay... subBalance(_sender, _t.value + fee); addBalance(_t.receiveAddress, _t.value); if (isContractAddress(_t.receiveAddress)) { - MinerFeeAdder feeAdder({this, 0}); // will add fee on destruction. - execute(_t.receiveAddress, _sender, _t.value, _t.data, &feeAdder.fee); + // Once we get here, there's no going back. + try + { + MinerFeeAdder feeAdder({this, 0}); // will add fee on destruction. + execute(_t.receiveAddress, _sender, _t.value, _t.data, &feeAdder.fee); + } + catch (VMException const& _e) + { + clog(StateChat) << "VM Exception: " << _e.description(); + } + catch (Exception const& _e) + { + clog(StateChat) << "Exception in VM: " << _e.description(); + } + catch (std::exception const& _e) + { + clog(StateChat) << "std::exception in VM: " << _e.what(); + } } } else { -#if ETH_SENDER_PAYS_SETUP - if (balance(_sender) < _t.value + fee) -#else - if (_t.value < fee) -#endif - throw NotEnoughCash(); - - Address newAddress = low160(_t.sha3()); + Address newAddress = right160(_t.sha3()); if (isContractAddress(newAddress) || isNormalAddress(newAddress)) + { + clog(StateChat) << "Contract address collision."; throw ContractAddressCollision(); + } - // All OK - set it up. - m_cache[newAddress] = AddressState(0, 0, sha3(RLPNull)); - auto& mem = m_cache[newAddress].takeMemory(); + // Increment associated nonce for sender. + noteSending(_sender); + + // Pay out of sender... + subBalance(_sender, _t.value + fee); + + // Set up new account... + m_cache[newAddress] = AddressState(_t.value, 0, AddressType::Contract); + auto& mem = m_cache[newAddress].memory(); for (uint i = 0; i < _t.data.size(); ++i) +#ifdef __clang__ if (mem.find(i) == mem.end()) mem.insert(make_pair(i, _t.data[i])); else mem.at(i) = _t.data[i]; - -#if ETH_SENDER_PAYS_SETUP - subBalance(_sender, _t.value + fee); - addBalance(newAddress, _t.value); #else - subBalance(_sender, _t.value); - addBalance(newAddress, _t.value - fee); + mem[i] = _t.data[i]; #endif } -} -// Convert from a 256-bit integer stack/memory entry into a 160-bit Address hash. -// Currently we just pull out the left (high-order in BE) 160-bits. -// TODO: CHECK: check that this is correct. -inline Address asAddress(u256 _item) -{ - return left160(h256(_item)); +#if ETH_DEBUG + commit(); + clog(StateChat) << "New state:" << rootHash(); +#endif } void State::execute(Address _myAddress, Address _txSender, u256 _txValue, u256s const& _txData, u256* _totalFee) { - std::vector stack; - - // Set up some local functions. - auto require = [&](u256 _n) - { - if (stack.size() < _n) - throw StackTooSmall(_n, stack.size()); - }; - ensureCached(_myAddress, true, true); - auto& myMemory = m_cache[_myAddress].takeMemory(); - - auto mem = [&](u256 _n) -> u256 - { - auto i = myMemory.find(_n); - return i == myMemory.end() ? 0 : i->second; - }; - auto setMem = [&](u256 _n, u256 _v) - { - if (_v) - { - auto it = myMemory.find(_n); - if (it == myMemory.end()) - myMemory.insert(make_pair(_n, _v)); - else - myMemory.at(_n) = _v; - } - else - myMemory.erase(_n); - }; - - u256 curPC = 0; - u256 nextPC = 1; - u256 stepCount = 0; - for (bool stopped = false; !stopped; curPC = nextPC, nextPC = curPC + 1) - { - stepCount++; - - bigint minerFee = stepCount > 16 ? m_fees.m_stepFee : 0; - bigint voidFee = 0; - - auto rawInst = mem(curPC); - if (rawInst > 0xff) - throw BadInstruction(); - Instruction inst = (Instruction)(uint8_t)rawInst; - - switch (inst) - { - case Instruction::STORE: - require(2); - if (!mem(stack.back()) && stack[stack.size() - 2]) - voidFee += m_fees.m_memoryFee; - if (mem(stack.back()) && !stack[stack.size() - 2]) - voidFee -= m_fees.m_memoryFee; - // continue on to... - case Instruction::LOAD: - minerFee += m_fees.m_dataFee; - break; - - case Instruction::EXTRO: - case Instruction::BALANCE: - minerFee += m_fees.m_extroFee; - break; - - case Instruction::MKTX: - minerFee += m_fees.m_txFee; - break; - - case Instruction::SHA256: - case Instruction::RIPEMD160: - case Instruction::ECMUL: - case Instruction::ECADD: - case Instruction::ECSIGN: - case Instruction::ECRECOVER: - case Instruction::ECVALID: - minerFee += m_fees.m_cryptoFee; - break; - default: - break; - } - - if (minerFee + voidFee > balance(_myAddress)) - throw NotEnoughCash(); - subBalance(_myAddress, minerFee + voidFee); - *_totalFee += (u256)minerFee; - - switch (inst) - { - case Instruction::ADD: - //pops two items and pushes S[-1] + S[-2] mod 2^256. - require(2); - stack[stack.size() - 2] += stack.back(); - stack.pop_back(); - break; - case Instruction::MUL: - //pops two items and pushes S[-1] * S[-2] mod 2^256. - require(2); - stack[stack.size() - 2] *= stack.back(); - stack.pop_back(); - break; - case Instruction::SUB: - require(2); - stack[stack.size() - 2] = stack.back() - stack[stack.size() - 2]; - stack.pop_back(); - break; - case Instruction::DIV: - require(2); - stack[stack.size() - 2] = stack.back() / stack[stack.size() - 2]; - stack.pop_back(); - break; - case Instruction::SDIV: - require(2); - (s256&)stack[stack.size() - 2] = (s256&)stack.back() / (s256&)stack[stack.size() - 2]; - stack.pop_back(); - break; - case Instruction::MOD: - require(2); - stack[stack.size() - 2] = stack.back() % stack[stack.size() - 2]; - stack.pop_back(); - break; - case Instruction::SMOD: - require(2); - (s256&)stack[stack.size() - 2] = (s256&)stack.back() % (s256&)stack[stack.size() - 2]; - stack.pop_back(); - break; - case Instruction::EXP: - { - // TODO: better implementation? - require(2); - auto n = stack.back(); - auto x = stack[stack.size() - 2]; - stack.pop_back(); - for (u256 i = 0; i < x; ++i) - n *= n; - stack.back() = n; - break; - } - case Instruction::NEG: - require(1); - stack.back() = ~(stack.back() - 1); - break; - case Instruction::LT: - require(2); - stack[stack.size() - 2] = stack.back() < stack[stack.size() - 2] ? 1 : 0; - stack.pop_back(); - break; - case Instruction::LE: - require(2); - stack[stack.size() - 2] = stack.back() <= stack[stack.size() - 2] ? 1 : 0; - stack.pop_back(); - break; - case Instruction::GT: - require(2); - stack[stack.size() - 2] = stack.back() > stack[stack.size() - 2] ? 1 : 0; - stack.pop_back(); - break; - case Instruction::GE: - require(2); - stack[stack.size() - 2] = stack.back() >= stack[stack.size() - 2] ? 1 : 0; - stack.pop_back(); - break; - case Instruction::EQ: - require(2); - stack[stack.size() - 2] = stack.back() == stack[stack.size() - 2] ? 1 : 0; - stack.pop_back(); - break; - case Instruction::NOT: - require(1); - stack.back() = stack.back() ? 0 : 1; - stack.pop_back(); - break; - case Instruction::MYADDRESS: - stack.push_back((u160)_myAddress); - break; - case Instruction::TXSENDER: - stack.push_back((u160)_txSender); - break; - case Instruction::TXVALUE: - stack.push_back(_txValue); - break; - case Instruction::TXDATAN: - stack.push_back(_txData.size()); - break; - case Instruction::TXDATA: - require(1); - stack.back() = stack.back() < _txData.size() ? _txData[(uint)stack.back()] : 0; - break; - case Instruction::BLK_PREVHASH: - stack.push_back(m_previousBlock.hash); - break; - case Instruction::BLK_COINBASE: - stack.push_back((u160)m_currentBlock.coinbaseAddress); - break; - case Instruction::BLK_TIMESTAMP: - stack.push_back(m_currentBlock.timestamp); - break; - case Instruction::BLK_NUMBER: - stack.push_back(m_currentNumber); - break; - case Instruction::BLK_DIFFICULTY: - stack.push_back(m_currentBlock.difficulty); - break; - case Instruction::SHA256: - { - uint s = (uint)min(stack.back(), (u256)(stack.size() - 1) * 32); - stack.pop_back(); - - CryptoPP::SHA256 digest; - uint i = 0; - for (; s; s = (s >= 32 ? s - 32 : 0), i += 32) - { - bytes b = toBigEndian(stack.back()); - digest.Update(b.data(), (int)min(32, s)); // b.size() == 32 - stack.pop_back(); - } - array final; - digest.TruncatedFinal(final.data(), 32); - stack.push_back(fromBigEndian(final)); - break; - } - case Instruction::RIPEMD160: - { - uint s = (uint)min(stack.back(), (u256)(stack.size() - 1) * 32); - stack.pop_back(); - - CryptoPP::RIPEMD160 digest; - uint i = 0; - for (; s; s = (s >= 32 ? s - 32 : 0), i += 32) - { - bytes b = toBigEndian(stack.back()); - digest.Update(b.data(), (int)min(32, s)); // b.size() == 32 - stack.pop_back(); - } - array final; - digest.TruncatedFinal(final.data(), 20); - // NOTE: this aligns to right of 256-bit container (low-order bytes). - // This won't work if they're treated as byte-arrays and thus left-aligned in a 256-bit container. - stack.push_back((u256)fromBigEndian(final)); - break; - } - case Instruction::ECMUL: - { - // ECMUL - pops three items. - // If (S[-2],S[-1]) are a valid point in secp256k1, including both coordinates being less than P, pushes (S[-1],S[-2]) * S[-3], using (0,0) as the point at infinity. - // Otherwise, pushes (0,0). - require(3); - - bytes pub(1, 4); - pub += toBigEndian(stack[stack.size() - 2]); - pub += toBigEndian(stack.back()); - stack.pop_back(); - stack.pop_back(); - - bytes x = toBigEndian(stack.back()); - stack.pop_back(); - - if (secp256k1_ecdsa_pubkey_verify(pub.data(), (int)pub.size())) // TODO: Check both are less than P. - { - secp256k1_ecdsa_pubkey_tweak_mul(pub.data(), (int)pub.size(), x.data()); - stack.push_back(fromBigEndian(bytesConstRef(&pub).cropped(1, 32))); - stack.push_back(fromBigEndian(bytesConstRef(&pub).cropped(33, 32))); - } - else - { - stack.push_back(0); - stack.push_back(0); - } - break; - } - case Instruction::ECADD: - { - // ECADD - pops four items and pushes (S[-4],S[-3]) + (S[-2],S[-1]) if both points are valid, otherwise (0,0). - require(4); - - bytes pub(1, 4); - pub += toBigEndian(stack[stack.size() - 2]); - pub += toBigEndian(stack.back()); - stack.pop_back(); - stack.pop_back(); - - bytes tweak(1, 4); - tweak += toBigEndian(stack[stack.size() - 2]); - tweak += toBigEndian(stack.back()); - stack.pop_back(); - stack.pop_back(); - - if (secp256k1_ecdsa_pubkey_verify(pub.data(),(int) pub.size()) && secp256k1_ecdsa_pubkey_verify(tweak.data(),(int) tweak.size())) - { - secp256k1_ecdsa_pubkey_tweak_add(pub.data(), (int)pub.size(), tweak.data()); - stack.push_back(fromBigEndian(bytesConstRef(&pub).cropped(1, 32))); - stack.push_back(fromBigEndian(bytesConstRef(&pub).cropped(33, 32))); - } - else - { - stack.push_back(0); - stack.push_back(0); - } - break; - } - case Instruction::ECSIGN: - { - require(2); - bytes sig(64); - int v = 0; - - u256 msg = stack.back(); - stack.pop_back(); - u256 priv = stack.back(); - stack.pop_back(); - bytes nonce = toBigEndian(Transaction::kFromMessage(msg, priv)); - - if (!secp256k1_ecdsa_sign_compact(toBigEndian(msg).data(), 64, sig.data(), toBigEndian(priv).data(), nonce.data(), &v)) - throw InvalidSignature(); - - stack.push_back(v + 27); - stack.push_back(fromBigEndian(bytesConstRef(&sig).cropped(0, 32))); - stack.push_back(fromBigEndian(bytesConstRef(&sig).cropped(32))); - break; - } - case Instruction::ECRECOVER: - { - require(4); - - bytes sig = toBigEndian(stack[stack.size() - 2]) + toBigEndian(stack.back()); - stack.pop_back(); - stack.pop_back(); - int v = (int)stack.back(); - stack.pop_back(); - bytes msg = toBigEndian(stack.back()); - stack.pop_back(); - - byte pubkey[65]; - int pubkeylen = 65; - if (secp256k1_ecdsa_recover_compact(msg.data(), (int)msg.size(), sig.data(), pubkey, &pubkeylen, 0, v - 27)) - { - stack.push_back(0); - stack.push_back(0); - } - else - { - stack.push_back(fromBigEndian(bytesConstRef(&pubkey[1], 32))); - stack.push_back(fromBigEndian(bytesConstRef(&pubkey[33], 32))); - } - break; - } - case Instruction::ECVALID: - { - require(2); - bytes pub(1, 4); - pub += toBigEndian(stack[stack.size() - 2]); - pub += toBigEndian(stack.back()); - stack.pop_back(); - stack.pop_back(); - - stack.back() = secp256k1_ecdsa_pubkey_verify(pub.data(), (int)pub.size()) ? 1 : 0; - break; - } - case Instruction::SHA3: - { - uint s = (uint)min(stack.back(), (u256)(stack.size() - 1) * 32); - stack.pop_back(); - - CryptoPP::SHA3_256 digest; - uint i = 0; - for (; s; s = (s >= 32 ? s - 32 : 0), i += 32) - { - bytes b = toBigEndian(stack.back()); - digest.Update(b.data(), (int)min(32, s)); // b.size() == 32 - stack.pop_back(); - } - array final; - digest.TruncatedFinal(final.data(), 32); - stack.push_back(fromBigEndian(final)); - break; - } - case Instruction::PUSH: - { - stack.push_back(mem(curPC + 1)); - nextPC = curPC + 2; - break; - } - case Instruction::POP: - require(1); - stack.pop_back(); - break; - case Instruction::DUP: - require(1); - stack.push_back(stack.back()); - break; - case Instruction::DUPN: - { - auto s = mem(curPC + 1); - if (s == 0 || s > stack.size()) - throw OperandOutOfRange(1, stack.size(), s); - stack.push_back(stack[stack.size() - (uint)s]); - nextPC = curPC + 2; - break; - } - case Instruction::SWAP: - { - require(2); - auto d = stack.back(); - stack.back() = stack[stack.size() - 2]; - stack[stack.size() - 2] = d; - break; - } - case Instruction::SWAPN: - { - require(1); - auto d = stack.back(); - auto s = mem(curPC + 1); - if (s == 0 || s > stack.size()) - throw OperandOutOfRange(1, stack.size(), s); - stack.back() = stack[stack.size() - (uint)s]; - stack[stack.size() - (uint)s] = d; - nextPC = curPC + 2; - break; - } - case Instruction::LOAD: - require(1); - stack.back() = mem(stack.back()); - break; - case Instruction::STORE: - require(2); - setMem(stack.back(), stack[stack.size() - 2]); - stack.pop_back(); - stack.pop_back(); - break; - case Instruction::JMP: - require(1); - nextPC = stack.back(); - stack.pop_back(); - break; - case Instruction::JMPI: - require(2); - if (stack.back()) - nextPC = stack[stack.size() - 2]; - stack.pop_back(); - stack.pop_back(); - break; - case Instruction::IND: - stack.push_back(curPC); - break; - case Instruction::EXTRO: - { - require(2); - auto memoryAddress = stack.back(); - stack.pop_back(); - Address contractAddress = asAddress(stack.back()); - stack.back() = contractMemory(contractAddress, memoryAddress); - break; - } - case Instruction::BALANCE: - { - require(1); - stack.back() = balance(asAddress(stack.back())); - break; - } - case Instruction::MKTX: - { - require(4); - - Transaction t; - t.receiveAddress = asAddress(stack.back()); - stack.pop_back(); - t.value = stack.back(); - stack.pop_back(); - - auto itemCount = stack.back(); - stack.pop_back(); - if (stack.size() < itemCount) - throw OperandOutOfRange(0, stack.size(), itemCount); - t.data.reserve((uint)itemCount); - for (auto i = 0; i < itemCount; ++i) - { - t.data.push_back(stack.back()); - stack.pop_back(); - } - - t.nonce = transactionsFrom(_myAddress); - executeBare(t, _myAddress); - - break; - } - case Instruction::SUICIDE: - { - require(1); - Address dest = asAddress(stack.back()); - u256 minusVoidFee = myMemory.size() * m_fees.m_memoryFee; - addBalance(dest, balance(_myAddress) + minusVoidFee); - m_cache[_myAddress].kill(); - // ...follow through to... - } - case Instruction::STOP: - return; - default: - throw BadInstruction(); - } - } + VM vm; + ExtVM evm(*this, _myAddress, _txSender, _txValue, _txData); + vm.go(evm); + *_totalFee = vm.runFee(); } diff --git a/libethereum/State.h b/libethereum/State.h index 7165b0847..e3670fe98 100644 --- a/libethereum/State.h +++ b/libethereum/State.h @@ -32,30 +32,23 @@ #include "AddressState.h" #include "Transaction.h" #include "TrieDB.h" +#include "FeeStructure.h" #include "Dagger.h" +#include "ExtVMFace.h" namespace eth { class BlockChain; -extern const u256 c_genesisDifficulty; +extern u256 c_genesisDifficulty; std::map const& genesisState(); -#define ETH_SENDER_PAYS_SETUP 1 +static const std::map EmptyMapU256U256; -struct FeeStructure -{ - /// The fee structure. Values yet to be agreed on... - void setMultiplier(u256 _x); ///< The current block multiplier. - u256 m_stepFee; - u256 m_dataFee; - u256 m_memoryFee; - u256 m_extroFee; - u256 m_cryptoFee; - u256 m_newContractFee; - u256 m_txFee; -}; +struct StateChat: public LogChannel { static const char* name() { return "=S="; } static const int verbosity = 4; }; + +class ExtVM; /** * @brief Model of the current state of the ledger. @@ -64,6 +57,9 @@ struct FeeStructure */ class State { + template friend class UnitTest; + friend class ExtVM; + public: /// Construct state object. State(Address _coinbaseAddress, Overlay const& _db); @@ -150,6 +146,10 @@ public: /// @returns 0 if no contract exists at that address. u256 contractMemory(Address _contract, u256 _memory) const; + /// Get the memory of a contract. + /// @returns std::map if no contract exists at that address. + std::map const& contractMemory(Address _contract) const; + /// Note that the given address is sending a transaction and thus increment the associated ticker. void noteSending(Address _id); @@ -161,10 +161,7 @@ public: h256 rootHash() const { return m_state.root(); } /// Get the list of pending transactions. - std::map const& pending() const { return m_transactions; } - - /// Finalise the block, applying the earned rewards. - void applyRewards(Addresses const& _uncleAddresses); + Transactions const& pending() const { return m_transactions; } /// Execute all transactions within a given block. /// @returns the additional total difficulty. @@ -214,9 +211,16 @@ private: /// Sets m_currentBlock to a clean state, (i.e. no change from m_previousBlock). void resetCurrent(); + /// Finalise the block, applying the earned rewards. + void applyRewards(Addresses const& _uncleAddresses); + + /// Unfinalise the block, unapplying the earned rewards. + void unapplyRewards(Addresses const& _uncleAddresses); + Overlay m_db; ///< Our overlay for the state tree. TrieDB m_state; ///< Our state tree, as an Overlay DB. - std::map m_transactions; ///< The current list of transactions that we've included in the state. + Transactions m_transactions; ///< The current list of transactions that we've included in the state. + std::set m_transactionSet; ///< The set of transaction hashes that we've included in the state. mutable std::map m_cache; ///< Our address cache. This stores the states of each address that has (or at least might have) been changed. @@ -224,7 +228,6 @@ private: BlockInfo m_currentBlock; ///< The current block's information. bytes m_currentBytes; ///< The current block. uint m_currentNumber; - h256 m_committedPreviousHash; ///< Hash of previous block that we are committing to mine. bytes m_currentTxs; bytes m_currentUncles; @@ -241,17 +244,98 @@ private: friend std::ostream& operator<<(std::ostream& _out, State const& _s); }; +class ExtVM: public ExtVMFace +{ +public: + ExtVM(State& _s, Address _myAddress, Address _txSender, u256 _txValue, u256s const& _txData): + ExtVMFace(_myAddress, _txSender, _txValue, _txData, _s.m_fees, _s.m_previousBlock, _s.m_currentBlock, _s.m_currentNumber), m_s(_s) + { + m_s.ensureCached(_myAddress, true, true); + m_store = &(m_s.m_cache[_myAddress].memory()); + } + + u256 store(u256 _n) + { + auto i = m_store->find(_n); + return i == m_store->end() ? 0 : i->second; + } + void setStore(u256 _n, u256 _v) + { + if (_v) + { +#ifdef __clang__ + auto it = m_store->find(_n); + if (it == m_store->end()) + m_store->insert(std::make_pair(_n, _v)); + else + m_store->at(_n) = _v; +#else + (*m_store)[_n] = _v; +#endif + } + else + m_store->erase(_n); + } + + void payFee(bigint _f) + { + if (_f > m_s.balance(myAddress)) + throw NotEnoughCash(); + m_s.subBalance(myAddress, _f); + } + + void mktx(Transaction& _t) + { + _t.nonce = m_s.transactionsFrom(myAddress); + m_s.executeBare(_t, myAddress); + } + u256 balance(Address _a) { return m_s.balance(_a); } + u256 txCount(Address _a) { return m_s.transactionsFrom(_a); } + u256 extro(Address _a, u256 _pos) { return m_s.contractMemory(_a, _pos); } + u256 extroPrice(Address _a) { return 0; } + void suicide(Address _a) + { + m_s.addBalance(_a, m_s.balance(myAddress) + m_store->size() * fees.m_memoryFee); + m_s.m_cache[myAddress].kill(); + } + +private: + State& m_s; + std::map* m_store; +}; + inline std::ostream& operator<<(std::ostream& _out, State const& _s) { _out << "--- " << _s.rootHash() << std::endl; std::set
d; - for (auto const& i: TrieDB(const_cast(&_s.m_db), _s.m_currentBlock.stateRoot)) + for (auto const& i: TrieDB(const_cast(&_s.m_db), _s.rootHash())) { auto it = _s.m_cache.find(i.first); if (it == _s.m_cache.end()) { RLP r(i.second); - _out << "[ " << (r.itemCount() == 3 ? "CONTRACT] " : " NORMAL] ") << i.first << ": " << std::dec << r[1].toInt() << "@" << r[0].toInt() << std::endl; + _out << "[ " << (r.itemCount() == 3 ? "CONTRACT] " : " NORMAL] ") << i.first << ": " << std::dec << r[1].toInt() << "@" << r[0].toInt(); + if (r.itemCount() == 3) + { + _out << " *" << r[2].toHash(); + TrieDB memdb(const_cast(&_s.m_db), r[2].toHash()); // promise we won't alter the overlay! :) + std::map mem; + for (auto const& j: memdb) + { + _out << std::endl << " [" << j.first << ":" << asHex(j.second) << "]"; +#ifdef __clang__ + auto mFinder = mem.find(j.first); + if (mFinder == mem.end()) + mem.insert(std::make_pair(j.first, RLP(j.second).toInt())); + else + mFinder->second = RLP(j.second).toInt(); +#else + mem[j.first] = RLP(j.second).toInt(); +#endif + } + _out << std::endl << mem; + } + _out << std::endl; } else d.insert(i.first); @@ -260,7 +344,37 @@ inline std::ostream& operator<<(std::ostream& _out, State const& _s) if (i.second.type() == AddressType::Dead) _out << "[XXX " << i.first << std::endl; else - _out << (d.count(i.first) ? "[ ! " : "[ * ") << (i.second.type() == AddressType::Contract ? "CONTRACT] " : " NORMAL] ") << i.first << ": " << std::dec << i.second.nonce() << "@" << i.second.balance() << std::endl; + { + _out << (d.count(i.first) ? "[ ! " : "[ * ") << (i.second.type() == AddressType::Contract ? "CONTRACT] " : " NORMAL] ") << i.first << ": " << std::dec << i.second.nonce() << "@" << i.second.balance(); + if (i.second.type() == AddressType::Contract) + { + if (i.second.haveMemory()) + { + _out << std::endl << i.second.memory(); + } + else + { + _out << " *" << i.second.oldRoot(); + TrieDB memdb(const_cast(&_s.m_db), i.second.oldRoot()); // promise we won't alter the overlay! :) + std::map mem; + for (auto const& j: memdb) + { + _out << std::endl << " [" << j.first << ":" << asHex(j.second) << "]"; +#ifdef __clang__ + auto mFinder = mem.find(j.first); + if (mFinder == mem.end()) + mem.insert(std::make_pair(j.first, RLP(j.second).toInt())); + else + mFinder->second = RLP(j.second).toInt(); +#else + mem[j.first] = RLP(j.second).toInt(); +#endif + } + _out << std::endl << mem; + } + } + _out << std::endl; + } return _out; } @@ -278,7 +392,7 @@ void commit(std::map const& _cache, DB& _db, TrieDB memdb(&_db); + TrieDB memdb(&_db); memdb.init(); for (auto const& j: i.second.memory()) if (j.second) diff --git a/libethereum/Transaction.cpp b/libethereum/Transaction.cpp index 893433cc7..cf8ff3140 100644 --- a/libethereum/Transaction.cpp +++ b/libethereum/Transaction.cpp @@ -40,6 +40,18 @@ Transaction::Transaction(bytesConstRef _rlpData) vrs = Signature{ rlp[4].toInt(), rlp[5].toInt(), rlp[6].toInt() }; } +Address Transaction::safeSender() const noexcept +{ + try + { + return sender(); + } + catch (...) + { + return Address(); + } +} + Address Transaction::sender() const { secp256k1_start(); diff --git a/libethereum/Transaction.h b/libethereum/Transaction.h index 4f769d8c4..aca8a125c 100644 --- a/libethereum/Transaction.h +++ b/libethereum/Transaction.h @@ -41,12 +41,16 @@ struct Transaction Transaction(bytesConstRef _rlp); Transaction(bytes const& _rlp): Transaction(&_rlp) {} + bool operator==(Transaction const& _c) const { return receiveAddress == _c.receiveAddress && value == _c.value && data == _c.data; } + bool operator!=(Transaction const& _c) const { return !operator==(_c); } + u256 nonce; Address receiveAddress; u256 value; u256s data; Signature vrs; + Address safeSender() const noexcept; Address sender() const; void sign(Secret _priv); @@ -63,11 +67,17 @@ using Transactions = std::vector; inline std::ostream& operator<<(std::ostream& _out, Transaction const& _t) { - _out << "{" << _t.receiveAddress << "/" << _t.nonce << "*" << _t.value; + _out << "{"; + if (_t.receiveAddress) + _out << _t.receiveAddress.abridged(); + else + _out << "[CREATE]"; + + _out << "/" << _t.nonce << "*" << _t.value; Address s; try { - _out << "<-" << _t.sender(); + _out << "<-" << _t.sender().abridged(); } catch (...) {} _out << "}"; diff --git a/libethereum/TrieCommon.cpp b/libethereum/TrieCommon.cpp index 7fee4af45..fedf3b095 100644 --- a/libethereum/TrieCommon.cpp +++ b/libethereum/TrieCommon.cpp @@ -110,14 +110,14 @@ std::string hexPrefixEncode(bytesConstRef _d1, uint _o1, bytesConstRef _d2, uint return ret; } -byte uniqueInUse(RLP const& _orig, byte _except) +byte uniqueInUse(RLP const& _orig, byte except) { byte used = 255; for (unsigned i = 0; i < 17; ++i) - if (i != _except && !_orig[i].isEmpty()) + if (i != except && !_orig[i].isEmpty()) { if (used == 255) - used = i; + used = (byte)i; else return 255; } diff --git a/libethereum/TrieCommon.h b/libethereum/TrieCommon.h index 4fdef1aee..99176ef8d 100644 --- a/libethereum/TrieCommon.h +++ b/libethereum/TrieCommon.h @@ -66,7 +66,7 @@ inline bool isLeaf(RLP const& _twoItem) { assert(_twoItem.isList() && _twoItem.itemCount() == 2); auto pl = _twoItem[0].payload(); - return (pl[0] & 0x20); + return (pl[0] & 0x20) != 0; } inline NibbleSlice keyOf(bytesConstRef _hpe) @@ -84,7 +84,7 @@ inline NibbleSlice keyOf(RLP const& _twoItem) return keyOf(_twoItem[0].payload()); } -byte uniqueInUse(RLP const& _orig, byte _except); +byte uniqueInUse(RLP const& _orig, byte except); std::string hexPrefixEncode(bytes const& _hexVector, bool _leaf = false, int _begin = 0, int _end = -1); std::string hexPrefixEncode(bytesConstRef _data, bool _leaf, int _beginNibble, int _endNibble, uint _offset); std::string hexPrefixEncode(bytesConstRef _d1, uint _o1, bytesConstRef _d2, uint _o2, bool _leaf); diff --git a/libethereum/TrieDB.h b/libethereum/TrieDB.h index 802162ec8..e2543548c 100644 --- a/libethereum/TrieDB.h +++ b/libethereum/TrieDB.h @@ -76,10 +76,10 @@ public: ldb::DB* db() const { return m_db.get(); } void setDB(ldb::DB* _db, bool _clearOverlay = true) { m_db = std::shared_ptr(_db); if (_clearOverlay) m_over.clear(); } - void commit() { for (auto const& i: m_over) m_db->Put(m_writeOptions, ldb::Slice((char const*)i.first.data(), i.first.size), ldb::Slice(i.second.data(), i.second.size())); m_over.clear(); m_refCount.clear(); } + void commit() { if (m_db) { for (auto const& i: m_over) m_db->Put(m_writeOptions, ldb::Slice((char const*)i.first.data(), i.first.size), ldb::Slice(i.second.data(), i.second.size())); m_over.clear(); m_refCount.clear(); } } void rollback() { m_over.clear(); m_refCount.clear(); } - std::string lookup(h256 _h) const { std::string ret = BasicMap::lookup(_h); if (ret.empty()) m_db->Get(m_readOptions, ldb::Slice((char const*)_h.data(), 32), &ret); return ret; } + std::string lookup(h256 _h) const { std::string ret = BasicMap::lookup(_h); if (ret.empty() && m_db) m_db->Get(m_readOptions, ldb::Slice((char const*)_h.data(), 32), &ret); return ret; } private: using BasicMap::clear; @@ -131,7 +131,7 @@ public: iterator(GenericTrieDB const* _db) { m_that = _db; - m_trail.push_back(Node{_db->node(_db->m_root), std::string(1, '\0'), 255}); // one null byte is the HPE for the empty key. + m_trail.push_back({_db->node(_db->m_root), std::string(1, '\0'), 255}); // one null byte is the HPE for the empty key. next(); } @@ -242,10 +242,13 @@ public: else { // lead-on to another node - enter child. - m_trail.push_back(m_trail.back()); - m_trail.back().key = hexPrefixEncode(keyOf(m_trail.back().key), NibbleSlice(bytesConstRef(&m_trail.back().child, 1), 1), false); - m_trail.back().rlp = m_that->deref(rlp[m_trail.back().child]); - m_trail.back().child = 255; + // fixed so that Node passed into push_back is constructed *before* m_trail is potentially resized (which invalidates back and rlp) + Node const& back = m_trail.back(); + m_trail.push_back(Node{ + m_that->deref(rlp[back.child]), + hexPrefixEncode(keyOf(back.key), NibbleSlice(bytesConstRef(&back.child, 1), 1), false), + 255 + }); break; } } @@ -727,10 +730,10 @@ template bytes GenericTrieDB::cleve(RLP const& _orig, uint _s) assert(_s && _s <= k.size()); RLPStream bottom(2); - bottom << hexPrefixEncode(k, isLeaf(_orig), _s) << _orig[1]; + bottom << hexPrefixEncode(k, isLeaf(_orig), /*ugh*/(int)_s) << _orig[1]; RLPStream top(2); - top << hexPrefixEncode(k, false, 0, _s); + top << hexPrefixEncode(k, false, 0, /*ugh*/(int)_s); streamNode(top, bottom.out()); return top.out(); diff --git a/libethereum/UPnP.cpp b/libethereum/UPnP.cpp index 9f6ed4e95..6008bc7de 100644 --- a/libethereum/UPnP.cpp +++ b/libethereum/UPnP.cpp @@ -33,8 +33,8 @@ using namespace eth; UPnP::UPnP() { - m_urls = new UPNPUrls; - m_data = new IGDdatas; + m_urls.reset(new UPNPUrls); + m_data.reset(new IGDdatas); m_ok = false; @@ -43,8 +43,8 @@ UPnP::UPnP() char* descXML; int descXMLsize = 0; int upnperror = 0; - memset(m_urls, 0, sizeof(struct UPNPUrls)); - memset(m_data, 0, sizeof(struct IGDdatas)); + memset(m_urls.get(), 0, sizeof(struct UPNPUrls)); + memset(m_data.get(), 0, sizeof(struct IGDdatas)); devlist = upnpDiscover(2000, NULL/*multicast interface*/, NULL/*minissdpd socket path*/, 0/*sameport*/, 0/*ipv6*/, &upnperror); if (devlist) { @@ -66,12 +66,12 @@ UPnP::UPnP() #endif if (descXML) { - parserootdesc (descXML, descXMLsize, m_data); + parserootdesc (descXML, descXMLsize, m_data.get()); free (descXML); descXML = 0; #if MINIUPNPC_API_VERSION >= 9 - GetUPNPUrls (m_urls, m_data, dev->descURL, 0); + GetUPNPUrls (m_urls.get(), m_data.get(), dev->descURL, 0); #else - GetUPNPUrls (m_urls, m_data, dev->descURL); + GetUPNPUrls (m_urls.get(), m_data.get(), dev->descURL); #endif m_ok = true; } diff --git a/libethereum/UPnP.h b/libethereum/UPnP.h index bb51c96d1..8713e1d2b 100644 --- a/libethereum/UPnP.h +++ b/libethereum/UPnP.h @@ -45,8 +45,8 @@ public: std::set m_reg; bool m_ok; - struct UPNPUrls* m_urls; - struct IGDdatas* m_data; + std::unique_ptr m_urls; + std::unique_ptr m_data; }; } diff --git a/libethereum/VM.cpp b/libethereum/VM.cpp new file mode 100644 index 000000000..727559751 --- /dev/null +++ b/libethereum/VM.cpp @@ -0,0 +1,60 @@ +/* + 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 . +*/ +/** @file VM.cpp + * @author Gav Wood + * @date 2014 + */ + +#include "VM.h" + +#include +#include +#if WIN32 +#pragma warning(push) +#pragma warning(disable:4244) +#else +#pragma GCC diagnostic ignored "-Wunused-function" +#endif +#include +#include +#include +#if WIN32 +#pragma warning(pop) +#else +#endif +#include +#include +#include "BlockChain.h" +#include "Instruction.h" +#include "Exceptions.h" +#include "Dagger.h" +#include "Defaults.h" +using namespace std; +using namespace eth; + +VM::VM() +{ + reset(); +} + +void VM::reset() +{ + m_curPC = 0; + m_nextPC = 1; + m_stepCount = 0; + m_runFee = 0; +} diff --git a/libethereum/VM.h b/libethereum/VM.h new file mode 100644 index 000000000..caafc64b0 --- /dev/null +++ b/libethereum/VM.h @@ -0,0 +1,594 @@ +/* + 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 . +*/ +/** @file VM.h + * @author Gav Wood + * @date 2014 + */ + +#pragma once + +#include +#include +#if WIN32 +#pragma warning(push) +#pragma warning(disable:4244) +#else +#pragma GCC diagnostic ignored "-Wunused-function" +#endif +#include +#include +#include +#if WIN32 +#pragma warning(pop) +#else +#endif +#include "Common.h" +#include "Exceptions.h" +#include "FeeStructure.h" +#include "Instruction.h" +#include "BlockInfo.h" +#include "ExtVMFace.h" + +namespace eth +{ + +// Convert from a 256-bit integer stack/memory entry into a 160-bit Address hash. +// Currently we just pull out the right (low-order in BE) 160-bits. +inline Address asAddress(u256 _item) +{ + return right160(h256(_item)); +} + +inline u256 fromAddress(Address _a) +{ + return (u160)_a; +} + +/** + */ +class VM +{ + template friend class UnitTest; + +public: + /// Construct VM object. + VM(); + + void reset(); + + template + void go(Ext& _ext, uint64_t _steps = (uint64_t)-1); + + void require(u256 _n) { if (m_stack.size() < _n) throw StackTooSmall(_n, m_stack.size()); } + u256 runFee() const { return m_runFee; } + +private: + u256 m_curPC = 0; + u256 m_nextPC = 1; + uint64_t m_stepCount = 0; + std::map m_temp; + std::vector m_stack; + u256 m_runFee = 0; +}; + +} + +// INLINE: +template void eth::VM::go(Ext& _ext, uint64_t _steps) +{ + for (bool stopped = false; !stopped && _steps--; m_curPC = m_nextPC, m_nextPC = m_curPC + 1) + { + m_stepCount++; + + // INSTRUCTION... + auto rawInst = _ext.store(m_curPC); + if (rawInst > 0xff) + throw BadInstruction(); + Instruction inst = (Instruction)(uint8_t)rawInst; + + // FEES... + bigint runFee = m_stepCount > 16 ? _ext.fees.m_stepFee : 0; + bigint storeCostDelta = 0; + switch (inst) + { + case Instruction::SSTORE: + require(2); + if (!_ext.store(m_stack.back()) && m_stack[m_stack.size() - 2]) + storeCostDelta += _ext.fees.m_memoryFee; + if (_ext.store(m_stack.back()) && !m_stack[m_stack.size() - 2]) + storeCostDelta -= _ext.fees.m_memoryFee; + // continue on to... + case Instruction::SLOAD: + runFee += _ext.fees.m_dataFee; + break; + + case Instruction::EXTRO: + case Instruction::BALANCE: + runFee += _ext.fees.m_extroFee; + break; + + case Instruction::MKTX: + runFee += _ext.fees.m_txFee; + break; + + case Instruction::SHA256: + case Instruction::RIPEMD160: + case Instruction::ECMUL: + case Instruction::ECADD: + case Instruction::ECSIGN: + case Instruction::ECRECOVER: + case Instruction::ECVALID: + runFee += _ext.fees.m_cryptoFee; + break; + default: + break; + } + _ext.payFee(runFee + storeCostDelta); + m_runFee += (u256)runFee; + + // EXECUTE... + switch (inst) + { + case Instruction::ADD: + //pops two items and pushes S[-1] + S[-2] mod 2^256. + require(2); + m_stack[m_stack.size() - 2] += m_stack.back(); + m_stack.pop_back(); + break; + case Instruction::MUL: + //pops two items and pushes S[-1] * S[-2] mod 2^256. + require(2); + m_stack[m_stack.size() - 2] *= m_stack.back(); + m_stack.pop_back(); + break; + case Instruction::SUB: + require(2); + m_stack[m_stack.size() - 2] = m_stack.back() - m_stack[m_stack.size() - 2]; + m_stack.pop_back(); + break; + case Instruction::DIV: + require(2); + m_stack[m_stack.size() - 2] = m_stack.back() / m_stack[m_stack.size() - 2]; + m_stack.pop_back(); + break; + case Instruction::SDIV: + require(2); + (s256&)m_stack[m_stack.size() - 2] = (s256&)m_stack.back() / (s256&)m_stack[m_stack.size() - 2]; + m_stack.pop_back(); + break; + case Instruction::MOD: + require(2); + m_stack[m_stack.size() - 2] = m_stack.back() % m_stack[m_stack.size() - 2]; + m_stack.pop_back(); + break; + case Instruction::SMOD: + require(2); + (s256&)m_stack[m_stack.size() - 2] = (s256&)m_stack.back() % (s256&)m_stack[m_stack.size() - 2]; + m_stack.pop_back(); + break; + case Instruction::EXP: + { + // TODO: better implementation? + require(2); + auto n = m_stack.back(); + auto x = m_stack[m_stack.size() - 2]; + m_stack.pop_back(); + for (u256 i = 0; i < x; ++i) + n *= n; + m_stack.back() = n; + break; + } + case Instruction::NEG: + require(1); + m_stack.back() = ~(m_stack.back() - 1); + break; + case Instruction::LT: + require(2); + m_stack[m_stack.size() - 2] = m_stack.back() < m_stack[m_stack.size() - 2] ? 1 : 0; + m_stack.pop_back(); + break; + case Instruction::LE: + require(2); + m_stack[m_stack.size() - 2] = m_stack.back() <= m_stack[m_stack.size() - 2] ? 1 : 0; + m_stack.pop_back(); + break; + case Instruction::GT: + require(2); + m_stack[m_stack.size() - 2] = m_stack.back() > m_stack[m_stack.size() - 2] ? 1 : 0; + m_stack.pop_back(); + break; + case Instruction::GE: + require(2); + m_stack[m_stack.size() - 2] = m_stack.back() >= m_stack[m_stack.size() - 2] ? 1 : 0; + m_stack.pop_back(); + break; + case Instruction::EQ: + require(2); + m_stack[m_stack.size() - 2] = m_stack.back() == m_stack[m_stack.size() - 2] ? 1 : 0; + m_stack.pop_back(); + break; + case Instruction::NOT: + require(1); + m_stack.back() = m_stack.back() ? 0 : 1; + break; + case Instruction::MYADDRESS: + m_stack.push_back(fromAddress(_ext.myAddress)); + break; + case Instruction::TXSENDER: + m_stack.push_back(fromAddress(_ext.txSender)); + break; + case Instruction::TXVALUE: + m_stack.push_back(_ext.txValue); + break; + case Instruction::TXDATAN: + m_stack.push_back(_ext.txData.size()); + break; + case Instruction::TXDATA: + require(1); + m_stack.back() = m_stack.back() < _ext.txData.size() ? _ext.txData[(uint)m_stack.back()] : 0; + break; + case Instruction::BLK_PREVHASH: + m_stack.push_back(_ext.previousBlock.hash); + break; + case Instruction::BLK_COINBASE: + m_stack.push_back((u160)_ext.currentBlock.coinbaseAddress); + break; + case Instruction::BLK_TIMESTAMP: + m_stack.push_back(_ext.currentBlock.timestamp); + break; + case Instruction::BLK_NUMBER: + m_stack.push_back(_ext.currentNumber); + break; + case Instruction::BLK_DIFFICULTY: + m_stack.push_back(_ext.currentBlock.difficulty); + break; + case Instruction::BLK_NONCE: + m_stack.push_back(_ext.previousBlock.nonce); + break; + case Instruction::BASEFEE: + m_stack.push_back(_ext.fees.multiplier()); + break; + case Instruction::SHA256: + { + require(1); + uint s = (uint)std::min(m_stack.back(), (u256)(m_stack.size() - 1) * 32); + m_stack.pop_back(); + + CryptoPP::SHA256 digest; + uint i = 0; + for (; s; s = (s >= 32 ? s - 32 : 0), i += 32) + { + bytes b = toBigEndian(m_stack.back()); + digest.Update(b.data(), (int)std::min(32, s)); // b.size() == 32 + m_stack.pop_back(); + } + std::array final; + digest.TruncatedFinal(final.data(), 32); + m_stack.push_back(fromBigEndian(final)); + break; + } + case Instruction::RIPEMD160: + { + require(1); + uint s = (uint)std::min(m_stack.back(), (u256)(m_stack.size() - 1) * 32); + m_stack.pop_back(); + + CryptoPP::RIPEMD160 digest; + uint i = 0; + for (; s; s = (s >= 32 ? s - 32 : 0), i += 32) + { + bytes b = toBigEndian(m_stack.back()); + digest.Update(b.data(), (int)std::min(32, s)); // b.size() == 32 + m_stack.pop_back(); + } + std::array final; + digest.TruncatedFinal(final.data(), 20); + // NOTE: this aligns to right of 256-bit container (low-order bytes). + // This won't work if they're treated as byte-arrays and thus left-aligned in a 256-bit container. + m_stack.push_back((u256)fromBigEndian(final)); + break; + } + case Instruction::ECMUL: + { + // ECMUL - pops three items. + // If (S[-2],S[-1]) are a valid point in secp256k1, including both coordinates being less than P, pushes (S[-1],S[-2]) * S[-3], using (0,0) as the point at infinity. + // Otherwise, pushes (0,0). + require(3); + + bytes pub(1, 4); + pub += toBigEndian(m_stack[m_stack.size() - 2]); + pub += toBigEndian(m_stack.back()); + m_stack.pop_back(); + m_stack.pop_back(); + + bytes x = toBigEndian(m_stack.back()); + m_stack.pop_back(); + + if (secp256k1_ecdsa_pubkey_verify(pub.data(), (int)pub.size())) // TODO: Check both are less than P. + { + secp256k1_ecdsa_pubkey_tweak_mul(pub.data(), (int)pub.size(), x.data()); + m_stack.push_back(fromBigEndian(bytesConstRef(&pub).cropped(1, 32))); + m_stack.push_back(fromBigEndian(bytesConstRef(&pub).cropped(33, 32))); + } + else + { + m_stack.push_back(0); + m_stack.push_back(0); + } + break; + } + case Instruction::ECADD: + { + // ECADD - pops four items and pushes (S[-4],S[-3]) + (S[-2],S[-1]) if both points are valid, otherwise (0,0). + require(4); + + bytes pub(1, 4); + pub += toBigEndian(m_stack[m_stack.size() - 2]); + pub += toBigEndian(m_stack.back()); + m_stack.pop_back(); + m_stack.pop_back(); + + bytes tweak(1, 4); + tweak += toBigEndian(m_stack[m_stack.size() - 2]); + tweak += toBigEndian(m_stack.back()); + m_stack.pop_back(); + m_stack.pop_back(); + + if (secp256k1_ecdsa_pubkey_verify(pub.data(),(int) pub.size()) && secp256k1_ecdsa_pubkey_verify(tweak.data(),(int) tweak.size())) + { + secp256k1_ecdsa_pubkey_tweak_add(pub.data(), (int)pub.size(), tweak.data()); + m_stack.push_back(fromBigEndian(bytesConstRef(&pub).cropped(1, 32))); + m_stack.push_back(fromBigEndian(bytesConstRef(&pub).cropped(33, 32))); + } + else + { + m_stack.push_back(0); + m_stack.push_back(0); + } + break; + } + case Instruction::ECSIGN: + { + require(2); + bytes sig(64); + int v = 0; + + u256 msg = m_stack.back(); + m_stack.pop_back(); + u256 priv = m_stack.back(); + m_stack.pop_back(); + bytes nonce = toBigEndian(Transaction::kFromMessage(msg, priv)); + + if (!secp256k1_ecdsa_sign_compact(toBigEndian(msg).data(), 64, sig.data(), toBigEndian(priv).data(), nonce.data(), &v)) + throw InvalidSignature(); + + m_stack.push_back(v + 27); + m_stack.push_back(fromBigEndian(bytesConstRef(&sig).cropped(0, 32))); + m_stack.push_back(fromBigEndian(bytesConstRef(&sig).cropped(32))); + break; + } + case Instruction::ECRECOVER: + { + require(4); + + bytes sig = toBigEndian(m_stack[m_stack.size() - 2]) + toBigEndian(m_stack.back()); + m_stack.pop_back(); + m_stack.pop_back(); + int v = (int)m_stack.back(); + m_stack.pop_back(); + bytes msg = toBigEndian(m_stack.back()); + m_stack.pop_back(); + + byte pubkey[65]; + int pubkeylen = 65; + if (secp256k1_ecdsa_recover_compact(msg.data(), (int)msg.size(), sig.data(), pubkey, &pubkeylen, 0, v - 27)) + { + m_stack.push_back(0); + m_stack.push_back(0); + } + else + { + m_stack.push_back(fromBigEndian(bytesConstRef(&pubkey[1], 32))); + m_stack.push_back(fromBigEndian(bytesConstRef(&pubkey[33], 32))); + } + break; + } + case Instruction::ECVALID: + { + require(2); + bytes pub(1, 4); + pub += toBigEndian(m_stack[m_stack.size() - 2]); + pub += toBigEndian(m_stack.back()); + m_stack.pop_back(); + m_stack.pop_back(); + + m_stack.back() = secp256k1_ecdsa_pubkey_verify(pub.data(), (int)pub.size()) ? 1 : 0; + break; + } + case Instruction::SHA3: + { + require(1); + uint s = (uint)std::min(m_stack.back(), (u256)(m_stack.size() - 1) * 32); + m_stack.pop_back(); + + CryptoPP::SHA3_256 digest; + uint i = 0; + for (; s; s = (s >= 32 ? s - 32 : 0), i += 32) + { + bytes b = toBigEndian(m_stack.back()); + digest.Update(b.data(), (int)std::min(32, s)); // b.size() == 32 + m_stack.pop_back(); + } + std::array final; + digest.TruncatedFinal(final.data(), 32); + m_stack.push_back(fromBigEndian(final)); + break; + } + case Instruction::PUSH: + { + m_stack.push_back(_ext.store(m_curPC + 1)); + m_nextPC = m_curPC + 2; + break; + } + case Instruction::POP: + require(1); + m_stack.pop_back(); + break; + case Instruction::DUP: + require(1); + m_stack.push_back(m_stack.back()); + break; + /*case Instruction::DUPN: + { + auto s = store(curPC + 1); + if (s == 0 || s > stack.size()) + throw OperandOutOfRange(1, stack.size(), s); + stack.push_back(stack[stack.size() - (uint)s]); + nextPC = curPC + 2; + break; + }*/ + case Instruction::SWAP: + { + require(2); + auto d = m_stack.back(); + m_stack.back() = m_stack[m_stack.size() - 2]; + m_stack[m_stack.size() - 2] = d; + break; + } + /*case Instruction::SWAPN: + { + require(1); + auto d = stack.back(); + auto s = store(curPC + 1); + if (s == 0 || s > stack.size()) + throw OperandOutOfRange(1, stack.size(), s); + stack.back() = stack[stack.size() - (uint)s]; + stack[stack.size() - (uint)s] = d; + nextPC = curPC + 2; + break; + }*/ + case Instruction::MLOAD: + { + require(1); +#ifdef __clang__ + auto mFinder = m_temp.find(m_stack.back()); + if (mFinder != m_temp.end()) + m_stack.back() = mFinder->second; + else + m_stack.back() = 0; +#else + m_stack.back() = m_temp[m_stack.back()]; +#endif + break; + } + case Instruction::MSTORE: + { + require(2); +#ifdef __clang__ + auto mFinder = m_temp.find(m_stack.back()); + if (mFinder == m_temp.end()) + m_temp.insert(std::make_pair(m_stack.back(), m_stack[m_stack.size() - 2])); + else + mFinder->second = m_stack[m_stack.size() - 2]; +#else + m_temp[m_stack.back()] = m_stack[m_stack.size() - 2]; +#endif + m_stack.pop_back(); + m_stack.pop_back(); + break; + } + case Instruction::SLOAD: + require(1); + m_stack.back() = _ext.store(m_stack.back()); + break; + case Instruction::SSTORE: + require(2); + _ext.setStore(m_stack.back(), m_stack[m_stack.size() - 2]); + m_stack.pop_back(); + m_stack.pop_back(); + break; + case Instruction::JMP: + require(1); + m_nextPC = m_stack.back(); + m_stack.pop_back(); + break; + case Instruction::JMPI: + require(2); + if (m_stack.back()) + m_nextPC = m_stack[m_stack.size() - 2]; + m_stack.pop_back(); + m_stack.pop_back(); + break; + case Instruction::IND: + m_stack.push_back(m_curPC); + break; + case Instruction::EXTRO: + { + require(2); + auto memoryAddress = m_stack.back(); + m_stack.pop_back(); + Address contractAddress = asAddress(m_stack.back()); + m_stack.back() = _ext.extro(contractAddress, memoryAddress); + break; + } + case Instruction::BALANCE: + { + require(1); + m_stack.back() = _ext.balance(asAddress(m_stack.back())); + break; + } + case Instruction::MKTX: + { + require(3); + + Transaction t; + t.receiveAddress = asAddress(m_stack.back()); + m_stack.pop_back(); + t.value = m_stack.back(); + m_stack.pop_back(); + + auto itemCount = m_stack.back(); + m_stack.pop_back(); + if (m_stack.size() < itemCount) + throw OperandOutOfRange(0, m_stack.size(), itemCount); + t.data.reserve((uint)itemCount); + for (auto i = 0; i < itemCount; ++i) + { + t.data.push_back(m_stack.back()); + m_stack.pop_back(); + } + + _ext.mktx(t); + break; + } + case Instruction::SUICIDE: + { + require(1); + Address dest = asAddress(m_stack.back()); + _ext.suicide(dest); + // ...follow through to... + } + case Instruction::STOP: + return; + default: + throw BadInstruction(); + } + } + if (_steps == (unsigned)-1) + throw StepsDone(); +} + diff --git a/release.sh b/release.sh index 64ad39b4c..8cf59acf5 100755 --- a/release.sh +++ b/release.sh @@ -1,7 +1,12 @@ #!/bin/bash dist="saucy" -version=$1 +version=$(grep "define ETH_VERSION" libethereum/Common.h | cut -d ' ' -f 3) +branch="$(git branch | grep \* | cut -c 3-)" + +if [[ ! "$1" == "" ]]; then + version=$1 +fi if [[ ! "$3" == "" ]]; then if [[ ! "$4" == "" ]]; then @@ -25,12 +30,9 @@ cd /tmp echo Checking out... git clone $opwd cd cpp-ethereum +git checkout "$branch" -if [ "$1" == "" ]; then - archdir="cpp-ethereum-$(date +%Y%m%d)" -else - archdir="cpp-ethereum-$version" -fi +archdir="cpp-ethereum-$version" archfile="$archdir.tar.bz2" echo Cleaning backup files... diff --git a/secp256k1/impl/num.h b/secp256k1/impl/num.h index fc6d05c3d..21e3390b5 100644 --- a/secp256k1/impl/num.h +++ b/secp256k1/impl/num.h @@ -11,6 +11,8 @@ #include "num_gmp.h" #elif defined(USE_NUM_OPENSSL) #include "num_openssl.h" +#elif defined(USE_NUM_BOOST) +#include "num_boost.h" #else #error "Please select num implementation" #endif diff --git a/secp256k1/impl/num_boost.h b/secp256k1/impl/num_boost.h new file mode 100644 index 000000000..b808e3214 --- /dev/null +++ b/secp256k1/impl/num_boost.h @@ -0,0 +1,212 @@ +// Copyright (c) 2014 Tim Hughes +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef _SECP256K1_NUM_REPR_IMPL_H_ +#define _SECP256K1_NUM_REPR_IMPL_H_ +#include +#include + +void static secp256k1_num_init(secp256k1_num_t *r) +{ + *r = 0; +} + +void static secp256k1_num_free(secp256k1_num_t*) +{ +} + +void static secp256k1_num_copy(secp256k1_num_t *r, const secp256k1_num_t *a) +{ + *r = *a; +} + +int static secp256k1_num_bits(const secp256k1_num_t *a) +{ + int numLimbs = a->backend().size(); + int ret = (numLimbs - 1) * a->backend().limb_bits; + for (auto x = a->backend().limbs()[numLimbs - 1]; x; x >>= 1, ++ret); + return ret; +} + +void static secp256k1_num_get_bin(unsigned char *r, unsigned int rlen, const secp256k1_num_t *a) +{ + for (auto n = abs(*a); n; n >>= 8) + { + assert(rlen > 0); // out of space? + r[--rlen] = n.convert_to(); + } + memset(r, 0, rlen); +} + +void static secp256k1_num_set_bin(secp256k1_num_t *r, const unsigned char *a, unsigned int alen) +{ + *r = 0; + for (unsigned int i = 0; i != alen; ++i) + { + *r <<= 8; + *r |= a[i]; + } +} + +void static secp256k1_num_set_int(secp256k1_num_t *r, int a) +{ + *r = a; +} + +void static secp256k1_num_mod(secp256k1_num_t *r, const secp256k1_num_t *m) +{ + *r %= *m; +} + +void static secp256k1_num_mod_inverse(secp256k1_num_t *r, const secp256k1_num_t *n, const secp256k1_num_t *m) +{ + // http://rosettacode.org/wiki/Modular_inverse + secp256k1_num_t a = *n; + secp256k1_num_t b = *m; + secp256k1_num_t x0 = 0; + secp256k1_num_t x1 = 1; + assert(*n > 0); + assert(*m > 0); + if (b != 1) + { + secp256k1_num_t q, t; + while (a > 1) + { + boost::multiprecision::divide_qr(a, b, q, t); + a = b; b = t; + + t = x1 - q * x0; + x1 = x0; x0 = t; + } + if (x1 < 0) + { + x1 += *m; + } + } + *r = x1; + + // check result + #ifdef _DEBUG + { + typedef boost::multiprecision::number> bignum; + bignum br = *r, bn = *n, bm = *m; + assert((((bn) * (br)) % bm) == 1); + } + #endif +} + +int static secp256k1_num_is_zero(const secp256k1_num_t *a) +{ + return a->is_zero(); +} + +int static secp256k1_num_is_odd(const secp256k1_num_t *a) +{ + return boost::multiprecision::bit_test(*a, 0); +} + +int static secp256k1_num_is_neg(const secp256k1_num_t *a) +{ + return a->backend().isneg(); +} + +int static secp256k1_num_cmp(const secp256k1_num_t *a, const secp256k1_num_t *b) +{ + return a->backend().compare_unsigned(b->backend()); +} + +void static secp256k1_num_add(secp256k1_num_t *r, const secp256k1_num_t *a, const secp256k1_num_t *b) +{ + *r = (*a) + (*b); +} + +void static secp256k1_num_sub(secp256k1_num_t *r, const secp256k1_num_t *a, const secp256k1_num_t *b) +{ + *r = (*a) - (*b); +} + +void static secp256k1_num_mul(secp256k1_num_t *r, const secp256k1_num_t *a, const secp256k1_num_t *b) +{ + *r = (*a) * (*b); +} + +void static secp256k1_num_div(secp256k1_num_t *r, const secp256k1_num_t *a, const secp256k1_num_t *b) +{ + *r = (*a) / (*b); +} + +void static secp256k1_num_mod_mul(secp256k1_num_t *r, const secp256k1_num_t *a, const secp256k1_num_t *b, const secp256k1_num_t *m) +{ + secp256k1_num_mul(r, a, b); + secp256k1_num_mod(r, m); +} + +int static secp256k1_num_shift(secp256k1_num_t *r, int bits) +{ + unsigned ret = r->convert_to() & ((1 << bits) - 1); + *r >>= bits; + return ret; +} + +int static secp256k1_num_get_bit(const secp256k1_num_t *a, int pos) +{ + return boost::multiprecision::bit_test(*a, pos); +} + +void static secp256k1_num_inc(secp256k1_num_t *r) +{ + ++*r; +} + +void static secp256k1_num_set_hex(secp256k1_num_t *r, const char *a, int alen) +{ + static const unsigned char cvt[256] = { + 0, 0, 0, 0, 0, 0, 0,0,0,0,0,0,0,0,0,0, + 0, 0, 0, 0, 0, 0, 0,0,0,0,0,0,0,0,0,0, + 0, 0, 0, 0, 0, 0, 0,0,0,0,0,0,0,0,0,0, + 0, 1, 2, 3, 4, 5, 6,7,8,9,0,0,0,0,0,0, + 0,10,11,12,13,14,15,0,0,0,0,0,0,0,0,0, + 0, 0, 0, 0, 0, 0, 0,0,0,0,0,0,0,0,0,0, + 0,10,11,12,13,14,15,0,0,0,0,0,0,0,0,0, + 0, 0, 0, 0, 0, 0, 0,0,0,0,0,0,0,0,0,0, + 0, 0, 0, 0, 0, 0, 0,0,0,0,0,0,0,0,0,0, + 0, 0, 0, 0, 0, 0, 0,0,0,0,0,0,0,0,0,0, + 0, 0, 0, 0, 0, 0, 0,0,0,0,0,0,0,0,0,0, + 0, 0, 0, 0, 0, 0, 0,0,0,0,0,0,0,0,0,0, + 0, 0, 0, 0, 0, 0, 0,0,0,0,0,0,0,0,0,0, + 0, 0, 0, 0, 0, 0, 0,0,0,0,0,0,0,0,0,0, + 0, 0, 0, 0, 0, 0, 0,0,0,0,0,0,0,0,0,0, + 0, 0, 0, 0, 0, 0, 0,0,0,0,0,0,0,0,0,0 + }; + *r = 0; + for (int i = 0; i != alen; ++i) + { + *r <<= 4; + *r |= cvt[a[i]]; + } +} + +void static secp256k1_num_get_hex(char *r, int rlen, const secp256k1_num_t *a) +{ + static const unsigned char cvt[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; + for (auto n = *a; n; n >>= 4) + { + assert(rlen > 0); // out of space? + r[--rlen] = cvt[n.convert_to() & 15]; + } + memset(r, '0', rlen); +} + +void static secp256k1_num_split(secp256k1_num_t *rl, secp256k1_num_t *rh, const secp256k1_num_t *a, int bits) +{ + *rl = *a & ((secp256k1_num_t(1) << bits) - 1); + *rh = *a >> bits; +} + +void static secp256k1_num_negate(secp256k1_num_t *r) +{ + r->backend().negate(); +} + +#endif diff --git a/secp256k1/num.h b/secp256k1/num.h index b2e7462bc..3fdd8f39d 100644 --- a/secp256k1/num.h +++ b/secp256k1/num.h @@ -9,6 +9,8 @@ #include "num_gmp.h" #elif defined(USE_NUM_OPENSSL) #include "num_openssl.h" +#elif defined(USE_NUM_BOOST) +#include "num_boost.h" #else #error "Please select num implementation" #endif diff --git a/secp256k1/num_boost.h b/secp256k1/num_boost.h new file mode 100644 index 000000000..2c9e49aee --- /dev/null +++ b/secp256k1/num_boost.h @@ -0,0 +1,12 @@ +// Copyright (c) 2013 Pieter Wuille +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef _SECP256K1_NUM_REPR_ +#define _SECP256K1_NUM_REPR_ + +#include + +typedef boost::multiprecision::number> secp256k1_num_t ; + +#endif diff --git a/secp256k1/secp256k1.c b/secp256k1/secp256k1.c index ed8bf2e95..b9b57d4a4 100644 --- a/secp256k1/secp256k1.c +++ b/secp256k1/secp256k1.c @@ -8,6 +8,10 @@ #include "impl/ecmult.h" #include "impl/ecdsa.h" +#ifdef __cplusplus +extern "C" { +#endif + void secp256k1_start(void) { secp256k1_fe_start(); secp256k1_ge_start(); @@ -267,3 +271,7 @@ int secp256k1_ecdsa_privkey_import(unsigned char *seckey, const unsigned char *p secp256k1_num_free(&key); return ret; } + +#ifdef __cplusplus +} +#endif diff --git a/secp256k1/tests.c b/secp256k1/tests.c new file mode 100644 index 000000000..26c894e2e --- /dev/null +++ b/secp256k1/tests.c @@ -0,0 +1,470 @@ +// Copyright (c) 2013 Pieter Wuille +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#if defined HAVE_CONFIG_H +#include "libsecp256k1-config.h" +#endif + +#include +#include + +#include "impl/num.h" +#include "impl/field.h" +#include "impl/group.h" +#include "impl/ecmult.h" +#include "impl/ecdsa.h" +#include "impl/util.h" + +#ifdef ENABLE_OPENSSL_TESTS +#include "openssl/bn.h" +#include "openssl/ec.h" +#include "openssl/ecdsa.h" +#include "openssl/obj_mac.h" +#endif + +static int count = 100; + +/***** NUM TESTS *****/ + +void random_num_negate(secp256k1_num_t *num) { + if (secp256k1_rand32() & 1) + secp256k1_num_negate(num); +} + +void random_num_order_test(secp256k1_num_t *num) { + do { + unsigned char b32[32]; + secp256k1_rand256_test(b32); + secp256k1_num_set_bin(num, b32, 32); + if (secp256k1_num_is_zero(num)) + continue; + if (secp256k1_num_cmp(num, &secp256k1_ge_consts->order) >= 0) + continue; + break; + } while(1); +} + +void random_num_order(secp256k1_num_t *num) { + do { + unsigned char b32[32]; + secp256k1_rand256(b32); + secp256k1_num_set_bin(num, b32, 32); + if (secp256k1_num_is_zero(num)) + continue; + if (secp256k1_num_cmp(num, &secp256k1_ge_consts->order) >= 0) + continue; + break; + } while(1); +} + +void test_num_copy_inc_cmp() { + secp256k1_num_t n1,n2; + secp256k1_num_init(&n1); + secp256k1_num_init(&n2); + random_num_order(&n1); + secp256k1_num_copy(&n2, &n1); + assert(secp256k1_num_cmp(&n1, &n2) == 0); + assert(secp256k1_num_cmp(&n2, &n1) == 0); + secp256k1_num_inc(&n2); + assert(secp256k1_num_cmp(&n1, &n2) != 0); + assert(secp256k1_num_cmp(&n2, &n1) != 0); + secp256k1_num_free(&n1); + secp256k1_num_free(&n2); +} + + +void test_num_get_set_hex() { + secp256k1_num_t n1,n2; + secp256k1_num_init(&n1); + secp256k1_num_init(&n2); + random_num_order_test(&n1); + char c[64]; + secp256k1_num_get_hex(c, 64, &n1); + secp256k1_num_set_hex(&n2, c, 64); + assert(secp256k1_num_cmp(&n1, &n2) == 0); + for (int i=0; i<64; i++) { + // check whether the lower 4 bits correspond to the last hex character + int low1 = secp256k1_num_shift(&n1, 4); + int lowh = c[63]; + int low2 = (lowh>>6)*9+(lowh-'0')&15; + assert(low1 == low2); + // shift bits off the hex representation, and compare + memmove(c+1, c, 63); + c[0] = '0'; + secp256k1_num_set_hex(&n2, c, 64); + assert(secp256k1_num_cmp(&n1, &n2) == 0); + } + secp256k1_num_free(&n2); + secp256k1_num_free(&n1); +} + +void test_num_get_set_bin() { + secp256k1_num_t n1,n2; + secp256k1_num_init(&n1); + secp256k1_num_init(&n2); + random_num_order_test(&n1); + unsigned char c[32]; + secp256k1_num_get_bin(c, 32, &n1); + secp256k1_num_set_bin(&n2, c, 32); + assert(secp256k1_num_cmp(&n1, &n2) == 0); + for (int i=0; i<32; i++) { + // check whether the lower 8 bits correspond to the last byte + int low1 = secp256k1_num_shift(&n1, 8); + int low2 = c[31]; + assert(low1 == low2); + // shift bits off the byte representation, and compare + memmove(c+1, c, 31); + c[0] = 0; + secp256k1_num_set_bin(&n2, c, 32); + assert(secp256k1_num_cmp(&n1, &n2) == 0); + } + secp256k1_num_free(&n2); + secp256k1_num_free(&n1); +} + +void run_num_int() { + secp256k1_num_t n1; + secp256k1_num_init(&n1); + for (int i=-255; i<256; i++) { + unsigned char c1[3] = {}; + c1[2] = abs(i); + unsigned char c2[3] = {0x11,0x22,0x33}; + secp256k1_num_set_int(&n1, i); + secp256k1_num_get_bin(c2, 3, &n1); + assert(memcmp(c1, c2, 3) == 0); + } + secp256k1_num_free(&n1); +} + +void test_num_negate() { + secp256k1_num_t n1; + secp256k1_num_t n2; + secp256k1_num_init(&n1); + secp256k1_num_init(&n2); + random_num_order_test(&n1); // n1 = R + random_num_negate(&n1); + secp256k1_num_copy(&n2, &n1); // n2 = R + secp256k1_num_sub(&n1, &n2, &n1); // n1 = n2-n1 = 0 + assert(secp256k1_num_is_zero(&n1)); + secp256k1_num_copy(&n1, &n2); // n1 = R + secp256k1_num_negate(&n1); // n1 = -R + assert(!secp256k1_num_is_zero(&n1)); + secp256k1_num_add(&n1, &n2, &n1); // n1 = n2+n1 = 0 + assert(secp256k1_num_is_zero(&n1)); + secp256k1_num_copy(&n1, &n2); // n1 = R + secp256k1_num_negate(&n1); // n1 = -R + assert(secp256k1_num_is_neg(&n1) != secp256k1_num_is_neg(&n2)); + secp256k1_num_negate(&n1); // n1 = R + assert(secp256k1_num_cmp(&n1, &n2) == 0); + assert(secp256k1_num_is_neg(&n1) == secp256k1_num_is_neg(&n2)); + secp256k1_num_free(&n2); + secp256k1_num_free(&n1); +} + +void test_num_add_sub() { + secp256k1_num_t n1; + secp256k1_num_t n2; + secp256k1_num_init(&n1); + secp256k1_num_init(&n2); + random_num_order_test(&n1); // n1 = R1 + random_num_negate(&n1); + random_num_order_test(&n2); // n2 = R2 + random_num_negate(&n2); + secp256k1_num_t n1p2, n2p1, n1m2, n2m1; + secp256k1_num_init(&n1p2); + secp256k1_num_init(&n2p1); + secp256k1_num_init(&n1m2); + secp256k1_num_init(&n2m1); + secp256k1_num_add(&n1p2, &n1, &n2); // n1p2 = R1 + R2 + secp256k1_num_add(&n2p1, &n2, &n1); // n2p1 = R2 + R1 + secp256k1_num_sub(&n1m2, &n1, &n2); // n1m2 = R1 - R2 + secp256k1_num_sub(&n2m1, &n2, &n1); // n2m1 = R2 - R1 + assert(secp256k1_num_cmp(&n1p2, &n2p1) == 0); + assert(secp256k1_num_cmp(&n1p2, &n1m2) != 0); + secp256k1_num_negate(&n2m1); // n2m1 = -R2 + R1 + assert(secp256k1_num_cmp(&n2m1, &n1m2) == 0); + assert(secp256k1_num_cmp(&n2m1, &n1) != 0); + secp256k1_num_add(&n2m1, &n2m1, &n2); // n2m1 = -R2 + R1 + R2 = R1 + assert(secp256k1_num_cmp(&n2m1, &n1) == 0); + assert(secp256k1_num_cmp(&n2p1, &n1) != 0); + secp256k1_num_sub(&n2p1, &n2p1, &n2); // n2p1 = R2 + R1 - R2 = R1 + assert(secp256k1_num_cmp(&n2p1, &n1) == 0); + secp256k1_num_free(&n2m1); + secp256k1_num_free(&n1m2); + secp256k1_num_free(&n2p1); + secp256k1_num_free(&n1p2); + secp256k1_num_free(&n2); + secp256k1_num_free(&n1); +} + +void run_num_smalltests() { + for (int i=0; i<100*count; i++) { + test_num_copy_inc_cmp(); + test_num_get_set_hex(); + test_num_get_set_bin(); + test_num_negate(); + test_num_add_sub(); + } + run_num_int(); +} + +void run_ecmult_chain() { + // random starting point A (on the curve) + secp256k1_fe_t ax; secp256k1_fe_set_hex(&ax, "8b30bbe9ae2a990696b22f670709dff3727fd8bc04d3362c6c7bf458e2846004", 64); + secp256k1_fe_t ay; secp256k1_fe_set_hex(&ay, "a357ae915c4a65281309edf20504740f0eb3343990216b4f81063cb65f2f7e0f", 64); + secp256k1_gej_t a; secp256k1_gej_set_xy(&a, &ax, &ay); + // two random initial factors xn and gn + secp256k1_num_t xn; + secp256k1_num_init(&xn); + secp256k1_num_set_hex(&xn, "84cc5452f7fde1edb4d38a8ce9b1b84ccef31f146e569be9705d357a42985407", 64); + secp256k1_num_t gn; + secp256k1_num_init(&gn); + secp256k1_num_set_hex(&gn, "a1e58d22553dcd42b23980625d4c57a96e9323d42b3152e5ca2c3990edc7c9de", 64); + // two small multipliers to be applied to xn and gn in every iteration: + secp256k1_num_t xf; + secp256k1_num_init(&xf); + secp256k1_num_set_hex(&xf, "1337", 4); + secp256k1_num_t gf; + secp256k1_num_init(&gf); + secp256k1_num_set_hex(&gf, "7113", 4); + // accumulators with the resulting coefficients to A and G + secp256k1_num_t ae; + secp256k1_num_init(&ae); + secp256k1_num_set_int(&ae, 1); + secp256k1_num_t ge; + secp256k1_num_init(&ge); + secp256k1_num_set_int(&ge, 0); + // the point being computed + secp256k1_gej_t x = a; + const secp256k1_num_t *order = &secp256k1_ge_consts->order; + for (int i=0; i<200*count; i++) { + // in each iteration, compute X = xn*X + gn*G; + secp256k1_ecmult(&x, &x, &xn, &gn); + // also compute ae and ge: the actual accumulated factors for A and G + // if X was (ae*A+ge*G), xn*X + gn*G results in (xn*ae*A + (xn*ge+gn)*G) + secp256k1_num_mod_mul(&ae, &ae, &xn, order); + secp256k1_num_mod_mul(&ge, &ge, &xn, order); + secp256k1_num_add(&ge, &ge, &gn); + secp256k1_num_mod(&ge, order); + // modify xn and gn + secp256k1_num_mod_mul(&xn, &xn, &xf, order); + secp256k1_num_mod_mul(&gn, &gn, &gf, order); + + // verify + if (i == 19999) { + char res[132]; int resl = 132; + secp256k1_gej_get_hex(res, &resl, &x); + assert(strcmp(res, "(D6E96687F9B10D092A6F35439D86CEBEA4535D0D409F53586440BD74B933E830,B95CBCA2C77DA786539BE8FD53354D2D3B4F566AE658045407ED6015EE1B2A88)") == 0); + } + } + // redo the computation, but directly with the resulting ae and ge coefficients: + secp256k1_gej_t x2; secp256k1_ecmult(&x2, &a, &ae, &ge); + char res[132]; int resl = 132; + char res2[132]; int resl2 = 132; + secp256k1_gej_get_hex(res, &resl, &x); + secp256k1_gej_get_hex(res2, &resl2, &x2); + assert(strcmp(res, res2) == 0); + assert(strlen(res) == 131); + secp256k1_num_free(&xn); + secp256k1_num_free(&gn); + secp256k1_num_free(&xf); + secp256k1_num_free(&gf); + secp256k1_num_free(&ae); + secp256k1_num_free(&ge); +} + +void test_point_times_order(const secp256k1_gej_t *point) { + // either the point is not on the curve, or multiplying it by the order results in O + if (!secp256k1_gej_is_valid(point)) + return; + + const secp256k1_num_t *order = &secp256k1_ge_consts->order; + secp256k1_num_t zero; + secp256k1_num_init(&zero); + secp256k1_num_set_int(&zero, 0); + secp256k1_gej_t res; + secp256k1_ecmult(&res, point, order, order); // calc res = order * point + order * G; + assert(secp256k1_gej_is_infinity(&res)); + secp256k1_num_free(&zero); +} + +void run_point_times_order() { + secp256k1_fe_t x; secp256k1_fe_set_hex(&x, "02", 2); + for (int i=0; i<500; i++) { + secp256k1_ge_t p; secp256k1_ge_set_xo(&p, &x, 1); + secp256k1_gej_t j; secp256k1_gej_set_ge(&j, &p); + test_point_times_order(&j); + secp256k1_fe_sqr(&x, &x); + } + char c[65]; int cl=65; + secp256k1_fe_get_hex(c, &cl, &x); + assert(strcmp(c, "7603CB59B0EF6C63FE6084792A0C378CDB3233A80F8A9A09A877DEAD31B38C45") == 0); +} + +void test_wnaf(const secp256k1_num_t *number, int w) { + secp256k1_num_t x, two, t; + secp256k1_num_init(&x); + secp256k1_num_init(&two); + secp256k1_num_init(&t); + secp256k1_num_set_int(&x, 0); + secp256k1_num_set_int(&two, 2); + int wnaf[257]; + int bits = secp256k1_ecmult_wnaf(wnaf, number, w); + int zeroes = -1; + for (int i=bits-1; i>=0; i--) { + secp256k1_num_mul(&x, &x, &two); + int v = wnaf[i]; + if (v) { + assert(zeroes == -1 || zeroes >= w-1); // check that distance between non-zero elements is at least w-1 + zeroes=0; + assert((v & 1) == 1); // check non-zero elements are odd + assert(v <= (1 << (w-1)) - 1); // check range below + assert(v >= -(1 << (w-1)) - 1); // check range above + } else { + assert(zeroes != -1); // check that no unnecessary zero padding exists + zeroes++; + } + secp256k1_num_set_int(&t, v); + secp256k1_num_add(&x, &x, &t); + } + assert(secp256k1_num_cmp(&x, number) == 0); // check that wnaf represents number + secp256k1_num_free(&x); + secp256k1_num_free(&two); + secp256k1_num_free(&t); +} + +void run_wnaf() { + secp256k1_num_t n; + secp256k1_num_init(&n); + for (int i=0; i 1) + count = strtol(argv[1], NULL, 0)*47; + + printf("test count = %i\n", count); + + // initialize + secp256k1_fe_start(); + secp256k1_ge_start(); + secp256k1_ecmult_start(); + + // num tests + run_num_smalltests(); + + // ecmult tests + run_wnaf(); + run_point_times_order(); + run_ecmult_chain(); + + // ecdsa tests + run_ecdsa_sign_verify(); +#ifdef ENABLE_OPENSSL_TESTS + run_ecdsa_openssl(); +#endif + + // shutdown + secp256k1_ecmult_stop(); + secp256k1_ge_stop(); + secp256k1_fe_stop(); + return 0; +} diff --git a/test/crypto.cpp b/test/crypto.cpp index d01ccee08..3d565dd73 100644 --- a/test/crypto.cpp +++ b/test/crypto.cpp @@ -87,7 +87,7 @@ int cryptoTest() ret = secp256k1_ecdsa_pubkey_create(pubkey.data(), &pubkeylen, privkey.data(), 1); pubkey.resize(pubkeylen); - int good = secp256k1_ecdsa_pubkey_verify(pubkey.data(), pubkey.size()); + int good = secp256k1_ecdsa_pubkey_verify(pubkey.data(), (int)pubkey.size()); cout << "PUB: " << dec << ret << " " << pubkeylen << " " << asHex(pubkey) << (good ? " GOOD" : " BAD") << endl; } @@ -99,12 +99,12 @@ int cryptoTest() cout << asHex(hmsg) << endl; cout << asHex(privkey) << endl; cout << hex << nonce << dec << endl; - int ret = secp256k1_ecdsa_sign_compact((byte const*)hmsg.data(), hmsg.size(), sig.data(), privkey.data(), (byte const*)&nonce, &v); + int ret = secp256k1_ecdsa_sign_compact((byte const*)hmsg.data(), (int)hmsg.size(), sig.data(), privkey.data(), (byte const*)&nonce, &v); cout << "MYSIG: " << dec << ret << " " << sig.size() << " " << asHex(sig) << " " << v << endl; bytes pubkey(65); int pubkeylen = 65; - ret = secp256k1_ecdsa_recover_compact((byte const*)hmsg.data(), hmsg.size(), (byte const*)sig.data(), pubkey.data(), &pubkeylen, 0, v); + ret = secp256k1_ecdsa_recover_compact((byte const*)hmsg.data(), (int)hmsg.size(), (byte const*)sig.data(), pubkey.data(), &pubkeylen, 0, v); pubkey.resize(pubkeylen); cout << "MYREC: " << dec << ret << " " << pubkeylen << " " << asHex(pubkey) << endl; } @@ -112,7 +112,7 @@ int cryptoTest() { bytes pubkey(65); int pubkeylen = 65; - int ret = secp256k1_ecdsa_recover_compact((byte const*)hmsg.data(), hmsg.size(), (byte const*)sig64.data(), pubkey.data(), &pubkeylen, 0, (int)t.vrs.v - 27); + int ret = secp256k1_ecdsa_recover_compact((byte const*)hmsg.data(), (int)hmsg.size(), (byte const*)sig64.data(), pubkey.data(), &pubkeylen, 0, (int)t.vrs.v - 27); pubkey.resize(pubkeylen); cout << "RECPUB: " << dec << ret << " " << pubkeylen << " " << asHex(pubkey) << endl; cout << "SENDER: " << hex << low160(eth::sha3(bytesConstRef(&pubkey).cropped(1))) << dec << endl; diff --git a/test/dagger.cpp b/test/dagger.cpp index ba2d143ac..cc22baa13 100644 --- a/test/dagger.cpp +++ b/test/dagger.cpp @@ -30,19 +30,17 @@ int daggerTest() { // Test dagger { - Dagger d; auto s = steady_clock::now(); - cout << hex << d.eval((h256)1, (h256)0); + cout << hex << Dagger().eval((h256)(u256)1, (h256)(u256)0); cout << " " << dec << duration_cast(steady_clock::now() - s).count() << " ms" << endl; - cout << hex << d.eval((h256)1, (h256)1); + cout << hex << Dagger().eval((h256)(u256)1, (h256)(u256)1); cout << " " << dec << duration_cast(steady_clock::now() - s).count() << " ms" << endl; } { - Dagger d; auto s = steady_clock::now(); - cout << hex << d.eval((h256)1, (h256)0); + cout << hex << Dagger().eval((h256)(u256)1, (h256)(u256)0); cout << " " << dec << duration_cast(steady_clock::now() - s).count() << " ms" << endl; - cout << hex << d.eval((h256)1, (h256)1); + cout << hex << Dagger().eval((h256)(u256)1, (h256)(u256)1); cout << " " << dec << duration_cast(steady_clock::now() - s).count() << " ms" << endl; } return 0; diff --git a/test/main.cpp b/test/main.cpp index 356ed70fc..ed0ea76e7 100644 --- a/test/main.cpp +++ b/test/main.cpp @@ -27,13 +27,14 @@ int rlpTest(); int daggerTest(); int cryptoTest(); int stateTest(); +int vmTest(); int hexPrefixTest(); int peerTest(int argc, char** argv); #include using namespace eth; -int main(int argc, char** argv) +int main(int, char**) { /* RLPStream s; BlockInfo::genesis().fillStream(s, false); @@ -46,6 +47,7 @@ int main(int argc, char** argv) trieTest(); daggerTest(); cryptoTest(); + vmTest(); // stateTest(); // peerTest(argc, argv); return 0; diff --git a/test/peer.cpp b/test/peer.cpp index 3f97b69da..21aa85004 100644 --- a/test/peer.cpp +++ b/test/peer.cpp @@ -22,6 +22,7 @@ #include #include +#include #include #include using namespace std; @@ -38,16 +39,16 @@ int peerTest(int argc, char** argv) { string arg = argv[i]; if (arg == "-l" && i + 1 < argc) - listenPort = atoi(argv[++i]); + listenPort = (short)atoi(argv[++i]); else if (arg == "-r" && i + 1 < argc) remoteHost = argv[++i]; else if (arg == "-p" && i + 1 < argc) - remotePort = atoi(argv[++i]); + remotePort = (short)atoi(argv[++i]); else remoteHost = argv[i]; } - BlockChain ch("/tmp"); + BlockChain ch(boost::filesystem::temp_directory_path().string()); PeerServer pn("Test", ch, 0, listenPort); if (!remoteHost.empty()) diff --git a/test/state.cpp b/test/state.cpp index 8cd5f9ff3..a3ea2aea2 100644 --- a/test/state.cpp +++ b/test/state.cpp @@ -20,6 +20,7 @@ * State test functions. */ +#include #include #include #include @@ -33,7 +34,7 @@ int stateTest() KeyPair myMiner = sha3("Gav's Miner"); // KeyPair you = sha3("123"); - Defaults::setDBPath("/tmp"); + Defaults::setDBPath(boost::filesystem::temp_directory_path().string()); Overlay stateDB = State::openDB(); BlockChain bc; diff --git a/test/trie.cpp b/test/trie.cpp index 85416c2c1..5cce3d0f4 100644 --- a/test/trie.cpp +++ b/test/trie.cpp @@ -27,6 +27,11 @@ using namespace std; using namespace eth; +inline h256 stringMapHash256(StringMap const& _s) +{ + return hash256(_s); +} + int trieTest() { { @@ -42,13 +47,13 @@ int trieTest() cout << t; cout << m; cout << t.root() << endl; - cout << hash256({{"test", "test"}}) << endl; + cout << stringMapHash256({{"test", "test"}}) << endl; t.insert(string("tesa"), string("testy")); cout << t; cout << m; cout << t.root() << endl; - cout << hash256({{"test", "test"}, {"te", "testy"}}) << endl; + cout << stringMapHash256({{"test", "test"}, {"te", "testy"}}) << endl; cout << t.at(string("test")) << endl; cout << t.at(string("te")) << endl; cout << t.at(string("t")) << endl; @@ -56,7 +61,7 @@ int trieTest() t.remove(string("te")); cout << m; cout << t.root() << endl; - cout << hash256({{"test", "test"}}) << endl; + cout << stringMapHash256({{"test", "test"}}) << endl; t.remove(string("test")); cout << m; @@ -72,7 +77,7 @@ int trieTest() cout << t; cout << m; cout << t.root() << endl; - cout << hash256({{"b", "B"}, {"a", "A"}}) << endl; + cout << stringMapHash256({{"b", "B"}, {"a", "A"}}) << endl; cout << RLP(rlp256({{"b", "B"}, {"a", "A"}})) << endl; } { @@ -89,7 +94,7 @@ int trieTest() cout << RLP(t.rlp()) << endl; } { - cout << hex << hash256({{"dog", "puppy"}, {"doe", "reindeer"}}) << endl; + cout << hex << stringMapHash256({{"dog", "puppy"}, {"doe", "reindeer"}}) << endl; MemTrie t; t.insert("dog", "puppy"); t.insert("doe", "reindeer"); diff --git a/test/vm.cpp b/test/vm.cpp new file mode 100644 index 000000000..137803a3e --- /dev/null +++ b/test/vm.cpp @@ -0,0 +1,451 @@ +/* + 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 . +*/ +/** @file state.cpp + * @author Gav Wood + * @date 2014 + * State test functions. + */ + +#include +#include "../json_spirit/json_spirit_reader_template.h" +#include "../json_spirit/json_spirit_writer_template.h" +#include +#include +#include +#include +using namespace std; +using namespace json_spirit; +using namespace eth; + +namespace eth +{ + +class FakeExtVM: public ExtVMFace +{ +public: + FakeExtVM() + {} + FakeExtVM(FeeStructure const& _fees, BlockInfo const& _previousBlock, BlockInfo const& _currentBlock, uint _currentNumber): + ExtVMFace(Address(), Address(), 0, u256s(), _fees, _previousBlock, _currentBlock, _currentNumber) + {} + + u256 store(u256 _n) + { +#ifdef __clang__ + tuple > & address = addresses[myAddress]; + map & third = get<3>(address); + auto sFinder = third.find(_n); + if (sFinder != third.end()) + return sFinder->second; + else + return 0; +#else + return get<3>(addresses[myAddress])[_n]; +#endif + } + void setStore(u256 _n, u256 _v) + { +#ifdef __clang__ + tuple > & address = addresses[myAddress]; + map & third = get<3>(address); + auto sFinder = third.find(_n); + if (sFinder != third.end()) + sFinder->second = _v; + else + third.insert(std::make_pair(_n, _v)); +#else + get<3>(addresses[myAddress])[_n] = _v; +#endif + } + void mktx(Transaction& _t) + { + if (get<0>(addresses[myAddress]) >= _t.value) + { + get<0>(addresses[myAddress]) -= _t.value; + get<1>(addresses[myAddress])++; +// get<0>(addresses[_t.receiveAddress]) += _t.value; + txs.push_back(_t); + } + } + u256 balance(Address _a) { return get<0>(addresses[_a]); } + void payFee(bigint _fee) { get<0>(addresses[myAddress]) = (u256)(get<0>(addresses[myAddress]) - _fee); } + u256 txCount(Address _a) { return get<1>(addresses[_a]); } + u256 extro(Address _a, u256 _pos) + { +#ifdef __clang__ + tuple > & address = addresses[_a]; + map & third = get<3>(address); + auto sFinder = third.find(_pos); + if (sFinder != third.end()) + return sFinder->second; + else + return 0; +#else + return get<3>(addresses[_a])[_pos]; +#endif + } + u256 extroPrice(Address _a) { return get<2>(addresses[_a]); } + void suicide(Address _a) + { + for (auto const& i: get<3>(addresses[myAddress])) + if (i.second) + get<0>(addresses[_a]) += fees.m_memoryFee; + get<0>(addresses[_a]) += get<0>(addresses[myAddress]); + addresses.erase(myAddress); + } + + void setTransaction(Address _txSender, u256 _txValue, u256s const& _txData) + { + txSender = _txSender; + txValue = _txValue; + txData = _txData; + } + void setContract(Address _myAddress, u256 _myBalance, u256 _myNonce, u256s _myData) + { + myAddress = _myAddress; + set(myAddress, _myBalance, _myNonce, _myData); + } + void set(Address _a, u256 _myBalance, u256 _myNonce, u256s _myData) + { + get<0>(addresses[_a]) = _myBalance; + get<1>(addresses[_a]) = _myNonce; + get<2>(addresses[_a]) = 0; + for (unsigned i = 0; i < _myData.size(); ++i) +#ifdef __clang__ + { + tuple > & address = addresses[_a]; + map & third = get<3>(address); + auto sFinder = third.find(i); + if (sFinder != third.end()) + sFinder->second = _myData[i]; + else + third.insert(std::make_pair(i, _myData[i])); + } +#else + get<3>(addresses[_a])[i] = _myData[i]; +#endif + } + + mObject exportEnv() + { + mObject ret; + ret["previousHash"] = toString(previousBlock.hash); + ret["previousNonce"] = toString(previousBlock.nonce); + push(ret, "currentDifficulty", currentBlock.difficulty); + push(ret, "currentTimestamp", currentBlock.timestamp); + ret["currentCoinbase"] = toString(currentBlock.coinbaseAddress); + push(ret, "feeMultiplier", fees.multiplier()); + return ret; + } + + void importEnv(mObject& _o) + { + previousBlock.hash = h256(_o["previousHash"].get_str()); + previousBlock.nonce = h256(_o["previousNonce"].get_str()); + currentBlock.difficulty = toInt(_o["currentDifficulty"]); + currentBlock.timestamp = toInt(_o["currentTimestamp"]); + currentBlock.coinbaseAddress = Address(_o["currentCoinbase"].get_str()); + fees.setMultiplier(toInt(_o["feeMultiplier"])); + } + + static u256 toInt(mValue const& _v) + { + switch (_v.type()) + { + case str_type: return u256(_v.get_str()); + case int_type: return (u256)_v.get_uint64(); + case bool_type: return (u256)(uint64_t)_v.get_bool(); + case real_type: return (u256)(uint64_t)_v.get_real(); + default: cwarn << "Bad type for scalar: " << _v.type(); + } + return 0; + } + + static void push(mObject& o, string const& _n, u256 _v) + { + if (_v < (u256)1 << 64) + o[_n] = (uint64_t)_v; + else + o[_n] = toString(_v); + } + + static void push(mArray& a, u256 _v) + { + if (_v < (u256)1 << 64) + a.push_back((uint64_t)_v); + else + a.push_back(toString(_v)); + } + + mObject exportState() + { + mObject ret; + for (auto const& a: addresses) + { + mObject o; + push(o, "balance", get<0>(a.second)); + push(o, "nonce", get<1>(a.second)); + push(o, "extroPrice", get<2>(a.second)); + + mObject store; + string curKey; + u256 li = 0; + mArray curVal; + for (auto const& s: get<3>(a.second)) + { + if (!li || s.first > li + 8) + { + if (li) + store[curKey] = curVal; + li = s.first; + curKey = toString(li); + curVal = mArray(); + } + else + for (; li != s.first; ++li) + curVal.push_back(0); + push(curVal, s.second); + ++li; + } + if (li) + { + store[curKey] = curVal; + o["store"] = store; + } + ret[toString(a.first)] = o; + } + return ret; + } + + void importState(mObject& _o) + { + for (auto const& i: _o) + { + mObject o = i.second.get_obj(); + auto& a = addresses[Address(i.first)]; + get<0>(a) = toInt(o["balance"]); + get<1>(a) = toInt(o["nonce"]); + get<2>(a) = toInt(o["extroPrice"]); + if (o.count("store")) + for (auto const& j: o["store"].get_obj()) + { + u256 adr(j.first); + for (auto const& k: j.second.get_array()) +#ifdef __clang__ + { + map & third = get<3>(a); + auto sFinder = third.find(adr); + if (sFinder != third.end()) + sFinder->second = toInt(k); + else + third.insert(std::make_pair(adr, toInt(k))); + adr++; + } +#else + get<3>(a)[adr++] = toInt(k); +#endif + } + if (o.count("code")) + { + u256s d = compileLisp(o["code"].get_str()); + for (unsigned i = 0; i < d.size(); ++i) +#ifdef __clang__ + { + map & third = get<3>(a); + auto sFinder = third.find(i); + if (sFinder != third.end()) + sFinder->second = d[i]; + else + third.insert(std::make_pair(i, d[i])); + } +#else + get<3>(a)[(u256)i] = d[i]; +#endif + } + } + } + + mObject exportExec() + { + mObject ret; + ret["address"] = toString(myAddress); + ret["sender"] = toString(txSender); + push(ret, "value", txValue); + mArray d; + for (auto const& i: txData) + push(d, i); + ret["data"] = d; + return ret; + } + + void importExec(mObject& _o) + { + myAddress = Address(_o["address"].get_str()); + txSender = Address(_o["sender"].get_str()); + txValue = toInt(_o["value"]); + for (auto const& j: _o["data"].get_array()) + txData.push_back(toInt(j)); + } + + mArray exportTxs() + { + mArray ret; + for (Transaction const& tx: txs) + { + mObject o; + o["destination"] = toString(tx.receiveAddress); + push(o, "value", tx.value); + mArray d; + for (auto const& i: tx.data) + push(d, i); + o["data"] = d; + ret.push_back(o); + } + return ret; + } + + void importTxs(mArray& _txs) + { + for (mValue& v: _txs) + { + auto tx = v.get_obj(); + Transaction t; + t.receiveAddress = Address(tx["destination"].get_str()); + t.value = toInt(tx["value"]); + for (auto const& j: tx["data"].get_array()) + t.data.push_back(toInt(j)); + txs.push_back(t); + } + } + + void reset(u256 _myBalance, u256 _myNonce, u256s _myData) + { + txs.clear(); + addresses.clear(); + set(myAddress, _myBalance, _myNonce, _myData); + } + + map>> addresses; + Transactions txs; +}; + +#define CREATE_TESTS 0 + +template <> class UnitTest<1> +{ +public: + int operator()() + { + json_spirit::mValue v; +#if CREATE_TESTS + string s = asString(contents("../../cpp-ethereum/test/vmtests.json")); + json_spirit::read_string(s, v); + bool passed = doTests(v, true); + cout << json_spirit::write_string(v, true) << endl; +#else + string s = asString(contents("../../tests/vmtests.json")); + json_spirit::read_string(s, v); + bool passed = doTests(v, false); +#endif + return passed ? 0 : 1; + } + + bool doTests(json_spirit::mValue& v, bool _fillin) + { + bool passed = true; + for (auto& i: v.get_obj()) + { + cnote << i.first; + mObject& o = i.second.get_obj(); + + VM vm; + FakeExtVM fev; + fev.importEnv(o["env"].get_obj()); + fev.importState(o["pre"].get_obj()); + + if (_fillin) + o["pre"] = mValue(fev.exportState()); + + for (auto i: o["exec"].get_array()) + { + fev.importExec(i.get_obj()); + vm.go(fev); + } + if (_fillin) + { + o["post"] = mValue(fev.exportState()); + o["txs"] = fev.exportTxs(); + } + else + { + FakeExtVM test; + test.importState(o["post"].get_obj()); + test.importTxs(o["txs"].get_array()); + if (test.addresses != fev.addresses) + { + cwarn << "Test failed: state different."; + passed = false; + } + if (test.txs != fev.txs) + { + cwarn << "Test failed: tx list different:"; + cwarn << test.txs; + cwarn << fev.txs; + passed = false; + } + } + } + return passed; + } + + string makeTestCase() + { + json_spirit::mObject o; + + VM vm; + BlockInfo pb; + pb.hash = sha3("previousHash"); + pb.nonce = sha3("previousNonce"); + BlockInfo cb = pb; + cb.difficulty = 256; + cb.timestamp = 1; + cb.coinbaseAddress = toAddress(sha3("coinbase")); + FeeStructure fees; + fees.setMultiplier(1); + FakeExtVM fev(fees, pb, cb, 0); + fev.setContract(toAddress(sha3("contract")), ether, 0, compileLisp("(suicide (txsender))")); + o["env"] = fev.exportEnv(); + o["pre"] = fev.exportState(); + fev.setTransaction(toAddress(sha3("sender")), ether, u256s()); + mArray execs; + execs.push_back(fev.exportExec()); + o["exec"] = execs; + vm.go(fev); + o["post"] = fev.exportState(); + o["txs"] = fev.exportTxs(); + + return json_spirit::write_string(json_spirit::mValue(o), true); + } +}; + +} + +int vmTest() +{ + return UnitTest<1>()(); +} + diff --git a/test/vmtests.json b/test/vmtests.json new file mode 100644 index 000000000..52e31f4ec --- /dev/null +++ b/test/vmtests.json @@ -0,0 +1,84 @@ +{ + "suicide": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "previousNonce" : "9c9c6567b5ec0c5f3f25df79be42707090f1e62e9db84cbb556ae2a2f6ccccae", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", + "feeMultiplier" : 1 + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : 1000000000000000000, + "nonce" : 0, + "code" : "(suicide (txsender))" + } + }, + "exec" : [ + { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "sender" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : 1000000000000000000, + "data" : [ + ] + } + ] + }, + + "mktx": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "previousNonce" : "9c9c6567b5ec0c5f3f25df79be42707090f1e62e9db84cbb556ae2a2f6ccccae", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", + "feeMultiplier" : 1 + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : 1000000000000000000, + "nonce" : 0, + "code" : "(mktx (txsender) 500000000000000000 0)" + } + }, + "exec" : [ + { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "sender" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : 1000000000000000000, + "data" : [ + ] + } + ] + }, + + "fan": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "previousNonce" : "9c9c6567b5ec0c5f3f25df79be42707090f1e62e9db84cbb556ae2a2f6ccccae", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", + "feeMultiplier" : 1 + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : 0, + "nonce" : 0, + "code" : "(seq (unless (gt (txvalue) 100finney) (stop)) (\"value\" (div (sub (txvalue) 100finney) (txdatan))) (\"i\" 0) (for (lt (\"i\") (txdatan)) (seq (mktx (txdata (\"i\")) (\"value\") 0) (\"i\" (add (\"i\") 1)) ) ) )" + } + }, + "exec" : [ + { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "sender" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : 1000000000000000000, + "data" : [ + "0xcd1722f3947def4cf144679da39c4c32bdc35681", + "0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + ] + } + ] + } +} diff --git a/windows/Alethzero.vcxproj b/windows/Alethzero.vcxproj new file mode 100644 index 000000000..334a5147e --- /dev/null +++ b/windows/Alethzero.vcxproj @@ -0,0 +1,212 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + Win32Proj + AlethZero + AlethZero + {BFCFFC46-78A3-4350-9938-0EA3A2B1CCCD} + + + + Application + true + v120 + + + Application + true + v120 + + + Application + false + true + v120 + + + Application + false + true + v120 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + + + + false + + + false + + + + Use + Disabled + WIN32;_DEBUG;%(PreprocessorDefinitions) + MultiThreadedDebug + true + stdafx.h + + + Windows + true + true + + + + + Use + Disabled + WIN32;_DEBUG;%(PreprocessorDefinitions) + MultiThreadedDebug + true + stdafx.h + + + Windows + true + + + + + Use + MaxSpeed + true + true + WIN32;NDEBUG;%(PreprocessorDefinitions) + MultiThreaded + true + AnySuitable + stdafx.h + + + Windows + true + true + true + true + + + + + Use + MaxSpeed + true + true + WIN32;NDEBUG;%(PreprocessorDefinitions) + MultiThreaded + true + AnySuitable + stdafx.h + + + Windows + true + true + true + + + + + {826e68cb-d3ee-4a8a-b540-59a8c3f38d4f} + + + + + + + + Create + Create + Create + Create + + + + + + "$(Lua)" moc.lua "$(QtBin)/moc" "$(IntDir)moc_%(FileName).cpp" "@(ClCompile->'%(AdditionalIncludeDirectories)');$(IncludePath)" "@(ClCompile->'%(PreprocessorDefinitions)');_MSC_VER=1800" "%(FullPath)" + $(IntDir)moc_%(FileName).cpp + + + "$(Lua)" moc.lua "$(QtBin)/moc" "$(IntDir)moc_%(FileName).cpp" "@(ClCompile->'%(AdditionalIncludeDirectories)');$(IncludePath)" "@(ClCompile->'%(PreprocessorDefinitions)');_MSC_VER=1800" "%(FullPath)" + + + "$(Lua)" moc.lua "$(QtBin)/moc" "$(IntDir)moc_%(FileName).cpp" "@(ClCompile->'%(AdditionalIncludeDirectories)');$(IncludePath)" "@(ClCompile->'%(PreprocessorDefinitions)');_MSC_VER=1800" "%(FullPath)" + + + "$(Lua)" moc.lua "$(QtBin)/moc" "$(IntDir)moc_%(FileName).cpp" "@(ClCompile->'%(AdditionalIncludeDirectories)');$(IncludePath)" "@(ClCompile->'%(PreprocessorDefinitions)');_MSC_VER=1800" "%(FullPath)" + + + $(IntDir)moc_%(FileName).cpp + $(IntDir)moc_%(FileName).cpp + $(IntDir)moc_%(FileName).cpp + + + + + + Document + "$(QtBin)/uic" -o "$(IntDir)ui_%(FileName).h" "%(FullPath)" + "$(QtBin)/uic" -o "$(IntDir)ui_%(FileName).h" "%(FullPath)" + $(IntDir)ui_%(FileName).h + "$(QtBin)/uic" -o "$(IntDir)ui_%(FileName).h" "%(FullPath)" + "$(QtBin)/uic" -o "$(IntDir)ui_%(FileName).h" "%(FullPath)" + $(IntDir)ui_%(FileName).h + "$(QtBin)/uic" -o "$(IntDir)ui_%(FileName).h" "%(FullPath)" + "$(QtBin)/uic" -o "$(IntDir)ui_%(FileName).h" "%(FullPath)" + $(IntDir)ui_%(FileName).h + "$(QtBin)/uic" -o "$(IntDir)ui_%(FileName).h" "%(FullPath)" + "$(QtBin)/uic" -o "$(IntDir)ui_%(FileName).h" "%(FullPath)" + $(IntDir)ui_%(FileName).h + + + + + + \ No newline at end of file diff --git a/windows/Alethzero.vcxproj.filters b/windows/Alethzero.vcxproj.filters new file mode 100644 index 000000000..ecf9afee0 --- /dev/null +++ b/windows/Alethzero.vcxproj.filters @@ -0,0 +1,28 @@ + + + + + + + Windows + + + Windows + + + + + + Windows + + + + + {049fb46c-1677-45cb-95ae-3a2d9e3090be} + + + + + + + \ No newline at end of file diff --git a/windows/CopyBinary.props b/windows/CopyBinary.props new file mode 100644 index 000000000..3f94f615a --- /dev/null +++ b/windows/CopyBinary.props @@ -0,0 +1,18 @@ + + + + + + + + xcopy /F /Y $(OutDir)$(TargetName)$(TargetExt) ..\..\_binaries\$(Platform)\ + + + + + + + + + + \ No newline at end of file diff --git a/windows/Ethereum.sln b/windows/Ethereum.sln new file mode 100644 index 000000000..1be0ab848 --- /dev/null +++ b/windows/Ethereum.sln @@ -0,0 +1,121 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Express 2013 for Windows Desktop +VisualStudioVersion = 12.0.21005.1 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Libs", "Libs", "{988F2383-FA1D-408B-BCF6-C0EE7AB0A560}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{6838FA95-01BF-4FF7-914C-FC209B81406E}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "LibEthereum", "LibEthereum.vcxproj", "{826E68CB-D3EE-4A8A-B540-59A8C3F38D4F}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "LibLevelDB", "LibLevelDB.vcxproj", "{27014763-955D-486B-9BA7-69872192E6F4}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "LibSecp256k1", "LibSecp256k1.vcxproj", "{1E1175BB-C4A9-41D8-B2D1-9022F71D3CEA}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Ethereum", "Ethereum.vcxproj", "{C60C065C-2135-4B2B-AFD4-35FD7AC56B40}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TestSecp256k1", "TestSecp256k1.vcxproj", "{3BF049F8-AF7E-4E1C-9627-3E94C887AF24}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TestEthereum", "TestEthereum.vcxproj", "{3F3E389B-88DE-41D5-A73B-4F6036E18B36}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "LibCryptoPP", "LibCryptoPP.vcxproj", "{1CC213A4-3482-4211-B47B-172E90DAC7DE}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "LibMiniUPnPc", "LibMiniUPnPc.vcxproj", "{1B1CA20E-39C3-4D9B-AC37-3783048E6672}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Alethzero", "Alethzero.vcxproj", "{BFCFFC46-78A3-4350-9938-0EA3A2B1CCCD}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Debug|x64 = Debug|x64 + Release|Win32 = Release|Win32 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {826E68CB-D3EE-4A8A-B540-59A8C3F38D4F}.Debug|Win32.ActiveCfg = Debug|Win32 + {826E68CB-D3EE-4A8A-B540-59A8C3F38D4F}.Debug|Win32.Build.0 = Debug|Win32 + {826E68CB-D3EE-4A8A-B540-59A8C3F38D4F}.Debug|x64.ActiveCfg = Debug|x64 + {826E68CB-D3EE-4A8A-B540-59A8C3F38D4F}.Debug|x64.Build.0 = Debug|x64 + {826E68CB-D3EE-4A8A-B540-59A8C3F38D4F}.Release|Win32.ActiveCfg = Release|Win32 + {826E68CB-D3EE-4A8A-B540-59A8C3F38D4F}.Release|Win32.Build.0 = Release|Win32 + {826E68CB-D3EE-4A8A-B540-59A8C3F38D4F}.Release|x64.ActiveCfg = Release|x64 + {826E68CB-D3EE-4A8A-B540-59A8C3F38D4F}.Release|x64.Build.0 = Release|x64 + {27014763-955D-486B-9BA7-69872192E6F4}.Debug|Win32.ActiveCfg = Debug|Win32 + {27014763-955D-486B-9BA7-69872192E6F4}.Debug|Win32.Build.0 = Debug|Win32 + {27014763-955D-486B-9BA7-69872192E6F4}.Debug|x64.ActiveCfg = Debug|x64 + {27014763-955D-486B-9BA7-69872192E6F4}.Debug|x64.Build.0 = Debug|x64 + {27014763-955D-486B-9BA7-69872192E6F4}.Release|Win32.ActiveCfg = Release|Win32 + {27014763-955D-486B-9BA7-69872192E6F4}.Release|Win32.Build.0 = Release|Win32 + {27014763-955D-486B-9BA7-69872192E6F4}.Release|x64.ActiveCfg = Release|x64 + {27014763-955D-486B-9BA7-69872192E6F4}.Release|x64.Build.0 = Release|x64 + {1E1175BB-C4A9-41D8-B2D1-9022F71D3CEA}.Debug|Win32.ActiveCfg = Debug|Win32 + {1E1175BB-C4A9-41D8-B2D1-9022F71D3CEA}.Debug|Win32.Build.0 = Debug|Win32 + {1E1175BB-C4A9-41D8-B2D1-9022F71D3CEA}.Debug|x64.ActiveCfg = Debug|x64 + {1E1175BB-C4A9-41D8-B2D1-9022F71D3CEA}.Debug|x64.Build.0 = Debug|x64 + {1E1175BB-C4A9-41D8-B2D1-9022F71D3CEA}.Release|Win32.ActiveCfg = Release|Win32 + {1E1175BB-C4A9-41D8-B2D1-9022F71D3CEA}.Release|Win32.Build.0 = Release|Win32 + {1E1175BB-C4A9-41D8-B2D1-9022F71D3CEA}.Release|x64.ActiveCfg = Release|x64 + {1E1175BB-C4A9-41D8-B2D1-9022F71D3CEA}.Release|x64.Build.0 = Release|x64 + {C60C065C-2135-4B2B-AFD4-35FD7AC56B40}.Debug|Win32.ActiveCfg = Debug|Win32 + {C60C065C-2135-4B2B-AFD4-35FD7AC56B40}.Debug|Win32.Build.0 = Debug|Win32 + {C60C065C-2135-4B2B-AFD4-35FD7AC56B40}.Debug|x64.ActiveCfg = Debug|x64 + {C60C065C-2135-4B2B-AFD4-35FD7AC56B40}.Debug|x64.Build.0 = Debug|x64 + {C60C065C-2135-4B2B-AFD4-35FD7AC56B40}.Release|Win32.ActiveCfg = Release|Win32 + {C60C065C-2135-4B2B-AFD4-35FD7AC56B40}.Release|Win32.Build.0 = Release|Win32 + {C60C065C-2135-4B2B-AFD4-35FD7AC56B40}.Release|x64.ActiveCfg = Release|x64 + {C60C065C-2135-4B2B-AFD4-35FD7AC56B40}.Release|x64.Build.0 = Release|x64 + {3BF049F8-AF7E-4E1C-9627-3E94C887AF24}.Debug|Win32.ActiveCfg = Debug|Win32 + {3BF049F8-AF7E-4E1C-9627-3E94C887AF24}.Debug|Win32.Build.0 = Debug|Win32 + {3BF049F8-AF7E-4E1C-9627-3E94C887AF24}.Debug|x64.ActiveCfg = Debug|x64 + {3BF049F8-AF7E-4E1C-9627-3E94C887AF24}.Debug|x64.Build.0 = Debug|x64 + {3BF049F8-AF7E-4E1C-9627-3E94C887AF24}.Release|Win32.ActiveCfg = Release|Win32 + {3BF049F8-AF7E-4E1C-9627-3E94C887AF24}.Release|Win32.Build.0 = Release|Win32 + {3BF049F8-AF7E-4E1C-9627-3E94C887AF24}.Release|x64.ActiveCfg = Release|x64 + {3BF049F8-AF7E-4E1C-9627-3E94C887AF24}.Release|x64.Build.0 = Release|x64 + {3F3E389B-88DE-41D5-A73B-4F6036E18B36}.Debug|Win32.ActiveCfg = Debug|Win32 + {3F3E389B-88DE-41D5-A73B-4F6036E18B36}.Debug|Win32.Build.0 = Debug|Win32 + {3F3E389B-88DE-41D5-A73B-4F6036E18B36}.Debug|x64.ActiveCfg = Debug|x64 + {3F3E389B-88DE-41D5-A73B-4F6036E18B36}.Debug|x64.Build.0 = Debug|x64 + {3F3E389B-88DE-41D5-A73B-4F6036E18B36}.Release|Win32.ActiveCfg = Release|Win32 + {3F3E389B-88DE-41D5-A73B-4F6036E18B36}.Release|Win32.Build.0 = Release|Win32 + {3F3E389B-88DE-41D5-A73B-4F6036E18B36}.Release|x64.ActiveCfg = Release|x64 + {3F3E389B-88DE-41D5-A73B-4F6036E18B36}.Release|x64.Build.0 = Release|x64 + {1CC213A4-3482-4211-B47B-172E90DAC7DE}.Debug|Win32.ActiveCfg = Debug|Win32 + {1CC213A4-3482-4211-B47B-172E90DAC7DE}.Debug|Win32.Build.0 = Debug|Win32 + {1CC213A4-3482-4211-B47B-172E90DAC7DE}.Debug|x64.ActiveCfg = Debug|x64 + {1CC213A4-3482-4211-B47B-172E90DAC7DE}.Debug|x64.Build.0 = Debug|x64 + {1CC213A4-3482-4211-B47B-172E90DAC7DE}.Release|Win32.ActiveCfg = Release|Win32 + {1CC213A4-3482-4211-B47B-172E90DAC7DE}.Release|Win32.Build.0 = Release|Win32 + {1CC213A4-3482-4211-B47B-172E90DAC7DE}.Release|x64.ActiveCfg = Release|x64 + {1CC213A4-3482-4211-B47B-172E90DAC7DE}.Release|x64.Build.0 = Release|x64 + {1B1CA20E-39C3-4D9B-AC37-3783048E6672}.Debug|Win32.ActiveCfg = Debug|Win32 + {1B1CA20E-39C3-4D9B-AC37-3783048E6672}.Debug|Win32.Build.0 = Debug|Win32 + {1B1CA20E-39C3-4D9B-AC37-3783048E6672}.Debug|x64.ActiveCfg = Debug|x64 + {1B1CA20E-39C3-4D9B-AC37-3783048E6672}.Debug|x64.Build.0 = Debug|x64 + {1B1CA20E-39C3-4D9B-AC37-3783048E6672}.Release|Win32.ActiveCfg = Release|Win32 + {1B1CA20E-39C3-4D9B-AC37-3783048E6672}.Release|Win32.Build.0 = Release|Win32 + {1B1CA20E-39C3-4D9B-AC37-3783048E6672}.Release|x64.ActiveCfg = Release|x64 + {1B1CA20E-39C3-4D9B-AC37-3783048E6672}.Release|x64.Build.0 = Release|x64 + {BFCFFC46-78A3-4350-9938-0EA3A2B1CCCD}.Debug|Win32.ActiveCfg = Debug|Win32 + {BFCFFC46-78A3-4350-9938-0EA3A2B1CCCD}.Debug|Win32.Build.0 = Debug|Win32 + {BFCFFC46-78A3-4350-9938-0EA3A2B1CCCD}.Debug|x64.ActiveCfg = Debug|x64 + {BFCFFC46-78A3-4350-9938-0EA3A2B1CCCD}.Debug|x64.Build.0 = Debug|x64 + {BFCFFC46-78A3-4350-9938-0EA3A2B1CCCD}.Release|Win32.ActiveCfg = Release|Win32 + {BFCFFC46-78A3-4350-9938-0EA3A2B1CCCD}.Release|Win32.Build.0 = Release|Win32 + {BFCFFC46-78A3-4350-9938-0EA3A2B1CCCD}.Release|x64.ActiveCfg = Release|x64 + {BFCFFC46-78A3-4350-9938-0EA3A2B1CCCD}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {826E68CB-D3EE-4A8A-B540-59A8C3F38D4F} = {988F2383-FA1D-408B-BCF6-C0EE7AB0A560} + {27014763-955D-486B-9BA7-69872192E6F4} = {988F2383-FA1D-408B-BCF6-C0EE7AB0A560} + {1E1175BB-C4A9-41D8-B2D1-9022F71D3CEA} = {988F2383-FA1D-408B-BCF6-C0EE7AB0A560} + {1CC213A4-3482-4211-B47B-172E90DAC7DE} = {988F2383-FA1D-408B-BCF6-C0EE7AB0A560} + {1B1CA20E-39C3-4D9B-AC37-3783048E6672} = {988F2383-FA1D-408B-BCF6-C0EE7AB0A560} + {3BF049F8-AF7E-4E1C-9627-3E94C887AF24} = {6838FA95-01BF-4FF7-914C-FC209B81406E} + {3F3E389B-88DE-41D5-A73B-4F6036E18B36} = {6838FA95-01BF-4FF7-914C-FC209B81406E} + EndGlobalSection +EndGlobal diff --git a/eth/Ethereum.vcxproj b/windows/Ethereum.vcxproj similarity index 81% rename from eth/Ethereum.vcxproj rename to windows/Ethereum.vcxproj index 68ce0364c..023424879 100644 --- a/eth/Ethereum.vcxproj +++ b/windows/Ethereum.vcxproj @@ -29,59 +29,60 @@ Application true v120 - Unicode Application true v120 - Unicode Application false - v120 true - Unicode + v120 Application false - v120 true - Unicode + v120 - + - + - + + - + + true + eth - true + eth false + eth false + eth @@ -89,12 +90,8 @@ Disabled WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) - ../libethereum MultiThreadedDebug true - false - true - 4351 Console @@ -108,12 +105,8 @@ Disabled WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) - ../libethereum MultiThreadedDebug true - false - true - 4351 Console @@ -128,13 +121,9 @@ true true WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) - ../libethereum MultiThreaded true AnySuitable - false - true - 4351 Console @@ -152,13 +141,9 @@ true true WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) - ../libethereum MultiThreaded true AnySuitable - false - true - 4351 Console @@ -168,15 +153,12 @@ - - {3423ec9a-52e4-4a4d-9753-edebc38785ef} - - - {7050c7cf-7551-48be-8e57-92235906c13a} + + {826e68cb-d3ee-4a8a-b540-59a8c3f38d4f} - + diff --git a/windows/LibCryptoPP.vcxproj b/windows/LibCryptoPP.vcxproj new file mode 100644 index 000000000..90751d5d0 --- /dev/null +++ b/windows/LibCryptoPP.vcxproj @@ -0,0 +1,176 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Document + ml64.exe /c /nologo /Fo"$(IntDir)x64dll.obj" /Zi "%(FullPath)" + ml64.exe /c /nologo /Fo"$(IntDir)x64dll.obj" /Zi "%(FullPath)" + $(IntDir)x64dll.obj + $(IntDir)x64dll.obj + + + + Win32Proj + LibCryptoPP + {1CC213A4-3482-4211-B47B-172E90DAC7DE} + + + + StaticLibrary + true + v120 + + + StaticLibrary + true + v120 + + + StaticLibrary + false + true + v120 + + + StaticLibrary + false + true + v120 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Disabled + MultiThreadedDebug + 4189;4244;%(DisableSpecificWarnings) + + + Windows + true + + + + + + + Disabled + MultiThreadedDebug + 4189;4244;%(DisableSpecificWarnings) + + + Windows + true + + + + + + + MaxSpeed + true + true + MultiThreaded + 4189;4244;%(DisableSpecificWarnings) + + + Windows + true + true + true + + + + + + + MaxSpeed + true + true + MultiThreaded + 4189;4244;%(DisableSpecificWarnings) + + + Windows + true + true + true + + + + + + \ No newline at end of file diff --git a/windows/LibEthereum.props b/windows/LibEthereum.props new file mode 100644 index 000000000..3de930166 --- /dev/null +++ b/windows/LibEthereum.props @@ -0,0 +1,31 @@ + + + + + ../../boost + + + ..\..\_build\$(ProjectName)\$(Platform)_$(Configuration)\ + ..\..\_build\$(ProjectName)\$(Platform)_$(Configuration)\ + + + + 4068;4100;4127;4258;4505;4512;4706 + Level4 + true + false + include/$(ProjectName);../libethereum;$(BoostDir);../../leveldb/include;../../cryptopp;../secp256k1;../../miniupnp + ETH_BUILD_PLATFORM=Windows/VS2013;ETH_BUILD_TYPE=$(Configuration)-$(Platform);STATICLIB;LEVELDB_PLATFORM_WINDOWS;USE_NUM_BOOST;USE_FIELD_10X26;USE_FIELD_INV_BUILTIN;_WIN32_WINNT=0x0501;WIN32;%(PreprocessorDefinitions) + true + true + + + $(BoostDir)/stage/$(Platform) + + + + + $(BoostDir) + + + \ No newline at end of file diff --git a/windows/LibEthereum.vcxproj b/windows/LibEthereum.vcxproj new file mode 100644 index 000000000..af6abbc01 --- /dev/null +++ b/windows/LibEthereum.vcxproj @@ -0,0 +1,218 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + + true + true + true + true + + + + + + + + + + + + + + + + + + + + + + + stdafx.h + Create + Create + Create + Create + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {1cc213a4-3482-4211-b47b-172e90dac7de} + + + {27014763-955d-486b-9ba7-69872192e6f4} + + + {1b1ca20e-39c3-4d9b-ac37-3783048e6672} + + + {1e1175bb-c4a9-41d8-b2d1-9022f71d3cea} + + + + {826E68CB-D3EE-4A8A-B540-59A8C3F38D4F} + Win32Proj + LibEthereum + + + + StaticLibrary + true + v120 + + + StaticLibrary + true + v120 + + + StaticLibrary + false + true + v120 + + + StaticLibrary + false + true + v120 + + + + + + + + + + + + + + + + + + + + + + + + + + + + Disabled + WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) + false + MultiThreadedDebug + Use + stdafx.h + + + Windows + true + + + + + Disabled + WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) + false + MultiThreadedDebug + stdafx.h + Use + + + Windows + true + + + + + + + MaxSpeed + true + true + WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) + true + MultiThreaded + Use + stdafx.h + + + Windows + true + true + true + + + + + MaxSpeed + true + true + WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) + true + MultiThreaded + Use + stdafx.h + + + Windows + true + true + true + + + + + + \ No newline at end of file diff --git a/windows/LibEthereum.vcxproj.filters b/windows/LibEthereum.vcxproj.filters new file mode 100644 index 000000000..3e4a3ee6d --- /dev/null +++ b/windows/LibEthereum.vcxproj.filters @@ -0,0 +1,63 @@ + + + + + + + + + + + + + + + + + + + + + + + + Windows + + + + + + + + + + + + + + + + + + + + + + + + + + + + Windows + + + + + + + + {2d571d8f-bacf-4c49-a0d0-c9036f5c0cc9} + + + \ No newline at end of file diff --git a/windows/LibLevelDB.vcxproj b/windows/LibLevelDB.vcxproj new file mode 100644 index 000000000..ed04f569a --- /dev/null +++ b/windows/LibLevelDB.vcxproj @@ -0,0 +1,223 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {27014763-955D-486B-9BA7-69872192E6F4} + Win32Proj + LibLevelDB + + + + StaticLibrary + true + v120 + + + StaticLibrary + true + v120 + + + StaticLibrary + false + true + v120 + + + StaticLibrary + false + true + v120 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Disabled + MultiThreadedDebug + %(AdditionalIncludeDirectories);../../leveldb + _CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + 4018;4244;4267;4389;4702;4722;4800;4996;%(DisableSpecificWarnings) + + + Windows + true + + + + + + + Disabled + MultiThreadedDebug + %(AdditionalIncludeDirectories);../../leveldb + _CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + 4018;4244;4267;4389;4702;4722;4800;4996;%(DisableSpecificWarnings) + + + Windows + true + + + + + + + MaxSpeed + true + true + MultiThreaded + %(AdditionalIncludeDirectories);../../leveldb + _CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + 4018;4244;4267;4389;4702;4722;4800;4996;%(DisableSpecificWarnings) + + + Windows + true + true + true + + + + + + + MaxSpeed + true + true + MultiThreaded + %(AdditionalIncludeDirectories);../../leveldb + _CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + 4018;4244;4267;4389;4702;4722;4800;4996;%(DisableSpecificWarnings) + + + Windows + true + true + true + + + + + + \ No newline at end of file diff --git a/windows/LibLevelDB.vcxproj.filters b/windows/LibLevelDB.vcxproj.filters new file mode 100644 index 000000000..bc3306f56 --- /dev/null +++ b/windows/LibLevelDB.vcxproj.filters @@ -0,0 +1,246 @@ + + + + + {d83904b1-b5d1-4c5b-b476-96f08300d103} + + + {72573022-b7fd-4b7a-a92e-a68c06bd6366} + + + {7f821e9e-4ebf-4d18-8fb4-898bd3d81376} + + + {f285a595-6c39-4350-8d30-6f696a3a7c4c} + + + + + db + + + db + + + db + + + db + + + db + + + db + + + db + + + db + + + db + + + db + + + db + + + db + + + db + + + db + + + db + + + table + + + table + + + table + + + table + + + table + + + table + + + util + + + util + + + util + + + util + + + util + + + util + + + util + + + util + + + util + + + util + + + util + + + util + + + port + + + port + + + port + + + + + db + + + db + + + db + + + db + + + db + + + db + + + db + + + db + + + db + + + db + + + db + + + db + + + db + + + db + + + db + + + table + + + table + + + table + + + table + + + table + + + table + + + table + + + table + + + util + + + util + + + util + + + util + + + util + + + util + + + util + + + util + + + util + + + util + + + util + + + util + + + util + + + util + + + util + + + port + + + \ No newline at end of file diff --git a/windows/LibMiniUPnPc.vcxproj b/windows/LibMiniUPnPc.vcxproj new file mode 100644 index 000000000..7f3e59131 --- /dev/null +++ b/windows/LibMiniUPnPc.vcxproj @@ -0,0 +1,184 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Win32Proj + LibMiniUPnPc + {1B1CA20E-39C3-4D9B-AC37-3783048E6672} + LibMiniUPnPc + + + + StaticLibrary + true + v120 + + + StaticLibrary + true + v120 + + + StaticLibrary + false + true + v120 + + + StaticLibrary + false + true + v120 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Disabled + MultiThreadedDebug + _CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + 4244;4245;4267;4389;%(DisableSpecificWarnings) + + + Windows + true + + + Iphlpapi.lib + + + + + + + Disabled + MultiThreadedDebug + _CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + 4244;4245;4267;4389;%(DisableSpecificWarnings) + + + Windows + true + + + Iphlpapi.lib + + + + + + + MaxSpeed + true + true + MultiThreaded + _CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + 4244;4245;4267;4389;%(DisableSpecificWarnings) + + + Windows + true + true + true + + + Iphlpapi.lib + + + + + + + MaxSpeed + true + true + MultiThreaded + _CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + 4244;4245;4267;4389;%(DisableSpecificWarnings) + + + Windows + true + true + true + + + Iphlpapi.lib + + + + + + \ No newline at end of file diff --git a/libethereum/LibEthereum.vcxproj b/windows/LibSecp256k1.vcxproj similarity index 68% rename from libethereum/LibEthereum.vcxproj rename to windows/LibSecp256k1.vcxproj index 30b4d8380..cccf0e014 100644 --- a/libethereum/LibEthereum.vcxproj +++ b/windows/LibSecp256k1.vcxproj @@ -19,65 +19,73 @@ - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - + + CompileAsCpp + CompileAsCpp + CompileAsCpp + CompileAsCpp + + + + - {7050C7CF-7551-48BE-8E57-92235906C13A} Win32Proj - LibEthereum + LibSecp256k1 + {1E1175BB-C4A9-41D8-B2D1-9022F71D3CEA} + LibSecp256k1 StaticLibrary true v120 - Unicode StaticLibrary true v120 - Unicode StaticLibrary false - v120 true - Unicode + v120 StaticLibrary false - v120 true - Unicode + v120 @@ -99,25 +107,19 @@ - - ..\build\$(ProjectName)\$(Platform)_$(Configuration)\ - ..\build\$(ProjectName)\$(Platform)_$(Configuration)\ - + - - ..\build\$(ProjectName)\$(Platform)_$(Configuration)\ - ..\build\$(ProjectName)\$(Platform)_$(Configuration)\ - - - ..\build\$(ProjectName)\$(Platform)_$(Configuration)\ - ..\build\$(ProjectName)\$(Platform)_$(Configuration)\ - + + + + + Level3 Disabled WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) - false MultiThreadedDebug + 4189;4244;4267;%(DisableSpecificWarnings) Windows @@ -126,11 +128,13 @@ + + + Level3 Disabled WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) - true - false MultiThreadedDebug + 4189;4244;4267;%(DisableSpecificWarnings) Windows @@ -139,12 +143,15 @@ + Level3 + + MaxSpeed true true WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) - true MultiThreaded + 4189;4244;4267;%(DisableSpecificWarnings) Windows @@ -155,12 +162,15 @@ + Level3 + + MaxSpeed true true WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) - true MultiThreaded + 4189;4244;4267;%(DisableSpecificWarnings) Windows diff --git a/windows/LibSecp256k1.vcxproj.filters b/windows/LibSecp256k1.vcxproj.filters new file mode 100644 index 000000000..aa4e5b9cb --- /dev/null +++ b/windows/LibSecp256k1.vcxproj.filters @@ -0,0 +1,71 @@ + + + + + {7d4db365-bce7-45c1-861c-f5f86beca992} + + + + + impl + + + impl + + + impl + + + impl + + + impl + + + impl + + + impl + + + impl + + + impl + + + impl + + + impl + + + impl + + + impl + + + impl + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/windows/TestEthereum.vcxproj b/windows/TestEthereum.vcxproj new file mode 100644 index 000000000..045be7b46 --- /dev/null +++ b/windows/TestEthereum.vcxproj @@ -0,0 +1,181 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + Win32Proj + TestEthereum + TestEthereum + {3F3E389B-88DE-41D5-A73B-4F6036E18B36} + + + + Application + true + v120 + + + Application + true + v120 + + + Application + false + true + v120 + + + Application + false + true + v120 + + + + + + + + + + + + + + + + + + + + + + + true + + + true + + + false + + + false + + + + Use + Disabled + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + MultiThreadedDebug + true + true + stdafx.h + + + Console + true + true + + + + + Use + Disabled + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + MultiThreadedDebug + true + stdafx.h + + + Console + true + + + + + Use + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + MultiThreaded + true + AnySuitable + true + stdafx.h + + + Console + true + true + true + true + + + + + Use + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + MultiThreaded + true + AnySuitable + true + stdafx.h + + + Console + true + true + true + + + + + {826e68cb-d3ee-4a8a-b540-59a8c3f38d4f} + + + + + + + + + + + + + + Create + Create + Create + Create + + + + + + + + + \ No newline at end of file diff --git a/windows/TestEthereum.vcxproj.filters b/windows/TestEthereum.vcxproj.filters new file mode 100644 index 000000000..4f9afb9e9 --- /dev/null +++ b/windows/TestEthereum.vcxproj.filters @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + Windows + + + + + {0acd7e2f-2594-4c13-94cb-3247045bdbfd} + + + + + Windows + + + \ No newline at end of file diff --git a/test/Test.vcxproj b/windows/TestSecp256k1.vcxproj similarity index 81% rename from test/Test.vcxproj rename to windows/TestSecp256k1.vcxproj index 8b08adad2..f363f4706 100644 --- a/test/Test.vcxproj +++ b/windows/TestSecp256k1.vcxproj @@ -18,57 +18,66 @@ x64 + + + CompileAsCpp + CompileAsCpp + CompileAsCpp + CompileAsCpp + + + + + {1e1175bb-c4a9-41d8-b2d1-9022f71d3cea} + + Win32Proj - Test - Test - {3F3E389B-88DE-41D5-A73B-4F6036E18B36} + TestSecp256k1 + TestSecp256k1 + {3BF049F8-AF7E-4E1C-9627-3E94C887AF24} Application true v120 - Unicode Application true v120 - Unicode Application false - v120 true - Unicode + v120 Application false - v120 true - Unicode + v120 - + - + - + - + @@ -89,12 +98,10 @@ Disabled WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) - ../libethereum MultiThreadedDebug true - false true - 4351 + 4189;4244;4267;%(DisableSpecificWarnings) Console @@ -108,12 +115,9 @@ Disabled WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) - ../libethereum MultiThreadedDebug true - false - true - 4351 + 4189;4244;4267;%(DisableSpecificWarnings) Console @@ -128,13 +132,11 @@ true true WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) - ../libethereum MultiThreaded true AnySuitable - false true - 4351 + 4189;4244;4267;%(DisableSpecificWarnings) Console @@ -152,13 +154,11 @@ true true WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) - ../libethereum MultiThreaded true AnySuitable - false true - 4351 + 4189;4244;4267;%(DisableSpecificWarnings) Console @@ -167,17 +167,6 @@ true - - - {3423ec9a-52e4-4a4d-9753-edebc38785ef} - - - {7050c7cf-7551-48be-8e57-92235906c13a} - - - - - diff --git a/windows/UseQt.props b/windows/UseQt.props new file mode 100644 index 000000000..9ced7563f --- /dev/null +++ b/windows/UseQt.props @@ -0,0 +1,38 @@ + + + + + ../../Qt/$(Platform) + $(QtDir)/qtbase/bin + $(QtDir)/qtbase/include;../../Qt/Src/qtbase/include + $(QtDir)/qtbase/lib;$(QtDir)/qtbase/plugins/platforms + ../../lua/lua + + + + + + ..;$(IntDir);$(QtInclude);%(AdditionalIncludeDirectories) + + + $(QtLib);%(AdditionalLibraryDirectories) + + + + + $(QtDir) + + + $(QtBin) + + + $(QtInclude) + + + $(QtLib) + + + $(Lua) + + + \ No newline at end of file diff --git a/windows/WinMain.cpp b/windows/WinMain.cpp new file mode 100644 index 000000000..44680c47f --- /dev/null +++ b/windows/WinMain.cpp @@ -0,0 +1,72 @@ +// http://www.flipcode.com/archives/WinMain_Command_Line_Parser.shtml +// COTD Entry submitted by Max McGuire [amcguire@andrew.cmu.edu] + +#include + +extern int main(int argc, char* argv[]); + +int WINAPI WinMain(HINSTANCE instance, HINSTANCE prev_instance, char* command_line, int show_command) +{ + int argc; + char** argv; + char* arg; + int index; + int result; + + // count the arguments + argc = 1; + arg = command_line; + + while (arg[0] != 0) + { + while (arg[0] != 0 && arg[0] == ' ') + { + arg++; + } + if (arg[0] != 0) + { + argc++; + while (arg[0] != 0 && arg[0] != ' ') + { + arg++; + } + } + } + + // tokenize the arguments + argv = (char**)malloc(argc * sizeof(char*)); + arg = command_line; + index = 1; + + while (arg[0] != 0) + { + while (arg[0] != 0 && arg[0] == ' ') + { + arg++; + } + if (arg[0] != 0) + { + argv[index] = arg; + index++; + while (arg[0] != 0 && arg[0] != ' ') + { + arg++; + } + if (arg[0] != 0) + { + arg[0] = 0; + arg++; + } + } + } + + // put the program name into argv[0] + char filename[_MAX_PATH]; + GetModuleFileName(NULL, filename, _MAX_PATH); + argv[0] = filename; + + // call the user specified main function + result = main(argc, argv); + free(argv); + return result; +} diff --git a/windows/bootstrap.sh b/windows/bootstrap.sh new file mode 100644 index 000000000..126889732 --- /dev/null +++ b/windows/bootstrap.sh @@ -0,0 +1,160 @@ +#!/bin/bash +# @file bootstrap.sh +# @author Tim Hughes +# @date 2014 +# Script to fetch and compile depdencies for building Ethereum using Visual Studio Express 2013. +# Prerequisites: +# - Visual Studio Express 2013 for Desktop +# - On PATH: bash, git, git-svn, curl, sed, 7z + +error_exit() { + echo $1 1>&2 + exit 1 +} + +for i in python perl curl git sed 7z; do + which $i &>/dev/null || error_exit "Could not find $i on PATH" +done + +if [ ! -d "$VS120COMNTOOLS" ]; then + error_exit "Couldn't find Visual Studio 2013" +fi + +if [[ ! $@ ]] || [ $1 == "fetch" ]; then + # fetch ethereum (develop branch) + if [ ! -d cpp-ethereum ]; then + (set -x; git clone https://github.com/ethereum/cpp-ethereum.git) + cd cpp-ethereum + (set -x; git checkout -b develop origin/develop) + cd .. + echo + fi + + # fetch CryptoPP-5.6.2 + if [ ! -d cryptopp ]; then + (set -x; git svn clone -r 541:541 http://svn.code.sf.net/p/cryptopp/code/trunk/c5 cryptopp) + echo + fi + + # fetch MiniUPnP-1.8 + if [ ! -d miniupnp ]; then + (set -x; git clone https://github.com/miniupnp/miniupnp.git) + cd miniupnp + (set -x; git checkout tags/miniupnpd_1_8) + cd .. + echo + fi + + # fetch LevelDB (windows branch) + if [ ! -d leveldb ]; then + (set -x; git clone https://code.google.com/p/leveldb/) + cd leveldb + (set -x; git checkout origin/windows) + cd .. + echo + fi + + # fetch and unpack boost-1.55 source + if [ ! -d boost ]; then + if [ ! -f _download/boost_1_55_0.7z ]; then + (set -x; mkdir -p _download) + (set -x; curl -o _download/boost_1_55_0.7z -L http://sourceforge.net/projects/boost/files/boost/1.55.0/boost_1_55_0.7z/download) + fi + (set -x; 7z x _download/boost_1_55_0.7z) + (set -x; mv boost_1_55_0 boost) + echo + fi + + # fetch and unpack Qt 5.1.2 source + if [ ! -d Qt ]; then + if [ ! -f _download/qt-everywhere-opensource-src-5.2.1.zip ]; then + (set -x; mkdir -p _download) + (set -x; curl -o _download/qt-everywhere-opensource-src-5.2.1.zip -L http://download.qt-project.org/official_releases/qt/5.2/5.2.1/single/qt-everywhere-opensource-src-5.2.1.zip) + fi + (set -x; mkdir Qt) + cd Qt + (set -x; 7z x ../_download/qt-everywhere-opensource-src-5.2.1.zip) + (set -x; mv qt-everywhere-opensource-src-5.2.1 Src) + # patch qmake.conf to use the static CRT + (set -x; sed -i -e 's/-MD/-MT/g' Src/qtbase/mkspecs/win32-msvc2013/qmake.conf) + cd .. + echo + fi + + # fetch jom + if [ ! -f "Qt/jom/jom.exe" ]; then + if [ ! -f "_download/jom.zip" ]; then + (set -x; mkdir -p _download) + (set -x; curl -o "_download/jom.zip" -L http://download.qt-project.org/official_releases/jom/jom.zip) + fi + (set -x; mkdir -p Qt/jom) + cd Qt/jom + (set -x; 7z x ../../_download/jom.zip) + cd ../.. + echo + fi + + # fetch and unpack Lua binaries + if [ ! -d lua ]; then + if [ ! -f _download/lua-5.2.1_Win32_bin.zip ]; then + (set -x; mkdir -p _download) + (set -x; curl -o _download/lua-5.2.1_Win32_bin.zip -L http://sourceforge.net/projects/luabinaries/files/5.2.1/Executables/lua-5.2.1_Win32_bin.zip/download) + fi + (set -x; mkdir lua) + cd lua + (set -x; 7z x ../_download/lua-5.2.1_Win32_bin.zip lua52.exe lua52.dll) + (set -x; mv lua52.exe lua.exe) + cd .. + echo + fi +fi + +compile_boost() +{ + if [ $platform == "x64" ]; then + addressModel="address-model=64" + else + addressModel="" + fi + + if [ ! -d "stage/$platform" ]; then + targets="--with-filesystem --with-system --with-thread --with-date_time --with-regex" + (set -x; ./b2 -j4 --build-type=complete link=static runtime-link=static variant=debug,release threading=multi $addressModel $targets stage) + (set -x; mv stage/lib stage/$platform) + fi +} + +if [[ ! $@ ]] || [ $1 == "compile-boost" ]; then + # bootstrap if b2 is missing + cd boost + if [ ! -f "b2.exe" ]; then + (set -x; cmd.exe /c bootstrap.bat) + fi + + # compile boost for x86 and x64 + platform="x64"; compile_boost + platform="Win32"; compile_boost + cd .. + echo +fi + +compile_qt() +{ + if [ ! -d $platform ]; then + (set -x; cmd.exe /c "..\\cpp-ethereum\\windows\\compile_qt.bat $platform") + fi +} + +if [[ ! $@ ]] || [ $1 == "compile-qt" ]; then + # compile Qt for x86 and x64 + cd Qt + platform="x64"; compile_qt + platform="Win32"; compile_qt + cd .. + echo +fi + +# finally run MS build +cd cpp-ethereum/windows +cmd.exe /c "compile_ethereum.bat" +cd .. \ No newline at end of file diff --git a/windows/compile_ethereum.bat b/windows/compile_ethereum.bat new file mode 100644 index 000000000..ee51f0a89 --- /dev/null +++ b/windows/compile_ethereum.bat @@ -0,0 +1,14 @@ +@echo off +rem @file compileqt.bat +rem @author Tim Hughes +rem @date 2014 +echo on + +rem : import VC environment +call "%VS120COMNTOOLS%\VsDevCmd.bat" + +rem : build for x64 +msbuild /maxcpucount /p:Configuration=Release;Platform=x64 Ethereum.sln + +rem : build for Win32 +msbuild /maxcpucount /p:Configuration=Release;Platform=Win32 Ethereum.sln diff --git a/windows/compile_qt.bat b/windows/compile_qt.bat new file mode 100644 index 000000000..c9f0d4ef5 --- /dev/null +++ b/windows/compile_qt.bat @@ -0,0 +1,41 @@ +@echo off +rem @file compileqt.bat +rem @author Tim Hughes +rem @date 2014 + +rem : enable use prefix if we want to produce standalone Qt binaries +rem : off by default since this takes longer and duplicates all the headers +set USE_PREFIX=0 + +rem : echo commands so we can see what's going on +echo on + +rem : select platform and toolset from first argument +IF %1%==x64 (set PLATFORM=x64&set TOOLSET=x86_amd64) ELSE (set PLATFORM=Win32&set TOOLSET=x86) + +rem : import VC environment vars +call "%VS120COMNTOOLS%\..\..\VC\vcvarsall.bat" %TOOLSET% + +rem : assume our root Qt dir is the current dir +set QT=%CD% + +rem : create the build folder and add the qtbase/bin folder to the PATH +if not exist %QT%\%PLATFORM% mkdir %QT%\%PLATFORM% +if %USE_PREFIX%==1 ( + if not exist %QT%\%PLATFORM%-Build mkdir %QT%\%PLATFORM%-Build + if not exist %QT%\%PLATFORM%\qtbase mkdir %QT%\%PLATFORM%\qtbase + cd %QT%\%PLATFORM%-Build + set QT_PREFIX=-prefix %Qt%\%PLATFORM%\qtbase + set QT_TARGETS=module-qtbase-install_subtargets +) else ( + cd %QT%\%PLATFORM% + set QT_PREFIX= + set QT_TARGETS=module-qtbase +) +set PATH=%CD%\qtbase\bin;%PATH% + +rem : run Qt configure with desired settings +call %QT%\Src\configure.bat -opensource -confirm-license %QT_PREFIX% -mp -opengl desktop -static -debug-and-release -platform win32-msvc2013 -nomake examples -nomake tests + +rem : compile and install module-qtbase +%QT%\jom\jom %QT_TARGETS% diff --git a/windows/include/LibLevelDB/port/port.h b/windows/include/LibLevelDB/port/port.h new file mode 100644 index 000000000..ce7ae19c4 --- /dev/null +++ b/windows/include/LibLevelDB/port/port.h @@ -0,0 +1,23 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_PORT_PORT_H_ +#define STORAGE_LEVELDB_PORT_PORT_H_ + +#include + +// Include the appropriate platform specific file below. If you are +// porting to a new platform, see "port_example.h" for documentation +// of what the new port_.h file must provide. +#if defined(LEVELDB_PLATFORM_POSIX) +# include "port/port_posix.h" +#elif defined(LEVELDB_PLATFORM_CHROMIUM) +# include "port/port_chromium.h" +#elif defined(LEVELDB_PLATFORM_ANDROID) +# include "port/port_android.h" +#elif defined(LEVELDB_PLATFORM_WINDOWS) +# include "port/port_win.h" +#endif + +#endif // STORAGE_LEVELDB_PORT_PORT_H_ diff --git a/windows/include/LibLevelDB/unistd.h b/windows/include/LibLevelDB/unistd.h new file mode 100644 index 000000000..e69de29bb diff --git a/windows/include/LibMiniUPnPc/miniupnpcstrings.h b/windows/include/LibMiniUPnPc/miniupnpcstrings.h new file mode 100644 index 000000000..364342ecf --- /dev/null +++ b/windows/include/LibMiniUPnPc/miniupnpcstrings.h @@ -0,0 +1,14 @@ +/* $Id: miniupnpcstrings.h.in,v 1.4 2011/01/04 11:41:53 nanard Exp $ */ +/* Project: miniupnp + * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ + * Author: Thomas Bernard + * Copyright (c) 2005-2011 Thomas Bernard + * This software is subjects to the conditions detailed + * in the LICENCE file provided within this distribution */ +#ifndef MINIUPNPCSTRINGS_H_INCLUDED +#define MINIUPNPCSTRINGS_H_INCLUDED + +#define OS_STRING "Windows" +#define MINIUPNPC_VERSION_STRING "1.9" + +#endif diff --git a/windows/moc.lua b/windows/moc.lua new file mode 100644 index 000000000..8a129eeae --- /dev/null +++ b/windows/moc.lua @@ -0,0 +1,38 @@ + +local function quote(x) + return '"' .. x .. '"' +end + +local function toForward(x) + return x:gsub('\\', '/') +end + +-- arguments are in this order +local cmd = arg[1] +local outFile = arg[2] +local includes = toForward(arg[3]) +local defines = arg[4] +local inFile = arg[5] + +-- build list of includes +local includes2 = "" +for i in string.gmatch(includes, "[^;]+") do + includes2 = includes2.." -I "..quote(i) +end +includes = includes2; + +-- build list of defines +local defines2 = "" +for i in string.gmatch(defines, "[^;]+") do + defines2 = defines2.." -D"..i +end +defines = defines2 + +-- moc doesn't compile boost correctly, so skip those headers +workarounds=' -DBOOST_MP_CPP_INT_HPP -DBOOST_THREAD_WEK01082003_HPP' + +-- build command +cmd = quote(cmd).." -o "..quote(outFile)..includes..defines..workarounds..' '..quote(inFile) +print(cmd) +os.execute(quote(cmd)) + diff --git a/windows/qt_plugin_import.cpp b/windows/qt_plugin_import.cpp index 39acb25c9..b3afd8a4e 100644 --- a/windows/qt_plugin_import.cpp +++ b/windows/qt_plugin_import.cpp @@ -1,4 +1,4 @@ // This file is autogenerated by qmake. It imports static plugin classes for // static plugins specified using QTPLUGIN and QT_PLUGIN_CLASS. variables. -#include +#include Q_IMPORT_PLUGIN(QWindowsIntegrationPlugin) diff --git a/windows/stdafx.cpp b/windows/stdafx.cpp new file mode 100644 index 000000000..d3c5fc299 --- /dev/null +++ b/windows/stdafx.cpp @@ -0,0 +1,21 @@ +/* + 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 . +*/ +/** @file stdafx.cpp + * @author Tim Hughes + * @date 2014 + */ + diff --git a/windows/stdafx.h b/windows/stdafx.h new file mode 100644 index 000000000..1ad98d97b --- /dev/null +++ b/windows/stdafx.h @@ -0,0 +1,42 @@ +/* + 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 . +*/ +/** @file stdafx.h + * @author Tim Hughes + * @date 2014 + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include