diff --git a/alethzero/Main.ui b/alethzero/Main.ui index 2ce00b0e3..97ed31aa9 100644 --- a/alethzero/Main.ui +++ b/alethzero/Main.ui @@ -95,8 +95,8 @@ - - + + about:blank @@ -1212,8 +1212,8 @@ 0 - - + + about:blank @@ -2035,7 +2035,7 @@ font-size: 14pt - false + true Claim Ether Presale &Wallet... diff --git a/alethzero/MainWin.cpp b/alethzero/MainWin.cpp index 0b3d6987d..e50860ee6 100644 --- a/alethzero/MainWin.cpp +++ b/alethzero/MainWin.cpp @@ -60,6 +60,11 @@ using namespace dev::p2p; using namespace dev::eth; namespace js = json_spirit; +#define Small "font-size: small; " +#define Mono "font-family: Ubuntu Mono, Monospace, Lucida Console, Courier New; font-weight: bold; " +#define Div(S) "
" +#define Span(S) "" + static void initUnits(QComboBox* _b) { for (auto n = (unsigned)units().size(); n-- != 0; ) @@ -452,7 +457,7 @@ void Main::eval(QString const& _js) else s = "unknown type"; m_consoleHistory.push_back(qMakePair(_js, s)); - s = "
"; + s = "" Div(Mono "position: absolute; bottom: 0; border: 0px; margin: 0px; width: 100%"); for (auto const& i: m_consoleHistory) s += "
>" + i.first.toHtmlEscaped() + "
" "
 " + i.second + "
"; @@ -755,7 +760,7 @@ void Main::on_importKey_triggered() void Main::on_importKeyFile_triggered() { - QString s = QFileDialog::getOpenFileName(this, "Import Account", QDir::homePath(), "JSON Files (*.json);;All Files (*)"); + QString s = QFileDialog::getOpenFileName(this, "Claim Account Contents", QDir::homePath(), "JSON Files (*.json);;All Files (*)"); try { js::mValue val; @@ -785,8 +790,12 @@ void Main::on_importKeyFile_triggered() if (std::find(m_myKeys.begin(), m_myKeys.end(), k) == m_myKeys.end()) { - m_myKeys.append(k); - keysChanged(); + if (m_myKeys.empty()) + { + m_myKeys.push_back(KeyPair::create()); + keysChanged(); + } + ethereum()->transact(k.sec(), ethereum()->balanceAt(k.address()) - gasPrice() * c_txGas, m_myKeys.back().address(), {}, c_txGas, gasPrice()); } else QMessageBox::warning(this, "Already Have Key", "Could not import the secret key: we already own this account."); @@ -1093,7 +1102,7 @@ void Main::refreshBlockChain() auto b = bc.block(h); for (auto const& i: RLP(b)[1]) { - Transaction t(i.data()); + Transaction t(i.data(), CheckSignature::Sender); if (bm || transactionMatch(filter, t)) { QString s = t.receiveAddress() ? @@ -1276,12 +1285,12 @@ void Main::on_transactionQueue_currentItemChanged() if (tx.data().size()) s << dev::memDump(tx.data(), 16, true); } - s << "
Hex: " << toHex(tx.rlp()) << "
"; + s << "
Hex: " Span(Mono) << toHex(tx.rlp()) << "
"; s << "
"; s << "
Log Bloom: " << receipt.bloom() << "
"; auto r = receipt.rlp(); s << "
Receipt: " << toString(RLP(r)) << "
"; - s << "
Receipt-Hex: " << toHex(receipt.rlp()) << "
"; + s << "
Receipt-Hex: " Span(Mono) << toHex(receipt.rlp()) << "
"; s << renderDiff(ethereum()->diff(i, 0)); // s << "Pre: " << fs.rootHash() << "
"; // s << "Post: " << ts.rootHash() << ""; @@ -1372,13 +1381,13 @@ void Main::on_blocks_currentItemChanged() for (auto const& i: block[1]) s << "
" << sha3(i.data()).abridged();// << ": " << i[1].toHash() << " [" << i[2].toInt() << " used]"; s << "
Post: " << info.stateRoot << ""; - s << "
Dump: " << toHex(block[0].data()) << ""; - s << "
Receipts-Hex: " << toHex(ethereum()->blockChain().receipts(h).rlp()) << "
"; + s << "
Dump: " Span(Mono) << toHex(block[0].data()) << ""; + s << "
Receipts-Hex: " Span(Mono) << toHex(ethereum()->blockChain().receipts(h).rlp()) << "
"; } else { unsigned txi = item->data(Qt::UserRole + 1).toInt(); - Transaction tx(block[1][txi].data()); + Transaction tx(block[1][txi].data(), CheckSignature::Sender); auto ss = tx.safeSender(); h256 th = sha3(rlpList(ss, tx.nonce())); TransactionReceipt receipt = ethereum()->blockChain().receipts(h).receipts[txi]; @@ -1407,12 +1416,12 @@ void Main::on_blocks_currentItemChanged() if (tx.data().size()) s << dev::memDump(tx.data(), 16, true); } - s << "
Hex: " << toHex(block[1][txi].data()) << "
"; + s << "
Hex: " Span(Mono) << toHex(block[1][txi].data()) << "
"; s << "
"; s << "
Log Bloom: " << receipt.bloom() << "
"; auto r = receipt.rlp(); s << "
Receipt: " << toString(RLP(r)) << "
"; - s << "
Receipt-Hex: " << toHex(receipt.rlp()) << "
"; + s << "
Receipt-Hex: " Span(Mono) << toHex(receipt.rlp()) << "
"; s << renderDiff(ethereum()->diff(txi, h)); ui->debugCurrent->setEnabled(true); ui->debugDumpState->setEnabled(true); @@ -1531,6 +1540,7 @@ void Main::on_contracts_currentItemChanged() for (auto const& i: storage) s << "@" << showbase << hex << prettyU256(i.first).toStdString() << "    " << showbase << hex << prettyU256(i.second).toStdString() << "
"; s << "

