diff --git a/alethzero/MainWin.cpp b/alethzero/MainWin.cpp index cdde0f147..59aba538f 100644 --- a/alethzero/MainWin.cpp +++ b/alethzero/MainWin.cpp @@ -189,6 +189,9 @@ Main::Main(QWidget *parent) : #endif m_servers.append(QString::fromStdString(Host::pocHost() + ":30303")); + if (!dev::contents(getDataDir() + "/genesis.json").empty()) + CanonBlockChain::setGenesis(contentsString(getDataDir() + "/genesis.json")); + cerr << "State root: " << CanonBlockChain::genesis().stateRoot() << endl; auto block = CanonBlockChain::createGenesisBlock(); cerr << "Block Hash: " << CanonBlockChain::genesis().hash() << endl; @@ -375,10 +378,17 @@ NetworkPreferences Main::netPrefs() const publicIP.clear(); } + NetworkPreferences ret; + if (isPublicAddress(publicIP)) - return NetworkPreferences(publicIP, listenIP, ui->port->value(), ui->upnp->isChecked()); + ret = NetworkPreferences(publicIP, listenIP, ui->port->value(), ui->upnp->isChecked()); else - return NetworkPreferences(listenIP, ui->port->value(), ui->upnp->isChecked()); + ret = NetworkPreferences(listenIP, ui->port->value(), ui->upnp->isChecked()); + + ret.discovery = m_privateChain.isEmpty(); + ret.pin = m_privateChain.isEmpty(); + + return ret; } void Main::onKeysChanged() @@ -773,6 +783,30 @@ void Main::writeSettings() s.setValue("windowState", saveState()); } +void Main::setPrivateChain(QString const& _private, bool _forceConfigure) +{ + if (m_privateChain == _private && !_forceConfigure) + return; + + m_privateChain = _private; + ui->usePrivate->setChecked(!m_privateChain.isEmpty()); + + CanonBlockChain::forceGenesisExtraData(m_privateChain.isEmpty() ? bytes() : sha3(m_privateChain.toStdString()).asBytes()); + + // rejig blockchain now. + writeSettings(); + ui->mine->setChecked(false); + ui->net->setChecked(false); + web3()->stopNetwork(); + + web3()->setNetworkPreferences(netPrefs()); + ethereum()->reopenChain(); + + readSettings(true); + installWatches(); + refreshAll(); +} + Secret Main::retrieveSecret(Address const& _address) const { while (true) @@ -849,8 +883,7 @@ void Main::readSettings(bool _skipGeometry) ui->listenIP->setText(s.value("listenIP", "").toString()); ui->port->setValue(s.value("port", ui->port->value()).toInt()); ui->nameReg->setText(s.value("nameReg", "").toString()); - m_privateChain = s.value("privateChain", "").toString(); - ui->usePrivate->setChecked(m_privateChain.size()); + setPrivateChain(s.value("privateChain", "").toString()); ui->verbosity->setValue(s.value("verbosity", 1).toInt()); #if ETH_EVMJIT // We care only if JIT is enabled. Otherwise it can cause misconfiguration. @@ -1033,21 +1066,15 @@ void Main::on_exportState_triggered() void Main::on_usePrivate_triggered() { + QString pc; if (ui->usePrivate->isChecked()) { - m_privateChain = QInputDialog::getText(this, "Enter Name", "Enter the name of your private chain", QLineEdit::Normal, QString("NewChain-%1").arg(time(0))); - if (m_privateChain.isEmpty()) - { - if (ui->usePrivate->isChecked()) - ui->usePrivate->setChecked(false); - else - // was cancelled. - return; - } + bool ok; + pc = QInputDialog::getText(this, "Enter Name", "Enter the name of your private chain", QLineEdit::Normal, QString("NewChain-%1").arg(time(0)), &ok); + if (!ok) + return; } - else - m_privateChain.clear(); - on_killBlockchain_triggered(); + setPrivateChain(pc); } void Main::on_vmInterpreter_triggered() { VMFactory::setKind(VMKind::Interpreter); } @@ -2012,7 +2039,7 @@ void Main::on_net_triggered() { web3()->setIdealPeerCount(ui->idealPeers->value()); web3()->setNetworkPreferences(netPrefs(), ui->dropPeers->isChecked()); - ethereum()->setNetworkId(m_privateChain.size() ? sha3(m_privateChain.toStdString()) : h256()); + ethereum()->setNetworkId((h256)(u256)(int)c_network); web3()->startNetwork(); ui->downloadView->setEthereum(ethereum()); ui->enode->setText(QString::fromStdString(web3()->enode())); diff --git a/alethzero/MainWin.h b/alethzero/MainWin.h index 0e944b042..f06048c02 100644 --- a/alethzero/MainWin.h +++ b/alethzero/MainWin.h @@ -226,6 +226,8 @@ private: void readSettings(bool _skipGeometry = false); void writeSettings(); + void setPrivateChain(QString const& _private, bool _forceConfigure = false); + unsigned installWatch(dev::eth::LogFilter const& _tf, WatchHandler const& _f); unsigned installWatch(dev::h256 _tf, WatchHandler const& _f); void uninstallWatch(unsigned _w); diff --git a/eth/main.cpp b/eth/main.cpp index 45902c96c..e47d0b0fd 100644 --- a/eth/main.cpp +++ b/eth/main.cpp @@ -121,8 +121,13 @@ void help() << "Usage eth [OPTIONS]" << endl << "Options:" << endl << endl << "Client mode (default):" << endl + << " --olympic Use the Olympic (0.9) protocol." << endl + << " --frontier Use the Frontier (1.0) protocol." << endl + << " --private Use a private chain." << endl << " -o,--mode Start a full node or a peer node (default: full)." << endl +#if ETH_JSCONSOLE || !ETH_TRUE << " -i,--interactive Enter interactive mode (default: non-interactive)." << endl +#endif #if ETH_JSONRPC || !ETH_TRUE << " -j,--json-rpc Enable JSON-RPC server (default: off)." << endl << " --json-rpc-port Specify JSON-RPC server port (implies '-j', default: " << SensibleHttpPort << ")." << endl @@ -139,7 +144,6 @@ void help() << " --master Give the master password for the key store." << endl << " --password Give a password for a private key." << endl << " --sentinel Set the sentinel for reporting bad blocks or chain issues." << endl - << " --prime Specify n as the 6 digit prime number to start Frontier." << endl << endl << "Client transacting:" << endl /*<< " -B,--block-fees Set the block fee profit in the reference unit e.g. ยข (default: 15)." << endl @@ -168,10 +172,10 @@ void help() << " --listen Listen on the given port for incoming connections (default: 30303)." << endl << " -r,--remote (:) Connect to remote host (default: none)." << endl << " --port Connect to remote port (default: 30303)." << endl - << " --network-id Only connect to other hosts with this network id (default:0)." << endl + << " --network-id Only connect to other hosts with this network id." << endl << " --upnp Use UPnP for NAT (default: on)." << endl - << " --no-discovery Disable Node discovery. (experimental)" << endl - << " --pin Only connect to required (trusted) peers. (experimental)" << endl + << " --no-discovery Disable Node discovery." << endl + << " --pin Only connect to required (trusted) peers." << endl // << " --require-peers List of required (trusted) peers. (experimental)" << endl << endl; MinerCLI::streamHelp(cout); @@ -196,9 +200,6 @@ void help() << " -v,--verbosity <0 - 9> Set the log verbosity from 0 to 9 (default: 8)." << endl << " -V,--version Show the version and exit." << endl << " -h,--help Show this help message and exit." << endl -#if ETH_JSCONSOLE || !ETH_TRUE - << " --console Use interactive javascript console" << endl -#endif ; exit(0); } @@ -1068,8 +1069,8 @@ int main(int argc, char** argv) /// Operating mode. OperationMode mode = OperationMode::Node; string dbPath; - unsigned prime = 0; - bool yesIReallyKnowWhatImDoing = false; +// unsigned prime = 0; +// bool yesIReallyKnowWhatImDoing = false; /// File name for import/export. string filename; @@ -1083,10 +1084,14 @@ int main(int argc, char** argv) /// General params for Node operation NodeMode nodeMode = NodeMode::Full; bool interactive = false; -#if ETH_JSONRPC +#if ETH_JSONRPC || !ETH_TRUE int jsonrpc = -1; #endif string jsonAdmin; + string genesisJSON; + dev::eth::Network releaseNetwork = c_network; + string privateChain; + bool upnp = true; WithExisting withExisting = WithExisting::Trust; string sentinel; @@ -1192,7 +1197,7 @@ int main(int argc, char** argv) mode = OperationMode::Export; filename = argv[++i]; } - else if (arg == "--prime" && i + 1 < argc) +/* else if (arg == "--prime" && i + 1 < argc) try { prime = stoi(argv[++i]); @@ -1204,7 +1209,7 @@ int main(int argc, char** argv) } else if (arg == "--yes-i-really-know-what-im-doing") yesIReallyKnowWhatImDoing = true; - else if (arg == "--sentinel" && i + 1 < argc) +*/ else if (arg == "--sentinel" && i + 1 < argc) sentinel = argv[++i]; else if (arg == "--mine-on-wrong-chain") mineOnWrongChain = true; @@ -1253,6 +1258,15 @@ int main(int argc, char** argv) cerr << "Bad " << arg << " option: " << argv[i] << endl; return -1; } + else if (arg == "--private" && i + 1 < argc) + try { + privateChain = argv[++i]; + } + catch (...) + { + cerr << "Bad " << arg << " option: " << argv[i] << endl; + return -1; + } else if (arg == "-K" || arg == "--kill-blockchain" || arg == "--kill") withExisting = WithExisting::Kill; else if (arg == "-R" || arg == "--rebuild") @@ -1306,11 +1320,11 @@ int main(int argc, char** argv) } else if ((arg == "-d" || arg == "--path" || arg == "--db-path") && i + 1 < argc) dbPath = argv[++i]; - else if (arg == "--genesis-nonce" && i + 1 < argc) + else if (arg == "--genesis-json" && i + 1 < argc) { try { - CanonBlockChain::setGenesisNonce(Nonce(argv[++i])); + genesisJSON = contentsString(argv[++i]); } catch (...) { @@ -1318,6 +1332,10 @@ int main(int argc, char** argv) return -1; } } + else if (arg == "--frontier") + releaseNetwork = eth::Network::Frontier; + else if (arg == "--olympic") + releaseNetwork = eth::Network::Olympic; /* else if ((arg == "-B" || arg == "--block-fees") && i + 1 < argc) { try @@ -1412,9 +1430,9 @@ int main(int argc, char** argv) pinning = true; else if (arg == "-f" || arg == "--force-mining") forceMining = true; - else if (arg == "-i" || arg == "--interactive") + else if (arg == "--old-interactive") interactive = true; -#if ETH_JSONRPC +#if ETH_JSONRPC || !ETH_TRUE else if ((arg == "-j" || arg == "--json-rpc")) jsonrpc = jsonrpc == -1 ? SensibleHttpPort : jsonrpc; else if (arg == "--json-rpc-port" && i + 1 < argc) @@ -1422,8 +1440,8 @@ int main(int argc, char** argv) else if (arg == "--json-admin" && i + 1 < argc) jsonAdmin = argv[++i]; #endif -#if ETH_JSCONSOLE - else if (arg == "--console") +#if ETH_JSCONSOLE || !ETH_TRUE + else if (arg == "-i" || arg == "--interactive" || arg == "--console") useConsole = true; #endif else if ((arg == "-v" || arg == "--verbosity") && i + 1 < argc) @@ -1471,6 +1489,13 @@ int main(int argc, char** argv) } } + // Set up all the chain config stuff. + resetNetwork(releaseNetwork); + if (!privateChain.empty()) + CanonBlockChain::forceGenesisExtraData(sha3(privateChain).asBytes()); + if (!genesisJSON.empty()) + CanonBlockChain::setGenesis(genesisJSON); + if (g_logVerbosity > 0) { cout << EthGrayBold "(++)Ethereum" EthReset << endl; @@ -1529,8 +1554,9 @@ int main(int argc, char** argv) StructuredLogger::get().initialize(structuredLogging, structuredLoggingFormat, structuredLoggingURL); auto netPrefs = publicIP.empty() ? NetworkPreferences(listenIP ,listenPort, upnp) : NetworkPreferences(publicIP, listenIP ,listenPort, upnp); - netPrefs.discovery = !disableDiscovery; - netPrefs.pin = pinning; + netPrefs.discovery = privateChain.empty() && !disableDiscovery; + netPrefs.pin = pinning || !privateChain.empty(); + auto nodesState = contents((dbPath.size() ? dbPath : getDataDir()) + "/network.rlp"); dev::WebThreeDirect web3( WebThreeDirect::composeClientVersion("++eth", clientName), @@ -1632,7 +1658,7 @@ int main(int argc, char** argv) cout << imported << " imported in " << e << " seconds at " << (round(imported * 10 / e) / 10) << " blocks/s (#" << web3.ethereum()->number() << ")" << endl; return 0; } - +/* if (c_network == eth::Network::Frontier && !yesIReallyKnowWhatImDoing) { auto pd = contents(getDataDir() + "primes"); @@ -1652,7 +1678,7 @@ int main(int argc, char** argv) primes.insert(prime); writeFile(getDataDir() + "primes", rlp(primes)); } - +*/ if (keyManager.exists()) { if (masterPassword.empty() || !keyManager.load(masterPassword)) diff --git a/ethminer/MinerAux.h b/ethminer/MinerAux.h index 3d4457483..65dd0111e 100644 --- a/ethminer/MinerAux.h +++ b/ethminer/MinerAux.h @@ -398,6 +398,12 @@ private: cdebug << genesis.boundary(); GenericFarm f; + map::SealerDescriptor> sealers; + sealers["cpu"] = GenericFarm::SealerDescriptor{&EthashCPUMiner::instances, [](GenericMiner::ConstructionInfo ci){ return new EthashCPUMiner(ci); }}; +#if ETH_ETHASHCL + sealers["opencl"] = GenericFarm::SealerDescriptor{&EthashGPUMiner::instances, [](GenericMiner::ConstructionInfo ci){ return new EthashGPUMiner(ci); }}; +#endif + f.setSealers(sealers); f.onSolutionFound([&](EthashProofOfWork::Solution) { return false; }); string platformInfo = _m == MinerType::CPU ? "CPU" : "GPU";//EthashProofOfWork::CPUMiner::platformInfo() : _m == MinerType::GPU ? EthashProofOfWork::GPUMiner::platformInfo() : ""; diff --git a/exp/main.cpp b/exp/main.cpp index 0b740e526..43396c5e5 100644 --- a/exp/main.cpp +++ b/exp/main.cpp @@ -284,6 +284,21 @@ int main() return 0; } #elif 1 + +int main() +{ + bytes tx = fromHex("f84c01028332dcd58004801ba024843272ee176277535489859cbd275686023fe64aabd158b6fcdf2ae6a1ab6ba02f252a5016a48e5ec8d17aefaf4324d29b9e123fa623dc5a60539b3ad3610c95"); + Transaction t(tx, CheckTransaction::None); + Public p = recover(t.signature(), t.sha3(WithoutSignature)); + cnote << t.signature().r; + cnote << t.signature().s; + cnote << t.signature().v; + cnote << p; + cnote << toAddress(p); + cnote << t.sender(); +} + +#elif 0 void mine(State& s, BlockChain const& _bc, SealEngineFace* _se) { s.commitToMine(_bc); diff --git a/libdevcore/Common.cpp b/libdevcore/Common.cpp index 6df0d4d69..d97b44445 100644 --- a/libdevcore/Common.cpp +++ b/libdevcore/Common.cpp @@ -28,7 +28,7 @@ using namespace dev; namespace dev { -char const* Version = "0.9.30"; +char const* Version = "0.9.34"; const u256 UndefinedU256 = ~(u256)0; diff --git a/libdevcore/Exceptions.h b/libdevcore/Exceptions.h index b0bab7d81..b166389f7 100644 --- a/libdevcore/Exceptions.h +++ b/libdevcore/Exceptions.h @@ -56,7 +56,7 @@ DEV_SIMPLE_EXCEPTION(BadHexCharacter); DEV_SIMPLE_EXCEPTION(NoNetworking); DEV_SIMPLE_EXCEPTION(NoUPnPDevice); DEV_SIMPLE_EXCEPTION(RootNotFound); -DEV_SIMPLE_EXCEPTION(BadRoot); +struct BadRoot: virtual Exception { public: BadRoot(h256 const& _root): Exception("BadRoot " + _root.hex()), root(_root) {} h256 root; }; DEV_SIMPLE_EXCEPTION(FileError); DEV_SIMPLE_EXCEPTION(Overflow); DEV_SIMPLE_EXCEPTION(FailedInvariant); diff --git a/libdevcore/TrieDB.h b/libdevcore/TrieDB.h index 91d68ebd3..47f359e6d 100644 --- a/libdevcore/TrieDB.h +++ b/libdevcore/TrieDB.h @@ -96,7 +96,7 @@ public: /// True if the trie is initialised but empty (i.e. that the DB contains the root node which is empty). bool isEmpty() const { return m_root == c_shaNull && node(m_root).size(); } - h256 const& root() const { if (node(m_root).empty()) BOOST_THROW_EXCEPTION(BadRoot()); /*std::cout << "Returning root as " << ret << " (really " << m_root << ")" << std::endl;*/ return m_root; } // patch the root in the case of the empty trie. TODO: handle this properly. + h256 const& root() const { if (node(m_root).empty()) BOOST_THROW_EXCEPTION(BadRoot(m_root)); /*std::cout << "Returning root as " << ret << " (really " << m_root << ")" << std::endl;*/ return m_root; } // patch the root in the case of the empty trie. TODO: handle this properly. std::string at(bytes const& _key) const { return at(&_key); } std::string at(bytesConstRef _key) const; diff --git a/libdevcrypto/Common.cpp b/libdevcrypto/Common.cpp index d64de835c..7cc16bd03 100644 --- a/libdevcrypto/Common.cpp +++ b/libdevcrypto/Common.cpp @@ -193,17 +193,23 @@ bytes dev::decryptAES128CTR(bytesConstRef _k, h128 const& _iv, bytesConstRef _ci } } +static const Public c_zeroKey("3f17f1962b36e491b30a40b2405849e597ba5fb5"); + Public dev::recover(Signature const& _sig, h256 const& _message) { + Public ret; #ifdef ETH_HAVE_SECP256K1 bytes o(65); int pubkeylen; if (!secp256k1_ecdsa_recover_compact(_message.data(), h256::size, _sig.data(), o.data(), &pubkeylen, false, _sig[64])) return Public(); - return FixedHash<64>(o.data()+1, Public::ConstructFromPointer); + ret = FixedHash<64>(o.data() + 1, Public::ConstructFromPointer); #else - return s_secp256k1pp.recover(_sig, _message.ref()); + ret = s_secp256k1pp.recover(_sig, _message.ref()); #endif + if (ret == c_zeroKey) + return Public(); + return ret; } Signature dev::sign(Secret const& _k, h256 const& _hash) diff --git a/libdevcrypto/CryptoPP.cpp b/libdevcrypto/CryptoPP.cpp index 5ca93af4d..2034069aa 100644 --- a/libdevcrypto/CryptoPP.cpp +++ b/libdevcrypto/CryptoPP.cpp @@ -287,6 +287,8 @@ Public Secp256k1PP::recover(Signature _signature, bytesConstRef _message) { // todo: make generator member p = m_curve.CascadeMultiply(u2, x, u1, m_params.GetSubgroupGenerator()); + if (p.identity) + return Public(); m_curve.EncodePoint(recoveredbytes, p, false); } memcpy(recovered.data(), &recoveredbytes[1], 64); diff --git a/libethcore/BlockInfo.cpp b/libethcore/BlockInfo.cpp index 8e2e2c4c4..60fb13ba9 100644 --- a/libethcore/BlockInfo.cpp +++ b/libethcore/BlockInfo.cpp @@ -183,28 +183,30 @@ void BlockInfo::populateFromParent(BlockInfo const& _parent) { m_stateRoot = _parent.stateRoot(); m_number = _parent.m_number + 1; + m_parentHash = _parent.m_hash; m_gasLimit = selectGasLimit(_parent); m_gasUsed = 0; m_difficulty = calculateDifficulty(_parent); - m_parentHash = _parent.m_hash; } u256 BlockInfo::selectGasLimit(BlockInfo const& _parent) const { - if (!m_parentHash) - return c_genesisGasLimit; + static const u256 c_gasFloorTarget = 3141592; + + if (!m_number) + throw GenesisBlockCannotBeCalculated(); else // target minimum of 3141592 - if (_parent.m_gasLimit < c_genesisGasLimit) - return min(c_genesisGasLimit, _parent.m_gasLimit + _parent.m_gasLimit / c_gasLimitBoundDivisor - 1); + if (_parent.m_gasLimit < c_gasFloorTarget) + return min(c_gasFloorTarget, _parent.m_gasLimit + _parent.m_gasLimit / c_gasLimitBoundDivisor - 1); else - return max(c_genesisGasLimit, _parent.m_gasLimit - _parent.m_gasLimit / c_gasLimitBoundDivisor + 1 + (_parent.m_gasUsed * 6 / 5) / c_gasLimitBoundDivisor); + return max(c_gasFloorTarget, _parent.m_gasLimit - _parent.m_gasLimit / c_gasLimitBoundDivisor + 1 + (_parent.m_gasUsed * 6 / 5) / c_gasLimitBoundDivisor); } u256 BlockInfo::calculateDifficulty(BlockInfo const& _parent) const { - if (!m_parentHash) - return (u256)c_genesisDifficulty; + if (!m_number) + throw GenesisBlockCannotBeCalculated(); else return max(c_minimumDifficulty, m_timestamp >= _parent.m_timestamp + c_durationLimit ? _parent.m_difficulty - (_parent.m_difficulty / c_difficultyBoundDivisor) : (_parent.m_difficulty + (_parent.m_difficulty / c_difficultyBoundDivisor))); } diff --git a/libethcore/BlockInfo.h b/libethcore/BlockInfo.h index cf3a9820e..f48461254 100644 --- a/libethcore/BlockInfo.h +++ b/libethcore/BlockInfo.h @@ -53,6 +53,7 @@ enum BlockDataType }; DEV_SIMPLE_EXCEPTION(NoHashRecorded); +DEV_SIMPLE_EXCEPTION(GenesisBlockCannotBeCalculated); /** @brief Encapsulation of a block header. * Class to contain all of a block header's data. It is able to parse a block header and populate diff --git a/libethcore/Common.cpp b/libethcore/Common.cpp index 05cc45280..16f315756 100644 --- a/libethcore/Common.cpp +++ b/libethcore/Common.cpp @@ -29,6 +29,7 @@ #include #include #include "Exceptions.h" +#include "Params.h" #include "BlockInfo.h" using namespace std; using namespace dev; @@ -51,11 +52,23 @@ const unsigned c_databaseVersionModifier = 0; #endif #if ETH_FRONTIER -Network const c_network = Network::Frontier; +Network c_network = resetNetwork(Network::Frontier); #else -Network const c_network = Network::Olympic; +Network c_network = resetNetwork(Network::Olympic); #endif +Network resetNetwork(Network _n) +{ + c_network = _n; + c_maximumExtraDataSize = c_network == Network::Olympic ? 1024 : 32; + c_minGasLimit = c_network == Network::Turbo ? 100000000 : 125000; + c_gasLimitBoundDivisor = 1024; + c_minimumDifficulty = 131072; + c_difficultyBoundDivisor = 2048; + c_durationLimit = c_network == Network::Turbo ? 2 : c_network == Network::Olympic ? 8 : 12; + return _n; +} + const unsigned c_databaseVersion = c_databaseBaseVersion + (c_databaseVersionModifier << 8) + (23 << 9); vector> const& units() diff --git a/libethcore/Common.h b/libethcore/Common.h index 116e1d5ed..c59b28f15 100644 --- a/libethcore/Common.h +++ b/libethcore/Common.h @@ -50,7 +50,9 @@ enum class Network Frontier = 1, Turbo = 2 }; -extern const Network c_network; +extern Network c_network; + +Network resetNetwork(Network _n); /// User-friendly string representation of the amount _b in wei. std::string formatBalance(bigint const& _b); diff --git a/libethcore/EthashSealEngine.cpp b/libethcore/EthashSealEngine.cpp index 4803d9ed4..5eacfc713 100644 --- a/libethcore/EthashSealEngine.cpp +++ b/libethcore/EthashSealEngine.cpp @@ -63,9 +63,11 @@ void EthashSealEngine::onSealGenerated(std::function const& { m_farm.onSolutionFound([=](EthashProofOfWork::Solution const& sol) { - cdebug << m_farm.work().seedHash << m_farm.work().headerHash << sol.nonce << EthashAux::eval(m_farm.work().seedHash, m_farm.work().headerHash, sol.nonce).value; +// cdebug << m_farm.work().seedHash << m_farm.work().headerHash << sol.nonce << EthashAux::eval(m_farm.work().seedHash, m_farm.work().headerHash, sol.nonce).value; m_sealing.m_mixHash = sol.mixHash; m_sealing.m_nonce = sol.nonce; + if (!m_sealing.preVerify()) + return false; RLPStream ret; m_sealing.streamRLP(ret); _f(ret.out()); diff --git a/libethcore/Miner.h b/libethcore/Miner.h index 51d5f0b0a..42d17ab53 100644 --- a/libethcore/Miner.h +++ b/libethcore/Miner.h @@ -58,6 +58,8 @@ public: using Solution = typename PoW::Solution; using Miner = GenericMiner; + virtual ~GenericFarmFace() {} + /** * @brief Called from a Miner to note a WorkPackage has a solution. * @param _p The solution. diff --git a/libethcore/Params.cpp b/libethcore/Params.cpp index 0fea39b30..a3a061f31 100644 --- a/libethcore/Params.cpp +++ b/libethcore/Params.cpp @@ -29,14 +29,12 @@ namespace eth { //--- BEGIN: AUTOGENERATED FROM github.com/ethereum/common/params.json -u256 const c_genesisDifficulty = 131072; -u256 const c_maximumExtraDataSize = 1024; -u256 const c_genesisGasLimit = c_network == Network::Turbo ? 100000000 : 3141592; -u256 const c_minGasLimit = c_network == Network::Turbo ? 100000000 : 125000; -u256 const c_gasLimitBoundDivisor = 1024; -u256 const c_minimumDifficulty = 131072; -u256 const c_difficultyBoundDivisor = 2048; -u256 const c_durationLimit = c_network == Network::Turbo ? 2 : c_network == Network::Olympic ? 8 : 12; +u256 c_maximumExtraDataSize; +u256 c_minGasLimit; +u256 c_gasLimitBoundDivisor; +u256 c_minimumDifficulty; +u256 c_difficultyBoundDivisor; +u256 c_durationLimit; //--- END: AUTOGENERATED FROM /feeStructure.json } diff --git a/libethcore/Params.h b/libethcore/Params.h index 3520b2f1b..07cbc36d5 100644 --- a/libethcore/Params.h +++ b/libethcore/Params.h @@ -29,14 +29,13 @@ namespace eth { //--- BEGIN: AUTOGENERATED FROM /feeStructure.json -extern u256 const c_genesisGasLimit; -extern u256 const c_minGasLimit; -extern u256 const c_gasLimitBoundDivisor; -extern u256 const c_genesisDifficulty; -extern u256 const c_minimumDifficulty; -extern u256 const c_difficultyBoundDivisor; -extern u256 const c_durationLimit; -extern u256 const c_maximumExtraDataSize; +extern u256 c_minGasLimit; +extern u256 c_gasLimitBoundDivisor; +extern u256 c_minimumDifficulty; +extern u256 c_difficultyBoundDivisor; +extern u256 c_durationLimit; +extern u256 c_maximumExtraDataSize; +//--- END: AUTOGENERATED FROM /feeStructure.json } } diff --git a/libethcore/Sealer.h b/libethcore/Sealer.h index 137659dfc..71651fafc 100644 --- a/libethcore/Sealer.h +++ b/libethcore/Sealer.h @@ -38,6 +38,8 @@ class BlockInfo; class SealEngineFace { public: + virtual ~SealEngineFace() {} + virtual std::string name() const = 0; virtual unsigned revision() const = 0; virtual unsigned sealFields() const = 0; diff --git a/libethereum/BlockChain.cpp b/libethereum/BlockChain.cpp index 9037f32e0..805347aaf 100644 --- a/libethereum/BlockChain.cpp +++ b/libethereum/BlockChain.cpp @@ -147,7 +147,12 @@ static const unsigned c_minCacheSize = 1024 * 1024 * 32; #endif BlockChain::BlockChain(bytes const& _genesisBlock, std::unordered_map const& _genesisState, std::string const& _path, WithExisting _we, ProgressCallback const& _p): - m_genesisState(_genesisState) + m_dbPath(_path) +{ + open(_genesisBlock, _genesisState, _path, _we, _p); +} + +void BlockChain::open(bytes const& _genesisBlock, std::unordered_map const& _genesisState, std::string const& _path, WithExisting _we, ProgressCallback const& _p) { // initialise deathrow. m_cacheUsage.resize(c_collectionQueueSize); @@ -156,11 +161,12 @@ BlockChain::BlockChain(bytes const& _genesisBlock, std::unordered_mapm_dbPath : _path; string chainPath = path + "/" + toHex(m_genesisHash.ref().cropped(0, 4)); @@ -220,8 +226,9 @@ unsigned BlockChain::open(std::string const& _path, WithExisting _we) if (_we != WithExisting::Verify && !details(m_genesisHash)) { + BlockInfo gb(m_genesisBlock); // Insert details of genesis block. - m_details[m_genesisHash] = BlockDetails(0, c_genesisDifficulty, h256(), {}); + m_details[m_genesisHash] = BlockDetails(0, gb.difficulty(), h256(), {}); auto r = m_details[m_genesisHash].rlp(); m_extrasDB->Put(m_writeOptions, toSlice(m_genesisHash, ExtraDetails), (ldb::Slice)dev::ref(r)); } @@ -243,12 +250,22 @@ unsigned BlockChain::open(std::string const& _path, WithExisting _we) void BlockChain::close() { cnote << "Closing blockchain DB"; + // Not thread safe... delete m_extrasDB; delete m_blocksDB; m_lastBlockHash = m_genesisHash; m_lastBlockNumber = 0; m_details.clear(); m_blocks.clear(); + m_logBlooms.clear(); + m_receipts.clear(); + m_transactionAddresses.clear(); + m_blockHashes.clear(); + m_blocksBlooms.clear(); + m_cacheUsage.clear(); + m_inUse.clear(); + m_lastLastHashes.clear(); + m_lastLastHashesNumber = (unsigned)-1; } void BlockChain::rebuild(std::string const& _path, std::function const& _progress, bool _prepPoW) @@ -293,7 +310,7 @@ void BlockChain::rebuild(std::string const& _path, std::functionPut(m_writeOptions, toSlice(m_lastBlockHash, ExtraDetails), (ldb::Slice)dev::ref(m_details[m_lastBlockHash].rlp())); @@ -586,9 +603,11 @@ ImportRoute BlockChain::import(VerifiedBlockRef const& _block, OverlayDB const& #endif } #if ETH_CATCH - catch (BadRoot&) + catch (BadRoot& ex) { - cwarn << "BadRoot error. Retrying import later."; + cwarn << "*** BadRoot error! Trying to import" << _block.info.hash() << "needed root" << ex.root; + cwarn << _block.info; + // Attempt in import later. BOOST_THROW_EXCEPTION(FutureTime()); } catch (Exception& ex) @@ -1245,6 +1264,7 @@ State BlockChain::genesisState(OverlayDB const& _db) dev::eth::commit(m_genesisState, ret.m_state); // bit horrible. maybe consider a better way of constructing it? ret.m_state.db()->commit(); // have to use this db() since it's the one that has been altered with the above commit. ret.m_previousBlock = BlockInfo(&m_genesisBlock); + ret.resetCurrent(); return ret; } diff --git a/libethereum/BlockChain.h b/libethereum/BlockChain.h index 415581f29..3a3c18f98 100644 --- a/libethereum/BlockChain.h +++ b/libethereum/BlockChain.h @@ -106,8 +106,8 @@ public: BlockChain(bytes const& _genesisBlock, StateDefinition const& _genesisState, std::string const& _path, WithExisting _we = WithExisting::Trust, ProgressCallback const& _p = ProgressCallback()); ~BlockChain(); - /// Attempt a database re-open. - void reopen(std::string const& _path, WithExisting _we = WithExisting::Trust) { close(); open(_path, _we); } + /// Reopen everything. + virtual void reopen(WithExisting _we = WithExisting::Trust, ProgressCallback const& _pc = ProgressCallback()) { close(); open(m_genesisBlock, m_genesisState, m_dbPath, _we, _pc); } /// (Potentially) renders invalid existing bytesConstRef returned by lastBlock. /// To be called from main loop every 100ms or so. @@ -291,7 +291,11 @@ public: protected: static h256 chunkId(unsigned _level, unsigned _index) { return h256(_index * 0xff + _level); } - unsigned open(std::string const& _path, WithExisting _we = WithExisting::Trust); + /// Initialise everything and open the database. + void open(bytes const& _genesisBlock, std::unordered_map const& _genesisState, std::string const& _path, WithExisting _we, ProgressCallback const& _p); + /// Open the database. + unsigned openDatabase(std::string const& _path, WithExisting _we = WithExisting::Trust); + /// Finalise everything and close the database. void close(); template T queryExtras(K const& _h, std::unordered_map& _m, boost::shared_mutex& _x, T const& _n, ldb::DB* _extrasDB = nullptr) const @@ -373,6 +377,8 @@ protected: std::function m_onBad; ///< Called if we have a block that doesn't verify. + std::string m_dbPath; + friend std::ostream& operator<<(std::ostream& _out, BlockChain const& _bc); }; @@ -393,10 +399,10 @@ public: virtual VerifiedBlockRef verifyBlock(bytesConstRef _block, std::function const& _onBad, ImportRequirements::value _ir) const override { VerifiedBlockRef res; - + BlockHeader h; try { - BlockHeader h(_block, (_ir & ImportRequirements::ValidSeal) ? Strictness::CheckEverything : Strictness::QuickNonce); + h = BlockHeader(_block, (_ir & ImportRequirements::ValidSeal) ? Strictness::CheckEverything : Strictness::QuickNonce); h.verifyInternals(_block); if ((_ir & ImportRequirements::Parent) != 0) { @@ -412,6 +418,7 @@ public: ex << errinfo_phase(1); ex << errinfo_now(time(0)); ex << errinfo_block(_block.toBytes()); + ex << errinfo_extraData(h.extraData()); if (_onBad) _onBad(ex); throw; @@ -422,9 +429,10 @@ public: if (_ir && ImportRequirements::UncleBasic) for (auto const& uncle: r[2]) { + BlockHeader h; try { - BlockHeader().populateFromHeader(RLP(uncle.data()), (_ir & ImportRequirements::UncleSeals) ? Strictness::CheckEverything : Strictness::IgnoreSeal); + h.populateFromHeader(RLP(uncle.data()), (_ir & ImportRequirements::UncleSeals) ? Strictness::CheckEverything : Strictness::IgnoreSeal); } catch (Exception& ex) { @@ -432,6 +440,7 @@ public: ex << errinfo_uncleIndex(i); ex << errinfo_now(time(0)); ex << errinfo_block(_block.toBytes()); + ex << errinfo_extraData(h.extraData()); if (_onBad) _onBad(ex); throw; diff --git a/libethereum/CanonBlockChain.cpp b/libethereum/CanonBlockChain.cpp index eee4b98b7..2a7408437 100644 --- a/libethereum/CanonBlockChain.cpp +++ b/libethereum/CanonBlockChain.cpp @@ -38,18 +38,23 @@ using namespace dev; using namespace dev::eth; namespace js = json_spirit; -#define ETH_CATCH 1 - -std::unique_ptr CanonBlockChain::s_genesis; +unique_ptr CanonBlockChain::s_genesis; boost::shared_mutex CanonBlockChain::x_genesis; Nonce CanonBlockChain::s_nonce(u64(42)); -std::string CanonBlockChain::s_genesisStateJSON; +string CanonBlockChain::s_genesisStateJSON; +bytes CanonBlockChain::s_genesisExtraData; CanonBlockChain::CanonBlockChain(std::string const& _path, WithExisting _we, ProgressCallback const& _pc): FullBlockChain(createGenesisBlock(), createGenesisState(), _path, _we, _pc) { } +void CanonBlockChain::reopen(WithExisting _we, ProgressCallback const& _pc) +{ + close(); + open(createGenesisBlock(), createGenesisState(), m_dbPath, _we, _pc); +} + bytes CanonBlockChain::createGenesisBlock() { RLPStream block(3); @@ -63,8 +68,35 @@ bytes CanonBlockChain::createGenesisBlock() stateRoot = state.root(); } + js::mValue val; + json_spirit::read_string(s_genesisStateJSON.empty() ? c_genesisInfo : s_genesisStateJSON, val); + js::mObject genesis = val.get_obj(); + + h256 mixHash(genesis["mixhash"].get_str()); + h256 parentHash(genesis["parentHash"].get_str()); + h160 beneficiary(genesis["coinbase"].get_str()); + u256 difficulty = fromBigEndian(fromHex(genesis["difficulty"].get_str())); + u256 gasLimit = fromBigEndian(fromHex(genesis["gasLimit"].get_str())); + u256 timestamp = fromBigEndian(fromHex(genesis["timestamp"].get_str())); + bytes extraData = fromHex(genesis["extraData"].get_str()); + h64 nonce(genesis["nonce"].get_str()); + block.appendList(15) - << h256() << EmptyListSHA3 << h160() << stateRoot << EmptyTrie << EmptyTrie << LogBloom() << c_genesisDifficulty << 0 << c_genesisGasLimit << 0 << (unsigned)0 << string() << h256() << s_nonce; + << parentHash + << EmptyListSHA3 // sha3(uncles) + << beneficiary + << stateRoot + << EmptyTrie // transactions + << EmptyTrie // receipts + << LogBloom() + << difficulty + << 0 // number + << gasLimit + << 0 // gasUsed + << timestamp + << (s_genesisExtraData.empty() ? extraData : s_genesisExtraData) + << mixHash + << nonce; block.appendRaw(RLPEmptyList); block.appendRaw(RLPEmptyList); return block.out(); @@ -78,12 +110,14 @@ unordered_map CanonBlockChain::createGenesisState() { js::mValue val; json_spirit::read_string(s_genesisStateJSON.empty() ? c_genesisInfo : s_genesisStateJSON, val); - for (auto account: val.get_obj()) + for (auto account: val.get_obj()["alloc"].get_obj()) { u256 balance; if (account.second.get_obj().count("wei")) balance = u256(account.second.get_obj()["wei"].get_str()); - else + else if (account.second.get_obj().count("balance")) + balance = u256(account.second.get_obj()["balance"].get_str()); + else if (account.second.get_obj().count("finney")) balance = u256(account.second.get_obj()["finney"].get_str()) * finney; if (account.second.get_obj().count("code")) { @@ -97,17 +131,17 @@ unordered_map CanonBlockChain::createGenesisState() return s_ret; } -void CanonBlockChain::setGenesisState(std::string const& _json) +void CanonBlockChain::setGenesis(std::string const& _json) { WriteGuard l(x_genesis); s_genesisStateJSON = _json; s_genesis.reset(); } -void CanonBlockChain::setGenesisNonce(Nonce const& _n) +void CanonBlockChain::forceGenesisExtraData(bytes const& _genesisExtraData) { WriteGuard l(x_genesis); - s_nonce = _n; + s_genesisExtraData = _genesisExtraData; s_genesis.reset(); } diff --git a/libethereum/CanonBlockChain.h b/libethereum/CanonBlockChain.h index d79926703..d09108b38 100644 --- a/libethereum/CanonBlockChain.h +++ b/libethereum/CanonBlockChain.h @@ -78,6 +78,9 @@ public: CanonBlockChain(std::string const& _path, WithExisting _we = WithExisting::Trust, ProgressCallback const& _pc = ProgressCallback()); ~CanonBlockChain() {} + /// Reopen everything. + virtual void reopen(WithExisting _we = WithExisting::Trust, ProgressCallback const& _pc = ProgressCallback()); + /// @returns the genesis block header. static Ethash::BlockHeader const& genesis(); @@ -89,18 +92,13 @@ public: /// @note This is slow as it's constructed anew each call. Consider genesis() instead. static std::unordered_map createGenesisState(); - /// Alter the value of the genesis block's nonce. - /// @warning Unless you're very careful, make sure you call this right at the start of the - /// program, before anything has had the chance to use this class at all. - static void setGenesisNonce(Nonce const& _n); - /// Alter all the genesis block's state by giving a JSON string with account details. - /// @TODO implement. /// @warning Unless you're very careful, make sure you call this right at the start of the /// program, before anything has had the chance to use this class at all. - static void setGenesisState(std::string const&); + static void setGenesis(std::string const& _genesisInfoJSON); - // TODO: setGenesisTimestamp, ...ExtraData, ...Difficulty, ...GasLimit, + /// Override the genesis block's extraData field. + static void forceGenesisExtraData(bytes const& _genesisExtraData); private: /// Static genesis info and its lock. @@ -108,6 +106,7 @@ private: static std::unique_ptr s_genesis; static Nonce s_nonce; static std::string s_genesisStateJSON; + static bytes s_genesisExtraData; }; } diff --git a/libethereum/Client.cpp b/libethereum/Client.cpp index 205d4ba96..c477117b5 100644 --- a/libethereum/Client.cpp +++ b/libethereum/Client.cpp @@ -217,6 +217,17 @@ void Client::onBadBlock(Exception& _ex) const report["hints"]["ethashResult"]["value"] = get<0>(*r).hex(); report["hints"]["ethashResult"]["mixHash"] = get<1>(*r).hex(); } + if (bytes const* ed = boost::get_error_info(_ex)) + { + RLP r(*ed); + report["hints"]["extraData"] = toHex(*ed); + try + { + if (r[0].toInt() == 0) + report["hints"]["minerVersion"] = r[1].toString(); + } + catch (...) {} + } DEV_HINT_ERRINFO(required); DEV_HINT_ERRINFO(got); DEV_HINT_ERRINFO_HASH(required_LogBloom); @@ -316,7 +327,7 @@ void Client::doneWorking() } } -void Client::killChain() +void Client::reopenChain(WithExisting _we) { bool wasMining = isMining(); if (wasMining) @@ -337,11 +348,11 @@ void Client::killChain() m_working = State(); m_stateDB = OverlayDB(); - m_stateDB = State::openDB(Defaults::dbPath(), bc().genesisHash(), WithExisting::Kill); - bc().reopen(Defaults::dbPath(), WithExisting::Kill); + bc().reopen(_we); + m_stateDB = State::openDB(Defaults::dbPath(), bc().genesisHash(), _we); m_preMine = bc().genesisState(m_stateDB); - m_postMine = State(m_stateDB); + m_postMine = m_preMine; } if (auto h = m_host.lock()) @@ -450,6 +461,14 @@ void Client::setShouldPrecomputeDAG(bool _precompute) sealEngine()->setOption("precomputeDAG", _precompute ? trueBytes: falseBytes); } +void Client::setTurboMining(bool _enable) +{ + m_turboMining = _enable; + sealEngine()->setSealer("opencl"); + if (isMining()) + startMining(); +} + bool Client::isMining() const { return Ethash::isWorking(m_sealEngine.get()); @@ -586,7 +605,7 @@ void Client::onNewBlocks(h256s const& _blocks, h256Hash& io_changed) appendFromBlock(h, BlockPolarity::Live, io_changed); } -void Client::restartMining() +void Client::resyncStateFromChain() { // RESTART MINING @@ -639,7 +658,7 @@ void Client::onChainChanged(ImportRoute const& _ir) m_tq.dropGood(t); } onNewBlocks(_ir.liveBlocks, changeds); - restartMining(); + resyncStateFromChain(); noteChanged(changeds); } diff --git a/libethereum/Client.h b/libethereum/Client.h index 9e88775f9..f4550efd1 100644 --- a/libethereum/Client.h +++ b/libethereum/Client.h @@ -79,10 +79,6 @@ std::ostream& operator<<(std::ostream& _out, ActivityReport const& _r); class Client: public ClientBase, protected Worker { public: - /// New-style Constructor. - /// Any final derived class's constructor should make sure they call init(). - explicit Client(std::shared_ptr _gpForAdoption); - /// Destructor. virtual ~Client(); @@ -139,7 +135,7 @@ public: /// Are we allowed to GPU mine? bool turboMining() const { return m_turboMining; } /// Enable/disable GPU mining. - void setTurboMining(bool _enable = true) { m_turboMining = _enable; if (isMining()) startMining(); } + void setTurboMining(bool _enable = true); /// Enable/disable precomputing of the DAG for next epoch void setShouldPrecomputeDAG(bool _precompute); @@ -180,7 +176,9 @@ public: /// Clears pending transactions. Just for debug use. void clearPending(); /// Kills the blockchain. Just for debug use. - void killChain(); + void killChain() { reopenChain(WithExisting::Kill); } + /// Reloads the blockchain. Just for debug use. + void reopenChain(WithExisting _we = WithExisting::Trust); /// Retries all blocks with unknown parents. void retryUnknown() { m_bq.retryAllUnknown(); } /// Get a report of activity. @@ -199,6 +197,10 @@ public: SealEngineFace* sealEngine() const { return m_sealEngine.get(); } protected: + /// New-style Constructor. + /// Any final derived class's constructor should make sure they call init(). + explicit Client(std::shared_ptr _gpForAdoption); + /// Perform critical setup functions. /// Must be called in the constructor of the finally derived class. void init(p2p::Host* _extNet, std::string const& _dbPath, WithExisting _forceAction, u256 _networkId); @@ -250,7 +252,7 @@ protected: void onNewBlocks(h256s const& _blocks, h256Hash& io_changed); /// Called after processing blocks by onChainChanged(_ir) - void restartMining(); + void resyncStateFromChain(); /// Magically called when the chain has changed. An import route is provided. /// Called by either submitWork() or in our main thread through syncBlockQueue(). @@ -342,13 +344,8 @@ public: WithExisting _forceAction = WithExisting::Trust, u256 _networkId = 0 ): - Client(_gpForAdoption), - m_bc(_dbPath, _forceAction, [](unsigned d, unsigned t){ std::cerr << "REVISING BLOCKCHAIN: Processed " << d << " of " << t << "...\r"; }) + SpecialisedClient(_gpForAdoption, _dbPath, _forceAction) { - m_sealEngine = std::shared_ptr(Ethash::createSealEngine()); - m_sealEngine->onSealGenerated([=](bytes const& header){ - this->submitSealed(header); - }); init(_host, _dbPath, _forceAction, _networkId); } @@ -358,10 +355,24 @@ public: CanonBlockChain const& blockChain() const { return m_bc; } protected: + explicit SpecialisedClient( + std::shared_ptr _gpForAdoption, + std::string const& _dbPath = std::string(), + WithExisting _forceAction = WithExisting::Trust + ): + Client(_gpForAdoption), + m_bc(_dbPath, _forceAction, [](unsigned d, unsigned t){ std::cerr << "REVISING BLOCKCHAIN: Processed " << d << " of " << t << "...\r"; }) + { + m_sealEngine = std::shared_ptr(Ethash::createSealEngine()); + m_sealEngine->onSealGenerated([=](bytes const& header){ + this->submitSealed(header); + }); + } + virtual BlockChain& bc() override { return m_bc; } virtual BlockChain const& bc() const override { return m_bc; } -protected: +private: CanonBlockChain m_bc; ///< Maintains block database. }; @@ -375,9 +386,11 @@ public: std::string const& _dbPath = std::string(), WithExisting _forceAction = WithExisting::Trust, u256 _networkId = 0 - ): SpecialisedClient(_host, _gpForAdoption, _dbPath, _forceAction, _networkId) {} - - virtual ~EthashClient() { stopWorking(); } + ): + SpecialisedClient(_gpForAdoption, _dbPath, _forceAction) + { + init(_host, _dbPath, _forceAction, _networkId); + } /// Update to the latest transactions and get hash of the current block to be mined minus the /// nonce (the 'work hash') and the difficulty to be met. @@ -389,10 +402,6 @@ public: * @return true if the solution was indeed valid and accepted. */ virtual bool submitEthashWork(h256 const& _mixHash, h64 const& _nonce) override; - -protected: - virtual BlockChain& bc() override { return m_bc; } - virtual BlockChain const& bc() const override { return m_bc; } }; } diff --git a/libethereum/GenesisInfo.cpp b/libethereum/GenesisInfo.cpp index 4e6a48284..c1e0388e1 100644 --- a/libethereum/GenesisInfo.cpp +++ b/libethereum/GenesisInfo.cpp @@ -24,17 +24,27 @@ std::string const dev::eth::c_genesisInfo = R"ETHEREUM( { - "0000000000000000000000000000000000000001": { "wei": "1" }, - "0000000000000000000000000000000000000002": { "wei": "1" }, - "0000000000000000000000000000000000000003": { "wei": "1" }, - "0000000000000000000000000000000000000004": { "wei": "1" }, - "dbdbdb2cbd23b783741e8d7fcf51e459b497e4a6": { "wei": "1606938044258990275541962092341162602522202993782792835301376" }, - "e6716f9544a56c530d868e4bfbacb172315bdead": { "wei": "1606938044258990275541962092341162602522202993782792835301376" }, - "b9c015918bdaba24b4ff057a92a3873d6eb201be": { "wei": "1606938044258990275541962092341162602522202993782792835301376" }, - "1a26338f0d905e295fccb71fa9ea849ffa12aaf4": { "wei": "1606938044258990275541962092341162602522202993782792835301376" }, - "2ef47100e0787b915105fd5e3f4ff6752079d5cb": { "wei": "1606938044258990275541962092341162602522202993782792835301376" }, - "cd2a3d9f938e13cd947ec05abc7fe734df8dd826": { "wei": "1606938044258990275541962092341162602522202993782792835301376" }, - "6c386a4b26f73c802f34673f7248bb118f97424a": { "wei": "1606938044258990275541962092341162602522202993782792835301376" }, - "e4157b34ea9615cfbde6b4fda419828124b70c78": { "wei": "1606938044258990275541962092341162602522202993782792835301376" }, + "nonce": "0x000000000000002a", + "difficulty": "0x20000", + "mixhash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "coinbase": "0x0000000000000000000000000000000000000000", + "timestamp": "0x00", + "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "extraData": "0x", + "gasLimit": "0x2FEFD8", + "alloc": { + "0000000000000000000000000000000000000001": { "wei": "1" }, + "0000000000000000000000000000000000000002": { "wei": "1" }, + "0000000000000000000000000000000000000003": { "wei": "1" }, + "0000000000000000000000000000000000000004": { "wei": "1" }, + "dbdbdb2cbd23b783741e8d7fcf51e459b497e4a6": { "wei": "1606938044258990275541962092341162602522202993782792835301376" }, + "e6716f9544a56c530d868e4bfbacb172315bdead": { "wei": "1606938044258990275541962092341162602522202993782792835301376" }, + "b9c015918bdaba24b4ff057a92a3873d6eb201be": { "wei": "1606938044258990275541962092341162602522202993782792835301376" }, + "1a26338f0d905e295fccb71fa9ea849ffa12aaf4": { "wei": "1606938044258990275541962092341162602522202993782792835301376" }, + "2ef47100e0787b915105fd5e3f4ff6752079d5cb": { "wei": "1606938044258990275541962092341162602522202993782792835301376" }, + "cd2a3d9f938e13cd947ec05abc7fe734df8dd826": { "wei": "1606938044258990275541962092341162602522202993782792835301376" }, + "6c386a4b26f73c802f34673f7248bb118f97424a": { "wei": "1606938044258990275541962092341162602522202993782792835301376" }, + "e4157b34ea9615cfbde6b4fda419828124b70c78": { "wei": "1606938044258990275541962092341162602522202993782792835301376" } + } } )ETHEREUM"; diff --git a/libethereum/State.h b/libethereum/State.h index a5d3764af..b6349d40b 100644 --- a/libethereum/State.h +++ b/libethereum/State.h @@ -52,6 +52,7 @@ using errinfo_uncleNumber = boost::error_info; using errinfo_unclesExcluded = boost::error_info; using errinfo_block = boost::error_info; using errinfo_now = boost::error_info; +using errinfo_extraData = boost::error_info; using errinfo_transactionIndex = boost::error_info; diff --git a/libwebthree/WebThree.cpp b/libwebthree/WebThree.cpp index 7889b5935..485de65a6 100644 --- a/libwebthree/WebThree.cpp +++ b/libwebthree/WebThree.cpp @@ -53,7 +53,7 @@ WebThreeDirect::WebThreeDirect( if (_interfaces.count("eth")) { m_ethereum.reset(new eth::EthashClient(&m_net, shared_ptr(), _dbPath, _we, 0)); - m_ethereum->setExtraData(rlpList(0, _clientVersion, m_net.id())); + m_ethereum->setExtraData(rlpList(0, _clientVersion)); } if (_interfaces.count("shh")) diff --git a/mix/QBigInt.h b/mix/QBigInt.h index 700c817e5..a2dac8cf9 100644 --- a/mix/QBigInt.h +++ b/mix/QBigInt.h @@ -82,6 +82,8 @@ public: BigIntVariant internalValue() const { return m_internalValue; } /// @returns a string representation of the big integer used. Invokable from QML. Q_INVOKABLE QString value() const; + /// hex value. + Q_INVOKABLE QString hexValue() const { return QString::fromStdString(dev::toHex(dev::u256(value().toStdString()))); } /// Set the value of the BigInteger used. Will use u256 type. Invokable from QML. Q_INVOKABLE void setValue(QString const& _value) { m_internalValue = dev::jsToU256(_value.toStdString()); } Q_INVOKABLE void setBigInt(QString const& _value) { m_internalValue = bigint(_value.toStdString()); } diff --git a/mix/QFunctionDefinition.h b/mix/QFunctionDefinition.h index a9c45ffcd..5a056fa75 100644 --- a/mix/QFunctionDefinition.h +++ b/mix/QFunctionDefinition.h @@ -55,6 +55,8 @@ public: FixedHash<4> hash() const { return m_hash; } /// Get the full hash of this function declaration on the contract ABI. FixedHash<32> fullHash() const { return m_fullHash; } + /// Get the hash of this function declaration on the contract ABI. returns QString + Q_INVOKABLE QString qhash() const { return QString::fromStdString(m_hash.hex()); } private: int m_index; diff --git a/mix/qml/Block.qml b/mix/qml/Block.qml index 732166628..383cb85e8 100644 --- a/mix/qml/Block.qml +++ b/mix/qml/Block.qml @@ -105,6 +105,7 @@ ColumnLayout anchors.verticalCenter: parent.verticalCenter anchors.right: parent.right anchors.rightMargin: 14 + visible: false MouseArea { anchors.fill: parent diff --git a/mix/qml/BlockChain.qml b/mix/qml/BlockChain.qml index 1b1b68862..960d95ebf 100644 --- a/mix/qml/BlockChain.qml +++ b/mix/qml/BlockChain.qml @@ -117,47 +117,7 @@ ColumnLayout { RowLayout { - id: header - spacing: 0 - Layout.preferredHeight: 24 - Rectangle - { - Layout.preferredWidth: statusWidth - Layout.preferredHeight: parent.height - color: "transparent" - Image { - id: debugImage - anchors.verticalCenter: parent.verticalCenter - anchors.horizontalCenter: parent.horizontalCenter - source: "qrc:/qml/img/recycleicon@2x.png" - width: statusWidth + 10 - fillMode: Image.PreserveAspectFit - } - } - Rectangle - { - anchors.verticalCenter: parent.verticalCenter - Layout.preferredWidth: fromWidth - Label - { - anchors.verticalCenter: parent.verticalCenter - text: "From" - anchors.left: parent.left - anchors.leftMargin: horizontalMargin - } - } - Label - { - text: "To" - anchors.verticalCenter: parent.verticalCenter - Layout.preferredWidth: toWidth + cellSpacing - } - Label - { - text: "" - anchors.verticalCenter: parent.verticalCenter - Layout.preferredWidth: debugActionWidth - } + Layout.preferredHeight: 10 } Rectangle @@ -178,18 +138,6 @@ ColumnLayout { width: parent.width spacing: 20 - Block - { - scenario: blockChainPanel.model - Layout.preferredWidth: blockChainScrollView.width - Layout.preferredHeight: 60 - blockIndex: -1 - transactions: [] - status: "" - number: -2 - trHeight: 60 - } - Repeater // List of blocks { id: blockChainRepeater diff --git a/mix/qml/DeployContractStep.qml b/mix/qml/DeployContractStep.qml index 10f879ab7..01f06e4f2 100644 --- a/mix/qml/DeployContractStep.qml +++ b/mix/qml/DeployContractStep.qml @@ -12,12 +12,15 @@ Rectangle { property variant paramsModel: [] property variant worker property variant gas: [] + property alias gasPrice: gasPriceInput color: "#E3E3E3E3" + signal deployed anchors.fill: parent id: root property int labelWidth: 150 + function show() { visible = true @@ -25,12 +28,13 @@ Rectangle { contractList.change() accountsModel.clear() for (var k in worker.accounts) - { accountsModel.append(worker.accounts[k]) - } - if (worker.accounts.length > 0) + if (worker.currentAccount === "" && worker.accounts.length > 0) + { worker.currentAccount = worker.accounts[0].id + accountsList.currentIndex = 0 + } if (projectModel.deployBlockNumber !== -1) { @@ -45,13 +49,28 @@ Rectangle { function updateVerification(blockNumber, trLost) { - verificationLabel.text = blockNumber - projectModel.deployBlockNumber - if (trLost.length > 0) + var nb = parseInt(blockNumber - projectModel.deployBlockNumber) + verificationTextArea.visible = false + verificationLabel.visible = true + if (nb >= 10) { - verificationLabel.text += "\n" + qsTr("Transactions lost") + "\n" - for (var k in trLost) + verificationLabel.text = qsTr("contracts deployment verified") + verificationLabel.color = "green" + } + else + { + verificationLabel.text = nb + if (trLost.length > 0) { - verificationLabel.text += trLost[k] + "\n" + verificationTextArea.visible = true + verificationLabel.visible = false + deploymentStepChanged("following transactions are invalidated:") + verificationTextArea.text += "\n" + qsTr("Transactions lost") + "\n" + for (var k in trLost) + { + deploymentStepChanged(trLost[k]) + verificationTextArea.text += trLost[k] + "\n" + } } } } @@ -250,7 +269,6 @@ Rectangle { { worker.currentAccount = currentText accountBalance.text = worker.balance(currentText).format() - console.log(worker.balance(currentText).format()) } } @@ -280,6 +298,11 @@ Rectangle { displayUnitSelection: true displayFormattedValue: true edit: true + + function toHexWei() + { + return "0x" + gasPriceInput.value.toWei().hexValue() + } } Connections @@ -329,9 +352,7 @@ Rectangle { root.gas = gas cost = 0 for (var k in gas) - { cost += gas[k] - } setCost() } }); @@ -349,7 +370,7 @@ Rectangle { width: labelWidth Label { - text: qsTr("Cost Estimate") + text: qsTr("Deployment Cost") anchors.right: parent.right anchors.verticalCenter: parent.verticalCenter } @@ -365,92 +386,87 @@ Rectangle { } } - RowLayout + Rectangle { - id: deployedRow + border.color: "#cccccc" + border.width: 2 Layout.fillWidth: true - Rectangle + Layout.preferredHeight: parent.height + 25 + color: "transparent" + id: rectDeploymentVariable + ScrollView { - width: labelWidth - Label + anchors.fill: parent + anchors.topMargin: 4 + anchors.bottomMargin: 4 + ColumnLayout { - id: labelAddresses - text: qsTr("Deployed Contracts") - anchors.right: parent.right - anchors.verticalCenter: parent.verticalCenter - } - } - - ColumnLayout - { - anchors.top: parent.top - anchors.topMargin: 1 - ListModel - { - id: deployedAddrModel - } - - Repeater - { - id: deployedAddresses - model: deployedAddrModel - function refresh() + RowLayout { - deployedAddrModel.clear() - deployedRow.visible = Object.keys(projectModel.deploymentAddresses).length > 0 - for (var k in projectModel.deploymentAddresses) + id: deployedRow + Layout.fillWidth: true + Rectangle { - if (k.indexOf("-") !== -1) // this is an contract instance. ctr without - are the last deployed (to support old project) - deployedAddrModel.append({ id: k, value: projectModel.deploymentAddresses[k]}) + width: labelWidth + Label + { + id: labelAddresses + text: qsTr("Deployed Contracts") + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + } + } + + ColumnLayout + { + anchors.top: parent.top + anchors.topMargin: 1 + width: parent.width + id: deployedAddresses + function refresh() + { + textAddresses.text = "" + deployedRow.visible = Object.keys(projectModel.deploymentAddresses).length > 0 + textAddresses.text = JSON.stringify(projectModel.deploymentAddresses, null, ' ') + } + TextArea + { + anchors.fill: parent + id: textAddresses + } } } - Rectangle + RowLayout { - Layout.preferredHeight: 20 - Layout.preferredWidth: 235 - color: "transparent" - Label + id: verificationRow + Layout.fillWidth: true + visible: Object.keys(projectModel.deploymentAddresses).length > 0 + Rectangle { - id: labelContract - width: 112 - elide: Text.ElideRight - text: index > -1 ? deployedAddrModel.get(index).id : "" + width: labelWidth + Label + { + text: qsTr("Verifications") + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + } } - TextField + TextArea { - width: 123 - anchors.verticalCenter: parent.verticalCenter - anchors.left: labelContract.right - text: index > - 1 ? deployedAddrModel.get(index).value : "" + id: verificationTextArea + visible: false } - } - } - } - } - RowLayout - { - id: verificationRow - Layout.fillWidth: true - visible: Object.keys(projectModel.deploymentAddresses).length > 0 - Rectangle - { - width: labelWidth - Label - { - text: qsTr("Verifications") - anchors.right: parent.right - anchors.verticalCenter: parent.verticalCenter + Label + { + id: verificationLabel + visible: true + } + } } } - - Label - { - id: verificationLabel - maximumLineCount: 20 - } } } @@ -460,12 +476,31 @@ Rectangle { Layout.alignment: Qt.BottomEdge Button { + Layout.preferredHeight: 22 + anchors.right: deployBtn.left + text: qsTr("Reset") + action: clearDeployAction + } + + Action { + id: clearDeployAction + onTriggered: { + worker.forceStopPooling() + fileIo.deleteDir(projectModel.deploymentDir) + projectModel.cleanDeploymentStatus() + deploymentDialog.steps.reset() + } + } + + Button + { + id: deployBtn anchors.right: parent.right text: qsTr("Deploy Contracts") onClicked: { projectModel.deployedScenarioIndex = contractList.currentIndex - NetworkDeploymentCode.deployContracts(root.gas, function(addresses, trHashes) + NetworkDeploymentCode.deployContracts(root.gas, gasPriceInput.toHexWei(), function(addresses, trHashes) { projectModel.deploymentTrHashes = trHashes worker.verifyHashes(trHashes, function (nb, trLost) @@ -473,6 +508,7 @@ Rectangle { projectModel.deployBlockNumber = nb projectModel.saveProject() root.updateVerification(nb, trLost) + root.deployed() }) projectModel.deploymentAddresses = addresses projectModel.saveProject() diff --git a/mix/qml/DeploymentDialog.qml b/mix/qml/DeploymentDialog.qml index 39022def2..280ce0d07 100644 --- a/mix/qml/DeploymentDialog.qml +++ b/mix/qml/DeploymentDialog.qml @@ -22,6 +22,7 @@ Dialog { property alias packageStep: packageStep property alias registerStep: registerStep property alias worker: worker + property alias steps: steps function close() { @@ -52,15 +53,31 @@ Dialog { anchors.fill: parent anchors.margins: 10 - RowLayout + Rectangle { id: explanation Layout.preferredWidth: parent.width - 50 Layout.preferredHeight: 50 + color: "transparent" Label { + id: info anchors.centerIn: parent - text: qsTr("Putting your dapp live is a multi step process. You can read more about it on the 'guide to uploading'.") + text: qsTr("Putting your dapp live is a multi step process. You can read more about it on the") + } + + Text { + anchors.left: info.right + anchors.leftMargin: 7 + id: linkText + text: 'guide to uploading' + onLinkActivated: Qt.openUrlExternally("https://github.com/ethereum/wiki/wiki/Mix:-The-DApp-IDE#deployment-to-network") + anchors.verticalCenter: parent.verticalCenter + MouseArea + { + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + } } } diff --git a/mix/qml/DeploymentDialogSteps.qml b/mix/qml/DeploymentDialogSteps.qml index 5ebd81dea..5874d54a8 100644 --- a/mix/qml/DeploymentDialogSteps.qml +++ b/mix/qml/DeploymentDialogSteps.qml @@ -26,13 +26,55 @@ Rectangle { selected(step) } + function reset() + { + for (var k in deployLogs.logs) + { + deployLogs.logs[k] = "" + } + deployLogs.switchLogs() + refreshCurrent() + } + border.color: "#cccccc" border.width: 1 + + Connections + { + id: deployStatus + target: deploymentDialog.deployStep + onDeployed: + { + console.log("deployed") + } + } + + Connections + { + id: packagedStatus + target: deploymentDialog.packageStep + onPackaged: + { + console.log("packaged") + } + } + + Connections + { + id: registerStatus + target: deploymentDialog.registerStep + onRegistered: + { + console.log("registered") + } + } + ColumnLayout { anchors.fill: parent anchors.margins: 1 + spacing: 0 Repeater { id: menu @@ -45,7 +87,7 @@ Rectangle { { step: 2, type:"package", - label: qsTr("Package files") + label: qsTr("Package Dapp") }, { step: 3, @@ -70,6 +112,7 @@ Rectangle { labelContainer.state = "selected" sel = index itemClicked(menu.model[index].type) + deployLogs.switchLogs() } function unselect() @@ -136,62 +179,60 @@ Rectangle { } Connections { - target: projectModel - onDeploymentStarted: log.text = log.text + qsTr("Running deployment...") + "\n" - onDeploymentError: log.text = log.text + error + "\n" - onDeploymentComplete: log.text = log.text + qsTr("Deployment complete") + "\n" - onDeploymentStepChanged: log.text = log.text + message + "\n" - } + property var logs: ({}) + id: deployLogs - Rectangle - { - Layout.fillWidth: true - Layout.preferredHeight: 1 - color: "#cccccc" - } + function switchLogs() + { + if (root.sel) + { + if (!logs[root.sel]) + logs[root.sel] = "" + log.text = logs[root.sel] + } + } - RowLayout - { - anchors.horizontalCenter: parent.horizontalCenter - Layout.preferredHeight: 20 - anchors.left: parent.left - anchors.leftMargin: 2 - Button + target: projectModel + onDeploymentStarted: { - Layout.preferredHeight: 22 - Layout.preferredWidth: 22 - action: clearAction - iconSource: "qrc:/qml/img/cleariconactive.png" + if (!logs[root.sel]) + logs[root.sel] = "" + logs[root.sel] = logs[root.sel] + qsTr("Running deployment...") + "\n" + log.text = logs[root.sel] } - Action { - id: clearAction - enabled: log.text !== "" - tooltip: qsTr("Clear") - onTriggered: { - log.text = "" - } + onDeploymentError: + { + if (!logs[root.sel]) + logs[root.sel] = "" + logs[root.sel] = logs[root.sel] + error + "\n" + log.text = logs[root.sel] } - Button + onDeploymentComplete: { - Layout.preferredHeight: 22 - text: qsTr("Clear Deployment") - action: clearDeployAction + if (!logs[root.sel]) + logs[root.sel] = "" + logs[root.sel] = logs[root.sel] + qsTr("Deployment complete") + "\n" + log.text = logs[root.sel] } - Action { - id: clearDeployAction - onTriggered: { - worker.forceStopPooling() - fileIo.deleteDir(projectModel.deploymentDir) - projectModel.cleanDeploymentStatus() - root.refreshCurrent() - log.text = "" - } + onDeploymentStepChanged: + { + if (!logs[root.sel]) + logs[root.sel] = "" + logs[root.sel] = logs[root.sel] + message + "\n" + log.text = logs[root.sel] } } + Rectangle + { + Layout.fillWidth: true + Layout.preferredHeight: 2 + color: "#cccccc" + } + ScrollView { Layout.fillHeight: true @@ -205,6 +246,47 @@ Rectangle { id: log } } + + Rectangle + { + Layout.preferredHeight: 20 + Layout.fillWidth: true + color: "#cccccc" + LogsPaneStyle + { + id: style + } + + Label + { + anchors.horizontalCenter: parent.horizontalCenter + anchors.verticalCenter: parent.verticalCenter + text: qsTr("Logs") + font.italic: true + font.pointSize: style.absoluteSize(-1) + } + + Button + { + height: 20 + width: 20 + anchors.right: parent.right + action: clearAction + iconSource: "qrc:/qml/img/cleariconactive.png" + tooltip: qsTr("Clear Messages") + } + + Action { + id: clearAction + enabled: log.text !== "" + tooltip: qsTr("Clear") + onTriggered: { + deployLogs.logs[root.sel] = "" + log.text = deployLogs.logs[root.sel] + } + } + } + } } diff --git a/mix/qml/DeploymentWorker.qml b/mix/qml/DeploymentWorker.qml index 9aef45112..c7532c840 100644 --- a/mix/qml/DeploymentWorker.qml +++ b/mix/qml/DeploymentWorker.qml @@ -38,6 +38,7 @@ Item var ids = JSON.parse(arg2)[0].result; requests = []; + accounts = [] for (var k in ids) { requests.push({ @@ -52,6 +53,7 @@ Item TransactionHelper.rpcCall(requests, function (request, response){ var balanceRet = JSON.parse(response); + balances = {} for (var k in balanceRet) { var ether = QEtherHelper.createEther(balanceRet[k].result, QEther.Wei); @@ -206,7 +208,7 @@ Item property var callBack property int elapsed property string hash - interval: 500 + interval: 2000 running: false repeat: true onTriggered: { @@ -227,7 +229,7 @@ Item stop(); callBack(1, receipt); } - else if (elapsed > 250000) + else if (elapsed > 2500000) { stop(); callBack(-1, null); diff --git a/mix/qml/KeyValuePanel.qml b/mix/qml/KeyValuePanel.qml index 056384b9e..d19f4d3fa 100644 --- a/mix/qml/KeyValuePanel.qml +++ b/mix/qml/KeyValuePanel.qml @@ -82,45 +82,44 @@ ColumnLayout { clip: true ColumnLayout { - anchors.margins: 10 + spacing: 0 + id: colValue + anchors.top: parent.top + anchors.topMargin: 5 Repeater { id: repeaterKeyValue model: modelKeyValue - RowLayout + Row { - Layout.fillWidth: true Layout.preferredHeight: 30 - spacing: 0 - Rectangle + spacing: 5 + anchors.left: colValue.left + anchors.leftMargin: 5 + Label { - Layout.preferredWidth: columnValues.width / 2 - Label - { - anchors.left: parent.left - anchors.leftMargin: 10 - text: { - if (index >= 0 && repeaterKeyValue.model.get(index).key !== undefined) - return repeaterKeyValue.model.get(index).key - else - return "" - } + maximumLineCount: 1 + text: { + if (index >= 0 && repeaterKeyValue.model.get(index).key !== undefined) + return repeaterKeyValue.model.get(index).key + else + return "" } } - Rectangle + Label { - Layout.preferredWidth: columnValues.width / 2 - 10 - Label - { - anchors.right: parent.right - anchors.rightMargin: 10 - text: { - if (index >= 0 && repeaterKeyValue.model.get(index).value !== undefined) - return repeaterKeyValue.model.get(index).value - else - return "" - } + text: "=" + } + + Label + { + maximumLineCount: 1 + text: { + if (index >= 0 && repeaterKeyValue.model.get(index).value !== undefined) + return repeaterKeyValue.model.get(index).value + else + return "" } } } diff --git a/mix/qml/PackagingStep.qml b/mix/qml/PackagingStep.qml index 424732869..6f7ed3f11 100644 --- a/mix/qml/PackagingStep.qml +++ b/mix/qml/PackagingStep.qml @@ -20,6 +20,7 @@ Rectangle { property alias lastDeployDate: lastDeployLabel.text property string deploymentId property string packageDir + signal packaged function show() { @@ -107,6 +108,7 @@ Rectangle { { NetworkDeploymentCode.packageDapp(projectModel.deploymentAddresses); projectModel.saveProject() + root.packaged() } } diff --git a/mix/qml/ProjectModel.qml b/mix/qml/ProjectModel.qml index 5ec833da6..14cc4ec39 100644 --- a/mix/qml/ProjectModel.qml +++ b/mix/qml/ProjectModel.qml @@ -80,20 +80,14 @@ Item { function getDocumentIndex(documentId) { return ProjectModelCode.getDocumentIndex(documentId); } function addExistingFiles(paths) { ProjectModelCode.doAddExistingFiles(paths); } function deployProject() { NetworkDeploymentCode.deployProject(false); } - function registerToUrlHint(url, callback) { NetworkDeploymentCode.registerToUrlHint(url, callback); } + function registerToUrlHint(url, gasPrice, callback) { NetworkDeploymentCode.registerToUrlHint(url, gasPrice, callback); } function formatAppUrl() { NetworkDeploymentCode.formatAppUrl(url); } function cleanDeploymentStatus() { deployedScenarioIndex = 0 - applicationUrlEth = "" - applicationUrlHttp = "" deployBlockNumber = "" deploymentTrHashes = {} - registerContentHashTrHash = "" - registerUrlTrHash = "" - registerContentHashBlockNumber = -1 - registerUrlBlockNumber = -1 deploymentAddresses = {} deploymentDir = "" deploymentDialog.packageStep.packageHash = "" @@ -102,6 +96,18 @@ Item { deploymentDialog.packageStep.lastDeployDate = "" deploymentDialog.packageStep.localPackageUrl = "" saveProject() + cleanRegisteringStatus() + } + + function cleanRegisteringStatus() + { + applicationUrlEth = "" + applicationUrlHttp = "" + registerContentHashTrHash = "" + registerUrlTrHash = "" + registerContentHashBlockNumber = -1 + registerUrlBlockNumber = -1 + saveProject() } Connections { diff --git a/mix/qml/RegisteringStep.qml b/mix/qml/RegisteringStep.qml index 648e04f16..e275e1434 100644 --- a/mix/qml/RegisteringStep.qml +++ b/mix/qml/RegisteringStep.qml @@ -19,42 +19,58 @@ Rectangle { id: root color: "#E3E3E3E3" anchors.fill: parent + signal registered function show() { ctrRegisterLabel.calculateRegisterGas() - applicationUrlEthCtrl.text = projectModel.applicationUrlEth - applicationUrlHttpCtrl.text = projectModel.applicationUrlHttp + if (applicationUrlHttpCtrl.text === "") + applicationUrlHttpCtrl.text = projectModel.applicationUrlHttp + + if (applicationUrlEthCtrl.text === "") + applicationUrlEthCtrl.text = projectModel.applicationUrlEth + visible = true verificationEthUrl.text = "" - if (projectModel.registerContentHashTrHash !== "") + if (projectModel.registerContentHashTrHash !== "" && projectModel.registerContentHashBlockNumber !== -1) { worker.verifyHash("registerHash", projectModel.registerContentHashTrHash, function(bn, trLost) { - updateVerification(projectModel.registerContentHashBlockNumber, bn, trLost, verificationEthUrl) + updateVerification(projectModel.registerContentHashBlockNumber, bn, trLost, verificationEthUrl, "registerHash") }); } + else if (projectModel.registerContentHashTrHash !== "" && projectModel.registerContentHashBlockNumber === -1) + verificationEthUrl.text = qsTr("waiting verifications") verificationUrl.text = "" - if (projectModel.registerUrlTrHash !== "") + if (projectModel.registerUrlTrHash !== "" && projectModel.registerUrlBlockNumber !== -1) { worker.verifyHash("registerUrl", projectModel.registerUrlTrHash, function(bn, trLost) { - updateVerification(projectModel.registerUrlBlockNumber, bn, trLost, verificationUrl) + updateVerification(projectModel.registerUrlBlockNumber, bn, trLost, verificationUrl, "registerUrl") }); } + else if (projectModel.registerUrlTrHash !== "" && projectModel.registerUrlBlockNumber === -1) + verificationUrl.text = qsTr("waiting verifications") } - function updateVerification(originbn, bn, trLost, ctrl) + function updateVerification(originbn, bn, trLost, ctrl, trContext) { if (trLost.length === 0) { ctrl.text = bn - originbn - ctrl.text += qsTr(" verifications") + if (parseInt(bn - originbn) >= 10) + { + ctrl.color= "green" + ctrl.text= qsTr("verified") + } + else + ctrl.text += qsTr(" verifications") } else { + deploymentStepChanged(trContext + qsTr(" has been invalidated.") + trLost[0] + " " + qsTr("no longer present") ) ctrl.text = qsTr("invalidated") } } @@ -124,6 +140,7 @@ Rectangle { { id: verificationUrl anchors.verticalCenter: parent.verticalCenter + font.italic: true } } @@ -136,7 +153,7 @@ Rectangle { Layout.preferredWidth: col.width / 2 Label { - text: qsTr("Gas to use for dapp registration") + text: qsTr("Registration Cost") anchors.right: parent.right anchors.verticalCenter: parent.verticalCenter id: ctrRegisterLabel @@ -148,9 +165,12 @@ Rectangle { NetworkDeploymentCode.checkPathCreationCost(applicationUrlEthCtrl.text, function(pathCreationCost) { var ether = QEtherHelper.createBigInt(pathCreationCost); - var gasTotal = ether.multiply(worker.gasPriceInt); - gasToUseDeployInput.value = QEtherHelper.createEther(gasTotal.value(), QEther.Wei, parent); - gasToUseDeployInput.update(); + if (deploymentDialog.deployStep.gasPrice.value) + { + var gasTotal = ether.multiply(deploymentDialog.deployStep.gasPrice.value.toWei()); + gasToUseDeployInput.value = QEtherHelper.createEther(gasTotal.value(), QEther.Wei, parent); + gasToUseDeployInput.update(); + } }); } } @@ -159,9 +179,9 @@ Rectangle { Ether { id: gasToUseDeployInput - displayUnitSelection: true + displayUnitSelection: false displayFormattedValue: true - edit: true + edit: false Layout.preferredWidth: 235 } } @@ -224,6 +244,10 @@ Rectangle { Label { id: verificationEthUrl + anchors.verticalCenter: parent.verticalCenter; + anchors.topMargin: 10 + font.italic: true + font.pointSize: appStyle.absoluteSize(-1) } } } @@ -234,7 +258,7 @@ Rectangle { anchors.bottomMargin: 10 width: parent.width - function registerHash(callback) + function registerHash(gasPrice, callback) { var inError = []; var ethUrl = NetworkDeploymentCode.formatAppUrl(applicationUrlEthCtrl.text); @@ -244,10 +268,11 @@ Rectangle { inError.push(qsTr("Member too long: " + ethUrl[k]) + "\n"); } if (!worker.stopForInputError(inError)) - { - NetworkDeploymentCode.registerDapp(ethUrl, function(){ + { + NetworkDeploymentCode.registerDapp(ethUrl, gasPrice, function(){ projectModel.applicationUrlEth = applicationUrlEthCtrl.text projectModel.saveProject() + verificationEthUrl.text = qsTr("waiting verifications") worker.waitForTrReceipt(projectModel.registerContentHashTrHash, function(status, receipt) { worker.verifyHash("registerHash", projectModel.registerContentHashTrHash, function(bn, trLost) @@ -262,7 +287,7 @@ Rectangle { } } - function registerUrl() + function registerUrl(gasPrice, callback) { if (applicationUrlHttp.text === "" || deploymentDialog.packageHash === "") { @@ -276,9 +301,10 @@ Rectangle { inError.push(qsTr(applicationUrlHttpCtrl.text)); if (!worker.stopForInputError(inError)) { - registerToUrlHint(applicationUrlHttpCtrl.text, function(){ + registerToUrlHint(applicationUrlHttpCtrl.text, gasPrice, function(){ projectModel.applicationUrlHttp = applicationUrlHttpCtrl.text projectModel.saveProject() + verificationUrl.text = qsTr("waiting verifications") worker.waitForTrReceipt(projectModel.registerUrlTrHash, function(status, receipt) { worker.verifyHash("registerUrl", projectModel.registerUrlTrHash, function(bn, trLost) @@ -286,6 +312,8 @@ Rectangle { projectModel.registerUrlBlockNumber = bn projectModel.saveProject() root.updateVerification(bn, bn, trLost, verificationUrl) + root.registered() + callback() }); }) }) @@ -300,8 +328,12 @@ Rectangle { width: 30 onClicked: { - parent.registerHash(function(){ - parent.registerUrl() + verificationEthUrl.text = "" + verificationUrl.text = "" + projectModel.cleanRegisteringStatus() + var gasPrice = deploymentDialog.deployStep.gasPrice.toHexWei() + parent.registerHash(gasPrice, function(){ + parent.registerUrl(gasPrice, function(){}) }) } } diff --git a/mix/qml/ScenarioLoader.qml b/mix/qml/ScenarioLoader.qml index 0de779135..8b6886c93 100644 --- a/mix/qml/ScenarioLoader.qml +++ b/mix/qml/ScenarioLoader.qml @@ -35,7 +35,7 @@ ColumnLayout { Layout.preferredWidth: 560 anchors.horizontalCenter: parent.horizontalCenter - Layout.preferredHeight: 60 + Layout.preferredHeight: 75 spacing: 0 anchors.top: parent.top anchors.topMargin: 10 @@ -88,7 +88,7 @@ ColumnLayout color: "#cccccc" id: deleteImg anchors.top: parent.top - anchors.topMargin: 6 + anchors.topMargin: 7 visible: projectModel.stateListModel.count > 1 MouseArea { diff --git a/mix/qml/StatusPane.qml b/mix/qml/StatusPane.qml index a8750fc7c..23a39d126 100644 --- a/mix/qml/StatusPane.qml +++ b/mix/qml/StatusPane.qml @@ -179,7 +179,7 @@ Rectangle { function updateWidth() { - if (text.length > 80) + if (text.length > 100) width = parent.width - 10 else width = undefined diff --git a/mix/qml/WebPreview.qml b/mix/qml/WebPreview.qml index 5d0f208af..1b3b1dc40 100644 --- a/mix/qml/WebPreview.qml +++ b/mix/qml/WebPreview.qml @@ -272,7 +272,8 @@ Item { Button { - height: 28 + height: 22 + width: 22 anchors.verticalCenter: parent.verticalCenter action: expressionAction iconSource: "qrc:/qml/img/console.png" diff --git a/mix/qml/js/NetworkDeployment.js b/mix/qml/js/NetworkDeployment.js index 42cbf5207..2e8c8c885 100644 --- a/mix/qml/js/NetworkDeployment.js +++ b/mix/qml/js/NetworkDeployment.js @@ -32,11 +32,10 @@ function deployProject(force) { deploymentDialog.open(); } -function deployContracts(gas, callback) +function deployContracts(gas, gasPrice, callback) { deploymentGas = gas; - var jsonRpcUrl = "http://127.0.0.1:8080"; - console.log("Deploying to " + jsonRpcUrl); + deploymentGasPrice = gasPrice deploymentStarted(); var ctrAddresses = {}; @@ -79,10 +78,7 @@ function checkPathCreationCost(ethUrl, callBack) } } else - { - deploymentStepChanged(qsTr("Your Dapp can be registered here.")); callBack((dappUrl.length - 1) * (deploymentDialog.registerStep.ownedRegistrarDeployGas + deploymentDialog.registerStep.ownedRegistrarSetSubRegistrarGas) + deploymentDialog.registerStep.ownedRegistrarSetContentHashGas); - } }); } @@ -140,6 +136,7 @@ function getFunction(ctrName, functionId) } var deploymentGas +var deploymentGasPrice var trRealIndex = -1 function executeTr(blockIndex, trIndex, state, ctrAddresses, trHashes, callBack) { @@ -153,14 +150,14 @@ function executeTr(blockIndex, trIndex, state, ctrAddresses, trHashes, callBack) else { var gasCost = clientModel.toHex(deploymentGas[trRealIndex]); - var rpcParams = { "from": deploymentDialog.worker.currentAccount, "gas": "0x" + gasCost }; + var rpcParams = { "from": deploymentDialog.worker.currentAccount, "gas": "0x" + gasCost, "gasPrice": deploymentGasPrice }; var params = replaceParamToken(func.parameters, tr.parameters, ctrAddresses); var encodedParams = clientModel.encodeParams(params, contractFromToken(tr.contractId), tr.functionId); if (tr.contractId === tr.functionId) rpcParams.code = codeModel.contracts[tr.contractId].codeHex + encodedParams.join(""); else - rpcParams.data = func.hash + encodedParams.join(""); + rpcParams.data = "0x" + func.qhash() + encodedParams.join(""); var requests = [{ jsonrpc: "2.0", @@ -214,13 +211,9 @@ function executeTrNextStep(blockIndex, trIndex, state, ctrAddresses, trHashes, c { blockIndex++ if (blockIndex < state.blocks.count) - { executeTr(blockIndex, 0, state, ctrAddresses, trHashes, callBack); - } else - { callBack(); - } } } @@ -298,16 +291,17 @@ function packageDapp(addresses) deploymentDialog.packageStep.packageBase64 = packageRet[1]; deploymentDialog.packageStep.localPackageUrl = packageRet[2] + "?hash=" + packageRet[0]; deploymentDialog.packageStep.lastDeployDate = date - deploymentComplete() + deploymentStepChanged(qsTr("Dapp is Packaged")) } -function registerDapp(url, callback) +function registerDapp(url, gasPrice, callback) { + deploymentGasPrice = gasPrice deploymentStepChanged(qsTr("Registering application on the Ethereum network ...")); checkEthPath(url, false, function (success) { if (!success) return; - deploymentComplete(); + deploymentStepChanged(qsTr("Dapp has been registered. Please wait for verifications.")); if (callback) callback() }); @@ -446,7 +440,7 @@ function continueRegistration(dappUrl, addr, callBack, checkOnly) requests.push({ jsonrpc: "2.0", method: "eth_sendTransaction", - params: [ { "from": deploymentDialog.worker.currentAccount, "gas": "0x" + gasCost, "code": "0x600080547fffffffffffffffffffffffff000000000000000000000000000000000000000016331781556105cd90819061003990396000f3007c010000000000000000000000000000000000000000000000000000000060003504630198489281146100b257806321f8a721146100e45780632dff6941146100ee5780633b3b57de1461010e5780635a3a05bd1461013e5780635fd4b08a146101715780637dd564111461017d57806389a69c0e14610187578063b387ef92146101bb578063b5c645bd146101f4578063be99a98014610270578063c3d014d6146102a8578063d93e7573146102dc57005b73ffffffffffffffffffffffffffffffffffffffff600435166000908152600160205260409020548060005260206000f35b6000808052602081f35b600435600090815260026020819052604090912001548060005260206000f35b600435600090815260026020908152604082205473ffffffffffffffffffffffffffffffffffffffff1680835291f35b600435600090815260026020908152604082206001015473ffffffffffffffffffffffffffffffffffffffff1680835291f35b60008060005260206000f35b6000808052602081f35b60005461030c9060043590602435903373ffffffffffffffffffffffffffffffffffffffff908116911614610569576105c9565b60005473ffffffffffffffffffffffffffffffffffffffff168073ffffffffffffffffffffffffffffffffffffffff1660005260206000f35b600435600090815260026020819052604090912080546001820154919092015473ffffffffffffffffffffffffffffffffffffffff9283169291909116908273ffffffffffffffffffffffffffffffffffffffff166000528173ffffffffffffffffffffffffffffffffffffffff166020528060405260606000f35b600054610312906004359060243590604435903373ffffffffffffffffffffffffffffffffffffffff90811691161461045457610523565b6000546103189060043590602435903373ffffffffffffffffffffffffffffffffffffffff90811691161461052857610565565b60005461031e90600435903373ffffffffffffffffffffffffffffffffffffffff90811691161461032457610451565b60006000f35b60006000f35b60006000f35b60006000f35b60008181526002602090815260408083205473ffffffffffffffffffffffffffffffffffffffff16835260019091529020548114610361576103e1565b6000818152600260205260408082205473ffffffffffffffffffffffffffffffffffffffff169183917ff63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a85459190a360008181526002602090815260408083205473ffffffffffffffffffffffffffffffffffffffff16835260019091528120555b600081815260026020819052604080832080547fffffffffffffffffffffffff00000000000000000000000000000000000000009081168255600182018054909116905590910182905582917fa6697e974e6a320f454390be03f74955e8978f1a6971ea6730542e37b66179bc91a25b50565b600083815260026020526040902080547fffffffffffffffffffffffff00000000000000000000000000000000000000001683179055806104bb57827fa6697e974e6a320f454390be03f74955e8978f1a6971ea6730542e37b66179bc60006040a2610522565b73ffffffffffffffffffffffffffffffffffffffff8216837ff63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a854560006040a373ffffffffffffffffffffffffffffffffffffffff821660009081526001602052604090208390555b5b505050565b600082815260026020819052604080832090910183905583917fa6697e974e6a320f454390be03f74955e8978f1a6971ea6730542e37b66179bc91a25b5050565b60008281526002602052604080822060010180547fffffffffffffffffffffffff0000000000000000000000000000000000000000168417905583917fa6697e974e6a320f454390be03f74955e8978f1a6971ea6730542e37b66179bc91a25b505056" } ], + params: [ { "from": deploymentDialog.worker.currentAccount, "gasPrice": deploymentGasPrice, "gas": "0x" + gasCost, "code": "0x600080547fffffffffffffffffffffffff000000000000000000000000000000000000000016331781556105cd90819061003990396000f3007c010000000000000000000000000000000000000000000000000000000060003504630198489281146100b257806321f8a721146100e45780632dff6941146100ee5780633b3b57de1461010e5780635a3a05bd1461013e5780635fd4b08a146101715780637dd564111461017d57806389a69c0e14610187578063b387ef92146101bb578063b5c645bd146101f4578063be99a98014610270578063c3d014d6146102a8578063d93e7573146102dc57005b73ffffffffffffffffffffffffffffffffffffffff600435166000908152600160205260409020548060005260206000f35b6000808052602081f35b600435600090815260026020819052604090912001548060005260206000f35b600435600090815260026020908152604082205473ffffffffffffffffffffffffffffffffffffffff1680835291f35b600435600090815260026020908152604082206001015473ffffffffffffffffffffffffffffffffffffffff1680835291f35b60008060005260206000f35b6000808052602081f35b60005461030c9060043590602435903373ffffffffffffffffffffffffffffffffffffffff908116911614610569576105c9565b60005473ffffffffffffffffffffffffffffffffffffffff168073ffffffffffffffffffffffffffffffffffffffff1660005260206000f35b600435600090815260026020819052604090912080546001820154919092015473ffffffffffffffffffffffffffffffffffffffff9283169291909116908273ffffffffffffffffffffffffffffffffffffffff166000528173ffffffffffffffffffffffffffffffffffffffff166020528060405260606000f35b600054610312906004359060243590604435903373ffffffffffffffffffffffffffffffffffffffff90811691161461045457610523565b6000546103189060043590602435903373ffffffffffffffffffffffffffffffffffffffff90811691161461052857610565565b60005461031e90600435903373ffffffffffffffffffffffffffffffffffffffff90811691161461032457610451565b60006000f35b60006000f35b60006000f35b60006000f35b60008181526002602090815260408083205473ffffffffffffffffffffffffffffffffffffffff16835260019091529020548114610361576103e1565b6000818152600260205260408082205473ffffffffffffffffffffffffffffffffffffffff169183917ff63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a85459190a360008181526002602090815260408083205473ffffffffffffffffffffffffffffffffffffffff16835260019091528120555b600081815260026020819052604080832080547fffffffffffffffffffffffff00000000000000000000000000000000000000009081168255600182018054909116905590910182905582917fa6697e974e6a320f454390be03f74955e8978f1a6971ea6730542e37b66179bc91a25b50565b600083815260026020526040902080547fffffffffffffffffffffffff00000000000000000000000000000000000000001683179055806104bb57827fa6697e974e6a320f454390be03f74955e8978f1a6971ea6730542e37b66179bc60006040a2610522565b73ffffffffffffffffffffffffffffffffffffffff8216837ff63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a854560006040a373ffffffffffffffffffffffffffffffffffffffff821660009081526001602052604090208390555b5b505050565b600082815260026020819052604080832090910183905583917fa6697e974e6a320f454390be03f74955e8978f1a6971ea6730542e37b66179bc91a25b5050565b60008281526002602052604080822060010180547fffffffffffffffffffffffff0000000000000000000000000000000000000000168417905583917fa6697e974e6a320f454390be03f74955e8978f1a6971ea6730542e37b66179bc91a25b505056" } ], id: jsonRpcRequestId++ }); @@ -468,7 +462,7 @@ function continueRegistration(dappUrl, addr, callBack, checkOnly) //setRegister() jsonrpc: "2.0", method: "eth_sendTransaction", - params: [ { "from": deploymentDialog.worker.currentAccount, "gas": "0x" + gasCost, "to": '0x' + addr, "data": "0x89a69c0e" + crLevel + newCtrAddress } ], + params: [ { "from": deploymentDialog.worker.currentAccount, "gasPrice": deploymentGasPrice, "gas": "0x" + gasCost, "to": '0x' + addr, "data": "0x89a69c0e" + crLevel + newCtrAddress } ], id: jsonRpcRequestId++ }); @@ -501,7 +495,7 @@ function reserve(registrar, callBack) //reserve() jsonrpc: "2.0", method: "eth_sendTransaction", - params: [ { "from": deploymentDialog.worker.currentAccount, "gas": "0xfffff", "to": '0x' + registrar, "data": "0x432ced04" + paramTitle } ], + params: [ { "from": deploymentDialog.worker.currentAccount, "gasPrice": deploymentGasPrice, "gas": "0xfffff", "to": '0x' + registrar, "data": "0x432ced04" + paramTitle } ], id: jsonRpcRequestId++ }); rpcCall(requests, function (httpRequest, response) { @@ -524,7 +518,7 @@ function registerContentHash(registrar, callBack) //setContent() jsonrpc: "2.0", method: "eth_sendTransaction", - params: [ { "from": deploymentDialog.worker.currentAccount, "gas": "0x" + gasCost, "to": '0x' + registrar, "data": "0xc3d014d6" + paramTitle + deploymentDialog.packageStep.packageHash } ], + params: [ { "from": deploymentDialog.worker.currentAccount, "gasPrice": deploymentGasPrice, "gas": "0x" + gasCost, "to": '0x' + registrar, "data": "0xc3d014d6" + paramTitle + deploymentDialog.packageStep.packageHash } ], id: jsonRpcRequestId++ }); rpcCall(requests, function (httpRequest, response) { @@ -533,9 +527,10 @@ function registerContentHash(registrar, callBack) }); } -function registerToUrlHint(url, callback) +function registerToUrlHint(url, gasPrice, callback) { console.log("register url " + deploymentDialog.packageStep.packageHash + " " + url) + deploymentGasPrice = gasPrice deploymentStepChanged(qsTr("Registering application Resources...")) urlHintAddress(function(urlHint){ var requests = []; @@ -545,13 +540,13 @@ function registerToUrlHint(url, callback) //urlHint => suggestUrl jsonrpc: "2.0", method: "eth_sendTransaction", - params: [ { "to": '0x' + urlHint, "from": deploymentDialog.worker.currentAccount, "gas": "0x" + gasCost, "data": "0x584e86ad" + deploymentDialog.packageStep.packageHash + paramUrlHttp } ], + params: [ { "to": '0x' + urlHint, "gasPrice": deploymentGasPrice, "from": deploymentDialog.worker.currentAccount, "gas": "0x" + gasCost, "data": "0x584e86ad" + deploymentDialog.packageStep.packageHash + paramUrlHttp } ], id: jsonRpcRequestId++ }); rpcCall(requests, function (httpRequest, response) { projectModel.registerUrlTrHash = JSON.parse(response)[0].result - deploymentComplete(); + deploymentStepChanged(qsTr("Dapp resources has been registered. Please wait for verifications.")); if (callback) callback() }); diff --git a/mix/qml/js/TransactionHelper.js b/mix/qml/js/TransactionHelper.js index 33dadab9d..11cf80b11 100644 --- a/mix/qml/js/TransactionHelper.js +++ b/mix/qml/js/TransactionHelper.js @@ -33,7 +33,8 @@ function rpcCall(requests, callBack, error) { var errorText = qsTr("Unable to initiate request to the live network. Please verify your ethereum node is up.") + qsTr(" Error status: ") + httpRequest.status; console.log(errorText); - error(errorText); + if (error) + error(errorText); } else { diff --git a/test/libsolidity/SolidityWallet.cpp b/test/libsolidity/SolidityWallet.cpp index b5f0f7249..d87edd359 100644 --- a/test/libsolidity/SolidityWallet.cpp +++ b/test/libsolidity/SolidityWallet.cpp @@ -116,7 +116,7 @@ contract multiowned { } // Replaces an owner `_from` with another `_to`. - function changeOwner(address _from, address _to) onlymanyowners(sha3(msg.data, block.number)) external { + function changeOwner(address _from, address _to) onlymanyowners(sha3(msg.data)) external { if (isOwner(_to)) return; uint ownerIndex = m_ownerIndex[uint(_from)]; if (ownerIndex == 0) return; @@ -128,7 +128,7 @@ contract multiowned { OwnerChanged(_from, _to); } - function addOwner(address _owner) onlymanyowners(sha3(msg.data, block.number)) external { + function addOwner(address _owner) onlymanyowners(sha3(msg.data)) external { if (isOwner(_owner)) return; clearPending(); @@ -142,7 +142,7 @@ contract multiowned { OwnerAdded(_owner); } - function removeOwner(address _owner) onlymanyowners(sha3(msg.data, block.number)) external { + function removeOwner(address _owner) onlymanyowners(sha3(msg.data)) external { uint ownerIndex = m_ownerIndex[uint(_owner)]; if (ownerIndex == 0) return; if (m_required > m_numOwners - 1) return; @@ -154,7 +154,7 @@ contract multiowned { OwnerRemoved(_owner); } - function changeRequirement(uint _newRequired) onlymanyowners(sha3(msg.data, block.number)) external { + function changeRequirement(uint _newRequired) onlymanyowners(sha3(msg.data)) external { if (_newRequired > m_numOwners) return; m_required = _newRequired; clearPending(); @@ -275,16 +275,17 @@ contract daylimit is multiowned { // METHODS - // constructor - just records the present day's index. - function daylimit() { + // constructor - stores initial daily limit and records the present day's index. + function daylimit(uint _limit) { + m_dailyLimit = _limit; m_lastDay = today(); } // (re)sets the daily limit. needs many of the owners to confirm. doesn't alter the amount already spent today. - function setDailyLimit(uint _newLimit) onlymanyowners(sha3(msg.data, block.number)) external { + function setDailyLimit(uint _newLimit) onlymanyowners(sha3(msg.data)) external { m_dailyLimit = _newLimit; } // (re)sets the daily limit. needs many of the owners to confirm. doesn't alter the amount already spent today. - function resetSpentToday() onlymanyowners(sha3(msg.data, block.number)) external { + function resetSpentToday() onlymanyowners(sha3(msg.data)) external { m_spentToday = 0; } @@ -354,12 +355,14 @@ contract Wallet is multisig, multiowned, daylimit { // METHODS - // constructor - just pass on the owner array to the multiowned. - function Wallet(address[] _owners, uint _required) multiowned(_owners, _required) { + // constructor - just pass on the owner array to the multiowned and + // the limit to daylimit + function Wallet(address[] _owners, uint _required, uint _daylimit) + multiowned(_owners, _required) daylimit(_daylimit) { } // kills the contract sending everything to `_to`. - function kill(address _to) onlymanyowners(sha3(msg.data, block.number)) external { + function kill(address _to) onlymanyowners(sha3(msg.data)) external { suicide(_to); } @@ -424,7 +427,12 @@ static unique_ptr s_compiledWallet; class WalletTestFramework: public ExecutionFramework { protected: - void deployWallet(u256 const& _value = 0, vector const& _owners = vector{}, u256 _required = 1) + void deployWallet( + u256 const& _value = 0, + vector const& _owners = vector{}, + u256 _required = 1, + u256 _dailyLimit = 0 + ) { if (!s_compiledWallet) { @@ -434,7 +442,7 @@ protected: ETH_TEST_REQUIRE_NO_THROW(m_compiler.compile(m_optimize, m_optimizeRuns), "Compiling contract failed"); s_compiledWallet.reset(new bytes(m_compiler.getBytecode("Wallet"))); } - bytes args = encodeArgs(u256(0x40), _required, u256(_owners.size()), _owners); + bytes args = encodeArgs(u256(0x60), _required, _dailyLimit, u256(_owners.size()), _owners); sendMessage(*s_compiledWallet + args, true, _value); BOOST_REQUIRE(!m_output.empty()); } @@ -519,7 +527,7 @@ BOOST_AUTO_TEST_CASE(initial_owners) u256("0x0000000000000000000000004c9113886af165b2de069d6e99430647e94a9fff"), u256("0x0000000000000000000000003fb1cd2cd96c6d5c0b5eb3322d807b34482481d4") }; - deployWallet(0, owners, 4); + deployWallet(0, owners, 4, 2); BOOST_CHECK(callContractFunction("m_numOwners()") == encodeArgs(u256(8))); BOOST_CHECK(callContractFunction("isOwner(address)", h256(m_sender, h256::AlignRight)) == encodeArgs(true)); for (u256 const& owner: owners) @@ -554,7 +562,9 @@ BOOST_AUTO_TEST_CASE(multisig_value_transfer) BOOST_AUTO_TEST_CASE(daylimit) { deployWallet(200); + BOOST_REQUIRE(callContractFunction("m_dailyLimit()") == encodeArgs(u256(0))); BOOST_REQUIRE(callContractFunction("setDailyLimit(uint256)", h256(100)) == encodeArgs()); + BOOST_REQUIRE(callContractFunction("m_dailyLimit()") == encodeArgs(u256(100))); BOOST_REQUIRE(callContractFunction("addOwner(address)", h256(0x12)) == encodeArgs()); BOOST_REQUIRE(callContractFunction("addOwner(address)", h256(0x13)) == encodeArgs()); BOOST_REQUIRE(callContractFunction("addOwner(address)", h256(0x14)) == encodeArgs()); @@ -585,6 +595,14 @@ BOOST_AUTO_TEST_CASE(daylimit) BOOST_CHECK_EQUAL(m_state.balance(Address(0x05)), 90); } +BOOST_AUTO_TEST_CASE(daylimit_constructor) +{ + deployWallet(200, {}, 1, 20); + BOOST_REQUIRE(callContractFunction("m_dailyLimit()") == encodeArgs(u256(20))); + BOOST_REQUIRE(callContractFunction("setDailyLimit(uint256)", h256(30)) == encodeArgs()); + BOOST_REQUIRE(callContractFunction("m_dailyLimit()") == encodeArgs(u256(30))); +} + //@todo test data calls BOOST_AUTO_TEST_SUITE_END()