Body Code

" << disassemble(ethereum()->codeAt(address)); + s << Div(Mono) << toHex(ethereum()->codeAt(address)) << "
"; ui->contractInfo->appendHtml(QString::fromStdString(s.str())); } catch (dev::InvalidTrie) @@ -1640,13 +1650,18 @@ static shh::Topic topicFromText(QString _s) return ret; } - bool Main::sourceIsSolidity(string const& _source) { // TODO: Improve this heuristic return (_source.substr(0, 8) == "contract" || _source.substr(0, 5) == "//sol"); } +static bool sourceIsSerpent(string const& _source) +{ + // TODO: Improve this heuristic + return (_source.substr(0, 5) == "//ser"); +} + string const Main::getFunctionHashes(dev::solidity::CompilerStack const &_compiler, string const& _contractName) { @@ -1681,6 +1696,7 @@ void Main::on_data_textChanged() dev::solidity::CompilerStack compiler; try { +// compiler.addSources(dev::solidity::StandardSources); m_data = compiler.compile(src, m_enableOptimizer); solidity = "

Solidity

"; solidity += "
" + QString::fromStdString(compiler.getInterface()).replace(QRegExp("\\s"), "").toHtmlEscaped() + "
"; @@ -1698,23 +1714,23 @@ void Main::on_data_textChanged() solidity = "

Solidity

Uncaught exception.
"; } } - else + else if (sourceIsSerpent(src)) { - m_data = compileLLL(src, m_enableOptimizer, &errors); - if (errors.size()) + try { - try - { - m_data = dev::asBytes(::compile(src)); - for (auto& i: errors) - i = "(LLL " + i + ")"; - } - catch (string err) - { - errors.push_back("Serpent " + err); - } + m_data = dev::asBytes(::compile(src)); + for (auto& i: errors) + i = "(LLL " + i + ")"; } - else + catch (string err) + { + errors.push_back("Serpent " + err); + } + } + else + { + m_data = compileLLL(src, m_enableOptimizer, &errors); + if (errors.empty()) { auto asmcode = compileLLLToAsm(src, false); lll = "

Pre

" + QString::fromStdString(asmcode).toHtmlEscaped() + "
"; @@ -1732,7 +1748,7 @@ void Main::on_data_textChanged() for (auto const& i: errors) errs.append("
" + QString::fromStdString(i).toHtmlEscaped() + "
"); } - ui->code->setHtml(errs + lll + solidity + "

Code

" + QString::fromStdString(disassemble(m_data)).toHtmlEscaped()); + ui->code->setHtml(errs + lll + solidity + "

Code

" + QString::fromStdString(disassemble(m_data)).toHtmlEscaped() + "

Hex

" Div(Mono) + QString::fromStdString(toHex(m_data)) + "
"); ui->gas->setMinimum((qint64)Client::txGas(m_data, 0)); if (!ui->gas->isEnabled()) ui->gas->setValue(m_backupGas); @@ -1965,9 +1981,24 @@ void Main::on_debug_clicked() } } +bool beginsWith(Address _a, bytes const& _b) +{ + for (unsigned i = 0; i < min(20, _b.size()); ++i) + if (_a[i] != _b[i]) + return false; + return true; +} + void Main::on_create_triggered() { - m_myKeys.append(KeyPair::create()); + bool ok = true; + QString s = QInputDialog::getText(this, "Special Beginning?", "If you want a special key, enter some hex digits that it should begin with.\nNOTE: The more you enter, the longer generation will take.", QLineEdit::Normal, QString(), &ok); + if (!ok) + return; + KeyPair p; + while (!beginsWith(p.address(), asBytes(s.toStdString()))) + p = KeyPair::create(); + m_myKeys.append(p); keysChanged(); } diff --git a/libethereum/Account.h b/libethereum/Account.h index 7e2fc38ab..3f3a5d548 100644 --- a/libethereum/Account.h +++ b/libethereum/Account.h @@ -140,7 +140,8 @@ public: h256 codeHash() const { assert(!isFreshCode()); return m_codeHash; } /// Sets the code of the account. Must only be called when isFreshCode() returns true. - void setCode(bytesConstRef _code) { assert(isFreshCode()); m_codeCache = _code.toBytes(); } + void setCode(bytes&& _code) { assert(isFreshCode()); m_codeCache = _code; } + void setCode(bytes const& _code) { assert(isFreshCode()); m_codeCache = _code; } /// @returns true if the account's code is available through code(). bool codeCacheValid() const { return m_codeHash == EmptySHA3 || m_codeHash == c_contractConceptionCodeHash || m_codeCache.size(); } diff --git a/libethereum/BlockChain.cpp b/libethereum/BlockChain.cpp index b1938d54d..45d79701a 100644 --- a/libethereum/BlockChain.cpp +++ b/libethereum/BlockChain.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include "State.h" #include "Defaults.h" using namespace std; @@ -56,6 +57,7 @@ std::map const& dev::eth::genesisState() { static std::map s_ret; if (s_ret.empty()) + { // Initialise. for (auto i: vector({ "51ba59315b3a95761d0863b05ccc7a7f54703d99", @@ -68,6 +70,7 @@ std::map const& dev::eth::genesisState() "e4157b34ea9615cfbde6b4fda419828124b70c78" })) s_ret[Address(fromHex(i))] = Account(u256(1) << 200, Account::NormalCreation); + } return s_ret; } diff --git a/libethereum/Client.cpp b/libethereum/Client.cpp index 41f0bcfbc..fcc18c2d1 100644 --- a/libethereum/Client.cpp +++ b/libethereum/Client.cpp @@ -642,7 +642,7 @@ Transaction Client::transaction(h256 _blockHash, unsigned _i) const { auto bl = m_bc.block(_blockHash); RLP b(bl); - return Transaction(b[1][_i].data()); + return Transaction(b[1][_i].data(), CheckSignature::Range); } BlockInfo Client::uncle(h256 _blockHash, unsigned _i) const diff --git a/libethereum/Executive.cpp b/libethereum/Executive.cpp index 4eed2b284..dc2e62824 100644 --- a/libethereum/Executive.cpp +++ b/libethereum/Executive.cpp @@ -53,7 +53,7 @@ void Executive::accrueSubState(SubState& _parentContext) bool Executive::setup(bytesConstRef _rlp) { // Entry point for a user-executed transaction. - m_t = Transaction(_rlp); + m_t = Transaction(_rlp, CheckSignature::Sender); // Avoid invalid transactions. auto nonceReq = m_s.transactionsFrom(m_t.sender()); @@ -197,7 +197,7 @@ bool Executive::go(OnOpFunc const& _onOp) m_endGas -= m_out.size() * c_createDataGas; else m_out.reset(); - m_s.m_cache[m_newAddress].setCode(m_out); + m_s.m_cache[m_newAddress].setCode(m_out.toBytes()); } } catch (StepsDone const&) diff --git a/libethereum/State.cpp b/libethereum/State.cpp index 9ed9a3a3a..5232144c2 100644 --- a/libethereum/State.cpp +++ b/libethereum/State.cpp @@ -395,7 +395,7 @@ bool State::cull(TransactionQueue& _tq) const { try { - Transaction t(i.second); + Transaction t(i.second, CheckSignature::Sender); if (t.nonce() <= transactionsFrom(t.sender())) { _tq.drop(i.first); diff --git a/libethereum/Transaction.cpp b/libethereum/Transaction.cpp index 24c56930e..78abd6267 100644 --- a/libethereum/Transaction.cpp +++ b/libethereum/Transaction.cpp @@ -30,7 +30,7 @@ using namespace dev::eth; #define ETH_ADDRESS_DEBUG 0 -Transaction::Transaction(bytesConstRef _rlpData, bool _checkSender) +Transaction::Transaction(bytesConstRef _rlpData, CheckSignature _checkSig) { int field = 0; RLP rlp(_rlpData); @@ -47,7 +47,9 @@ Transaction::Transaction(bytesConstRef _rlpData, bool _checkSender) h256 r = rlp[field = 7].toInt(); h256 s = rlp[field = 8].toInt(); m_vrs = SignatureStruct{ r, s, v }; - if (_checkSender) + if (_checkSig >= CheckSignature::Range && !m_vrs.isValid()) + BOOST_THROW_EXCEPTION(InvalidSignature()); + if (_checkSig == CheckSignature::Sender) m_sender = sender(); } catch (Exception& _e) diff --git a/libethereum/Transaction.h b/libethereum/Transaction.h index 490a2ac68..0f88a4bc0 100644 --- a/libethereum/Transaction.h +++ b/libethereum/Transaction.h @@ -37,6 +37,13 @@ enum IncludeSignature WithSignature = 1, ///< Do include a signature. }; +enum class CheckSignature +{ + None, + Range, + Sender +}; + /// Encodes a transaction, ready to be exported to or freshly imported from RLP. class Transaction { @@ -57,10 +64,10 @@ public: Transaction(u256 _value, u256 _gasPrice, u256 _gas, bytes const& _data): m_type(ContractCreation), m_value(_value), m_gasPrice(_gasPrice), m_gas(_gas), m_data(_data) {} /// Constructs a transaction from the given RLP. - explicit Transaction(bytesConstRef _rlp, bool _checkSender = false); + explicit Transaction(bytesConstRef _rlp, CheckSignature _checkSig); /// Constructs a transaction from the given RLP. - explicit Transaction(bytes const& _rlp, bool _checkSender = false): Transaction(&_rlp, _checkSender) {} + explicit Transaction(bytes const& _rlp, CheckSignature _checkSig): Transaction(&_rlp, _checkSig) {} /// Checks equality of transactions. diff --git a/libethereum/TransactionQueue.cpp b/libethereum/TransactionQueue.cpp index 6248f38c9..5701fc4a5 100644 --- a/libethereum/TransactionQueue.cpp +++ b/libethereum/TransactionQueue.cpp @@ -42,7 +42,7 @@ bool TransactionQueue::import(bytesConstRef _transactionRLP) // Check validity of _transactionRLP as a transaction. To do this we just deserialise and attempt to determine the sender. // If it doesn't work, the signature is bad. // The transaction's nonce may yet be invalid (or, it could be "valid" but we may be missing a marginally older transaction). - Transaction t(_transactionRLP, true); + Transaction t(_transactionRLP, CheckSignature::Sender); UpgradeGuard ul(l); // If valid, append to blocks. @@ -69,14 +69,14 @@ void TransactionQueue::setFuture(std::pair const& _t) if (m_current.count(_t.first)) { m_current.erase(_t.first); - m_unknown.insert(make_pair(Transaction(_t.second).sender(), _t)); + m_unknown.insert(make_pair(Transaction(_t.second, CheckSignature::Sender).sender(), _t)); } } void TransactionQueue::noteGood(std::pair const& _t) { WriteGuard l(m_lock); - auto r = m_unknown.equal_range(Transaction(_t.second).sender()); + auto r = m_unknown.equal_range(Transaction(_t.second, CheckSignature::Sender).sender()); for (auto it = r.first; it != r.second; ++it) m_current.insert(it->second); m_unknown.erase(r.first, r.second); diff --git a/liblll/CodeFragment.cpp b/liblll/CodeFragment.cpp index 2c200caa5..1e7766434 100644 --- a/liblll/CodeFragment.cpp +++ b/liblll/CodeFragment.cpp @@ -266,7 +266,6 @@ void CodeFragment::constructOperation(sp::utree const& _t, CompilerState& _s) } ++ii; } - } else if (us == "LIT") { diff --git a/liblll/CodeFragment.h b/liblll/CodeFragment.h index b24b474da..554f90b46 100644 --- a/liblll/CodeFragment.h +++ b/liblll/CodeFragment.h @@ -61,3 +61,4 @@ static const CodeFragment NullCodeFragment; } } + diff --git a/libsolidity/AST.cpp b/libsolidity/AST.cpp index 2cb738d30..826673670 100644 --- a/libsolidity/AST.cpp +++ b/libsolidity/AST.cpp @@ -82,7 +82,7 @@ map, FunctionDefinition const*> ContractDefinition::getInterfaceFun FunctionDefinition const* ContractDefinition::getConstructor() const { for (ASTPointer const& f: m_definedFunctions) - if (f->getName() == getName()) + if (f->isConstructor()) return f.get(); return nullptr; } @@ -95,7 +95,7 @@ void ContractDefinition::checkIllegalOverrides() const for (ContractDefinition const* contract: getLinearizedBaseContracts()) for (ASTPointer const& function: contract->getDefinedFunctions()) { - if (function->getName() == contract->getName()) + if (function->isConstructor()) continue; // constructors can neither be overriden nor override anything FunctionDefinition const*& override = functions[function->getName()]; if (!override) @@ -115,8 +115,7 @@ vector, FunctionDefinition const*>> const& ContractDefinition: m_interfaceFunctionList.reset(new vector, FunctionDefinition const*>>()); for (ContractDefinition const* contract: getLinearizedBaseContracts()) for (ASTPointer const& f: contract->getDefinedFunctions()) - if (f->isPublic() && f->getName() != contract->getName() && - functionsSeen.count(f->getName()) == 0) + if (f->isPublic() && !f->isConstructor() && functionsSeen.count(f->getName()) == 0) { functionsSeen.insert(f->getName()); FixedHash<4> hash(dev::sha3(f->getCanonicalSignature())); diff --git a/libsolidity/AST.h b/libsolidity/AST.h index 8079348cf..754e9254c 100755 --- a/libsolidity/AST.h +++ b/libsolidity/AST.h @@ -281,12 +281,13 @@ class FunctionDefinition: public Declaration public: FunctionDefinition(Location const& _location, ASTPointer const& _name, bool _isPublic, + bool _isConstructor, ASTPointer const& _documentation, ASTPointer const& _parameters, bool _isDeclaredConst, ASTPointer const& _returnParameters, ASTPointer const& _body): - Declaration(_location, _name), m_isPublic(_isPublic), + Declaration(_location, _name), m_isPublic(_isPublic), m_isConstructor(_isConstructor), m_parameters(_parameters), m_isDeclaredConst(_isDeclaredConst), m_returnParameters(_returnParameters), @@ -298,6 +299,7 @@ public: virtual void accept(ASTConstVisitor& _visitor) const override; bool isPublic() const { return m_isPublic; } + bool isConstructor() const { return m_isConstructor; } bool isDeclaredConst() const { return m_isDeclaredConst; } std::vector> const& getParameters() const { return m_parameters->getParameters(); } ParameterList const& getParameterList() const { return *m_parameters; } @@ -321,6 +323,7 @@ public: private: bool m_isPublic; + bool m_isConstructor; ASTPointer m_parameters; bool m_isDeclaredConst; ASTPointer m_returnParameters; diff --git a/libsolidity/CallGraph.cpp b/libsolidity/CallGraph.cpp index 88d874f3b..a671796bd 100644 --- a/libsolidity/CallGraph.cpp +++ b/libsolidity/CallGraph.cpp @@ -38,6 +38,7 @@ void CallGraph::addNode(ASTNode const& _node) set const& CallGraph::getCalls() { + computeCallGraph(); return m_functionsSeen; } @@ -45,8 +46,7 @@ void CallGraph::computeCallGraph() { while (!m_workQueue.empty()) { - FunctionDefinition const* fun = m_workQueue.front(); - fun->accept(*this); + m_workQueue.front()->accept(*this); m_workQueue.pop(); } } @@ -55,7 +55,12 @@ bool CallGraph::visit(Identifier const& _identifier) { FunctionDefinition const* fun = dynamic_cast(_identifier.getReferencedDeclaration()); if (fun) + { + if (m_overrideResolver) + fun = (*m_overrideResolver)(fun->getName()); + solAssert(fun, "Error finding override for function " + fun->getName()); addFunction(*fun); + } return true; } diff --git a/libsolidity/CallGraph.h b/libsolidity/CallGraph.h index e3558fc25..90176e7e6 100644 --- a/libsolidity/CallGraph.h +++ b/libsolidity/CallGraph.h @@ -22,6 +22,7 @@ #include #include +#include #include #include @@ -38,8 +39,11 @@ namespace solidity class CallGraph: private ASTConstVisitor { public: + using OverrideResolver = std::function; + + CallGraph(OverrideResolver const& _overrideResolver): m_overrideResolver(&_overrideResolver) {} + void addNode(ASTNode const& _node); - void computeCallGraph(); std::set const& getCalls(); @@ -48,8 +52,10 @@ private: virtual bool visit(Identifier const& _identifier) override; virtual bool visit(MemberAccess const& _memberAccess) override; + void computeCallGraph(); void addFunction(FunctionDefinition const& _function); + OverrideResolver const* m_overrideResolver; std::set m_functionsSeen; std::queue m_workQueue; }; diff --git a/libsolidity/Compiler.cpp b/libsolidity/Compiler.cpp index 36316b9ae..5a434a71b 100644 --- a/libsolidity/Compiler.cpp +++ b/libsolidity/Compiler.cpp @@ -43,13 +43,13 @@ void Compiler::compileContract(ContractDefinition const& _contract, for (ContractDefinition const* contract: _contract.getLinearizedBaseContracts()) for (ASTPointer const& function: contract->getDefinedFunctions()) - if (function->getName() != contract->getName()) // don't add the constructor here + if (!function->isConstructor()) m_context.addFunction(*function); appendFunctionSelector(_contract); for (ContractDefinition const* contract: _contract.getLinearizedBaseContracts()) for (ASTPointer const& function: contract->getDefinedFunctions()) - if (function->getName() != contract->getName()) // don't add the constructor here + if (!function->isConstructor()) function->accept(*this); // Swap the runtime context with the creation-time context @@ -93,11 +93,30 @@ void Compiler::packIntoContractCreator(ContractDefinition const& _contract, Comp } } - //@TODO add virtual functions - neededFunctions = getFunctionsCalled(nodesUsedInConstructors); + auto overrideResolver = [&](string const& _name) -> FunctionDefinition const* + { + for (ContractDefinition const* contract: bases) + for (ASTPointer const& function: contract->getDefinedFunctions()) + if (!function->isConstructor() && function->getName() == _name) + return function.get(); + return nullptr; + }; + + neededFunctions = getFunctionsCalled(nodesUsedInConstructors, overrideResolver); + // First add all overrides (or the functions themselves if there is no override) + for (FunctionDefinition const* fun: neededFunctions) + { + FunctionDefinition const* override = nullptr; + if (!fun->isConstructor()) + override = overrideResolver(fun->getName()); + if (!!override && neededFunctions.count(override)) + m_context.addFunction(*override); + } + // now add the rest for (FunctionDefinition const* fun: neededFunctions) - m_context.addFunction(*fun); + if (fun->isConstructor() || overrideResolver(fun->getName()) != fun) + m_context.addFunction(*fun); // Call constructors in base-to-derived order. // The Constructor for the most derived contract is called later. @@ -159,10 +178,10 @@ void Compiler::appendConstructorCall(FunctionDefinition const& _constructor) m_context << returnTag; } -set Compiler::getFunctionsCalled(set const& _nodes) +set Compiler::getFunctionsCalled(set const& _nodes, + function const& _resolveOverrides) { - // TODO this does not add virtual functions - CallGraph callgraph; + CallGraph callgraph(_resolveOverrides); for (ASTNode const* node: _nodes) callgraph.addNode(*node); return callgraph.getCalls(); diff --git a/libsolidity/Compiler.h b/libsolidity/Compiler.h index ea05f38ee..2bae6b397 100644 --- a/libsolidity/Compiler.h +++ b/libsolidity/Compiler.h @@ -21,6 +21,7 @@ */ #include +#include #include #include @@ -49,7 +50,9 @@ private: std::vector> const& _arguments); void appendConstructorCall(FunctionDefinition const& _constructor); /// Recursively searches the call graph and returns all functions referenced inside _nodes. - std::set getFunctionsCalled(std::set const& _nodes); + /// _resolveOverride is called to resolve virtual function overrides. + std::set getFunctionsCalled(std::set const& _nodes, + std::function const& _resolveOverride); void appendFunctionSelector(ContractDefinition const& _contract); /// Creates code that unpacks the arguments for the given function, from memory if /// @a _fromMemory is true, otherwise from call data. @returns the size of the data in bytes. diff --git a/libsolidity/CompilerStack.cpp b/libsolidity/CompilerStack.cpp index 790eb983a..c05756167 100644 --- a/libsolidity/CompilerStack.cpp +++ b/libsolidity/CompilerStack.cpp @@ -21,6 +21,7 @@ * Full-stack compiler that converts a source code string to bytecode. */ +#include #include #include #include @@ -123,9 +124,49 @@ void CompilerStack::compile(bool _optimize) } } +string CompilerStack::expanded(string const& _sourceCode) +{ + // TODO: populate some nicer way. + static const map c_requires = { + { "Config", "contract Config{function lookup(uint256 service)constant returns(address a){}function kill(){}function unregister(uint256 id){}function register(uint256 id,address service){}}" }, + { "owned", "contract owned{function owned(){owner = msg.sender;}address owner;}" }, + { "mortal", "#require owned\ncontract mortal is owned {function kill() { if (msg.sender == owner) suicide(owner); }}" }, + { "NameReg", "contract NameReg{function register(string32 name){}function addressOf(string32 name)constant returns(address addr){}function unregister(){}function nameOf(address addr)constant returns(string32 name){}}" }, + { "named", "#require Config NameReg\ncontract named is mortal, owned {function named(string32 name) {NameReg(Config().lookup(1)).register(name);}" }, + { "std", "#require owned mortal Config NameReg named" }, + }; + string sub; + set got; + function localExpanded; + localExpanded = [&](string const& s) -> string + { + string ret = s; + for (size_t p = 0; p != string::npos;) + if ((p = ret.find("#require ")) != string::npos) + { + string n = ret.substr(p + 9, ret.find_first_of('\n', p + 9) - p - 9); + ret.replace(p, n.size() + 9, ""); + vector rs; + boost::split(rs, n, boost::is_any_of(" \t,"), boost::token_compress_on); + for (auto const& r: rs) + if (!got.count(r)) + { + if (c_requires.count(r)) + sub.append("\n" + localExpanded(c_requires.at(r)) + "\n"); + got.insert(r); + } + } + // TODO: remove once we have genesis contracts. + else if ((p = ret.find("Config()")) != string::npos) + ret.replace(p, 8, "Config(0x661005d2720d855f1d9976f88bb10c1a3398c77f)"); + return ret; + }; + return sub + localExpanded(_sourceCode); +} + bytes const& CompilerStack::compile(string const& _sourceCode, bool _optimize) { - parse(_sourceCode); + parse(expanded(_sourceCode)); compile(_optimize); return getBytecode(); } diff --git a/libsolidity/CompilerStack.h b/libsolidity/CompilerStack.h index aa55abe50..68c82a350 100644 --- a/libsolidity/CompilerStack.h +++ b/libsolidity/CompilerStack.h @@ -138,6 +138,10 @@ private: Contract(); }; + /// Expand source code with preprocessor-like includes. + /// @todo Replace with better framework. + std::string expanded(std::string const& _sourceCode); + void reset(bool _keepSources = false); void resolveImports(); diff --git a/libsolidity/InterfaceHandler.cpp b/libsolidity/InterfaceHandler.cpp index 4c7ae5f45..1adce8cb8 100644 --- a/libsolidity/InterfaceHandler.cpp +++ b/libsolidity/InterfaceHandler.cpp @@ -351,9 +351,15 @@ void InterfaceHandler::parseDocString(std::string const& _string, CommentOwner _ currPos = appendDocTag(currPos, end, _owner); else if (currPos != end) { - if (nlPos == end) //end of text + // if it begins without a tag then consider it as @notice + if (currPos == _string.begin()) + { + currPos = parseDocTag(currPos, end, "notice", CommentOwner::FUNCTION); + continue; + } + else if (nlPos == end) //end of text return; - // else skip the line if a newline was found + // else skip the line if a newline was found and we get here currPos = nlPos + 1; } } diff --git a/libsolidity/Parser.cpp b/libsolidity/Parser.cpp index c0ca1abb2..fcabdb29b 100644 --- a/libsolidity/Parser.cpp +++ b/libsolidity/Parser.cpp @@ -112,9 +112,9 @@ ASTPointer Parser::parseImportDirective() ASTPointer Parser::parseContractDefinition() { ASTNodeFactory nodeFactory(*this); - ASTPointer docstring; + ASTPointer docString; if (m_scanner->getCurrentCommentLiteral() != "") - docstring = make_shared(m_scanner->getCurrentCommentLiteral()); + docString = make_shared(m_scanner->getCurrentCommentLiteral()); expectToken(Token::CONTRACT); ASTPointer name = expectIdentifierToken(); vector> baseContracts; @@ -142,7 +142,7 @@ ASTPointer Parser::parseContractDefinition() expectToken(Token::COLON); } else if (currentToken == Token::FUNCTION) - functions.push_back(parseFunctionDefinition(visibilityIsPublic)); + functions.push_back(parseFunctionDefinition(visibilityIsPublic, name.get())); else if (currentToken == Token::STRUCT) structs.push_back(parseStructDefinition()); else if (currentToken == Token::IDENTIFIER || currentToken == Token::MAPPING || @@ -157,7 +157,7 @@ ASTPointer Parser::parseContractDefinition() } nodeFactory.markEndPosition(); expectToken(Token::RBRACE); - return nodeFactory.createNode(name, docstring, baseContracts, structs, + return nodeFactory.createNode(name, docString, baseContracts, structs, stateVariables, functions); } @@ -178,7 +178,7 @@ ASTPointer Parser::parseInheritanceSpecifier() return nodeFactory.createNode(name, arguments); } -ASTPointer Parser::parseFunctionDefinition(bool _isPublic) +ASTPointer Parser::parseFunctionDefinition(bool _isPublic, ASTString const* _contractName) { ASTNodeFactory nodeFactory(*this); ASTPointer docstring; @@ -210,7 +210,8 @@ ASTPointer Parser::parseFunctionDefinition(bool _isPublic) } ASTPointer block = parseBlock(); nodeFactory.setEndPositionFromNode(block); - return nodeFactory.createNode(name, _isPublic, docstring, + bool const c_isConstructor = (_contractName && *name == *_contractName); + return nodeFactory.createNode(name, _isPublic, c_isConstructor, docstring, parameters, isDeclaredConst, returnParameters, block); } diff --git a/libsolidity/Parser.h b/libsolidity/Parser.h index 1b7a980ff..5905a0420 100644 --- a/libsolidity/Parser.h +++ b/libsolidity/Parser.h @@ -50,7 +50,7 @@ private: ASTPointer parseImportDirective(); ASTPointer parseContractDefinition(); ASTPointer parseInheritanceSpecifier(); - ASTPointer parseFunctionDefinition(bool _isPublic); + ASTPointer parseFunctionDefinition(bool _isPublic, ASTString const* _contractName); ASTPointer parseStructDefinition(); ASTPointer parseVariableDeclaration(bool _allowVar); ASTPointer parseTypeName(bool _allowVar); diff --git a/libsolidity/Types.cpp b/libsolidity/Types.cpp index c6d8b62fc..2446c513c 100644 --- a/libsolidity/Types.cpp +++ b/libsolidity/Types.cpp @@ -716,7 +716,7 @@ MemberList const& TypeType::getMembers() const // We are accessing the type of a base contract, so add all public and private // functions. Note that this does not add inherited functions on purpose. for (ASTPointer const& f: contract.getDefinedFunctions()) - if (f->getName() != contract.getName()) + if (!f->isConstructor()) members[f->getName()] = make_shared(*f); } m_members.reset(new MemberList(members)); diff --git a/neth/main.cpp b/neth/main.cpp index 638e9c624..abd045b1c 100644 --- a/neth/main.cpp +++ b/neth/main.cpp @@ -305,7 +305,6 @@ int main(int argc, char** argv) unsigned short remotePort = 30303; string dbPath; unsigned mining = ~(unsigned)0; - (void)mining; NodeMode mode = NodeMode::Full; unsigned peers = 5; #if ETH_JSONRPC @@ -441,6 +440,8 @@ int main(int argc, char** argv) web3.connect(Host::pocHost()); if (remoteHost.size()) web3.connect(remoteHost, remotePort); + if (mining) + c->startMining(); #if ETH_JSONRPC shared_ptr jsonrpcServer; @@ -885,7 +886,7 @@ int main(int argc, char** argv) auto b = bc.block(h); for (auto const& i: RLP(b)[1]) { - Transaction t(i.data()); + Transaction t(i.data(), CheckSignature::Sender); auto s = t.receiveAddress() ? boost::format(" %1% %2%> %3%: %4% [%5%]") % toString(t.safeSender()) % diff --git a/test/SolidityEndToEndTest.cpp b/test/SolidityEndToEndTest.cpp index cba926d6d..cf04edaad 100644 --- a/test/SolidityEndToEndTest.cpp +++ b/test/SolidityEndToEndTest.cpp @@ -1610,6 +1610,28 @@ BOOST_AUTO_TEST_CASE(function_usage_in_constructor_arguments) BOOST_CHECK(callContractFunction("getA()") == encodeArgs(2)); } +BOOST_AUTO_TEST_CASE(virtual_function_usage_in_constructor_arguments) +{ + char const* sourceCode = R"( + contract BaseBase { + uint m_a; + function BaseBase(uint a) { + m_a = a; + } + function overridden() returns (uint r) { return 1; } + function g() returns (uint r) { return overridden(); } + } + contract Base is BaseBase(BaseBase.g()) { + } + contract Derived is Base() { + function getA() returns (uint r) { return m_a; } + function overridden() returns (uint r) { return 2; } + } + )"; + compileAndRun(sourceCode, 0, "Derived"); + BOOST_CHECK(callContractFunction("getA()") == encodeArgs(2)); +} + BOOST_AUTO_TEST_CASE(constructor_argument_overriding) { char const* sourceCode = R"( diff --git a/test/SolidityNatspecJSON.cpp b/test/SolidityNatspecJSON.cpp index 5cec0444c..743651d54 100644 --- a/test/SolidityNatspecJSON.cpp +++ b/test/SolidityNatspecJSON.cpp @@ -506,17 +506,35 @@ BOOST_AUTO_TEST_CASE(dev_title_at_function_error) BOOST_CHECK_THROW(checkNatspec(sourceCode, natspec, false), DocstringParsingError); } -// test for bug where having no tags in docstring would cause infinite loop -BOOST_AUTO_TEST_CASE(natspec_no_tags) +BOOST_AUTO_TEST_CASE(natspec_notice_without_tag) { char const* sourceCode = "contract test {\n" " /// I do something awesome\n" - " function mul(uint a, uint second) returns(uint d) { return a * 7 + second; }\n" + " function mul(uint a) returns(uint d) { return a * 7; }\n" "}\n"; - char const* natspec = "{\"methods\": {}}"; + char const* natspec = "{" + "\"methods\":{" + " \"mul(uint256)\":{ \"notice\": \"I do something awesome\"}" + "}}"; + + checkNatspec(sourceCode, natspec, true); +} - checkNatspec(sourceCode, natspec, false); +BOOST_AUTO_TEST_CASE(natspec_multiline_notice_without_tag) +{ + char const* sourceCode = "contract test {\n" + " /// I do something awesome\n" + " /// which requires two lines to explain\n" + " function mul(uint a) returns(uint d) { return a * 7; }\n" + "}\n"; + + char const* natspec = "{" + "\"methods\":{" + " \"mul(uint256)\":{ \"notice\": \"I do something awesome which requires two lines to explain\"}" + "}}"; + + checkNatspec(sourceCode, natspec, true); } BOOST_AUTO_TEST_SUITE_END() diff --git a/test/TestHelper.cpp b/test/TestHelper.cpp index 355a5080d..45c56f6ae 100644 --- a/test/TestHelper.cpp +++ b/test/TestHelper.cpp @@ -115,7 +115,7 @@ void ImportTest::importState(json_spirit::mObject& _o, State& _state) if (code.size()) { _state.m_cache[address] = Account(toInt(o["balance"]), Account::ContractConception); - _state.m_cache[address].setCode(bytesConstRef(&code)); + _state.m_cache[address].setCode(code); } else _state.m_cache[address] = Account(toInt(o["balance"]), Account::NormalCreation);