Browse Source

A first version of Natspec warning popup

- Runtime Contract code hash can now be retrieved from the Compiler

- Using the hash the Natspec handler stores and later retrieves Natspec
  JSON for a given contract.
cl-refactor
Lefteris Karapetsas 10 years ago
parent
commit
18dd4515bd
  1. 10
      alethzero/MainWin.cpp
  2. 1
      alethzero/MainWin.h
  3. 17
      alethzero/NatspecHandler.cpp
  4. 11
      alethzero/NatspecHandler.h
  5. 33
      alethzero/OurWebThreeStubServer.cpp
  6. 3
      libdevcore/CommonData.h
  7. 76
      libjsqrc/ethereumjs/example/natspec_contract.html
  8. 5
      libsolidity/Compiler.cpp
  9. 2
      libsolidity/Compiler.h
  10. 20
      libsolidity/CompilerStack.cpp
  11. 10
      libsolidity/CompilerStack.h

10
alethzero/MainWin.cpp

@ -1887,8 +1887,11 @@ void Main::on_send_clicked()
{
m_data = compiler.compile(src, m_enableOptimizer);
for (std::string& s: compiler.getContractNames())
m_natspecDB.add(compiler.getContractCodeHash(s),
{
h256 contractHash = compiler.getContractCodeHash(s);
m_natspecDB.add(contractHash,
compiler.getMetadata(s, dev::solidity::DocumentationType::NATSPEC_USER));
}
}
catch (...)
@ -2286,6 +2289,11 @@ std::string Main::lookupNatSpec(dev::h256 const& _contractHash) const
return m_natspecDB.retrieve(_contractHash);
}
std::string Main::lookupNatSpecUserNotice(dev::h256 const& _contractHash, std::string const& _methodName)
{
return m_natspecDB.getUserNotice(_contractHash, _methodName);
}
void Main::refreshWhispers()
{
ui->whispers->clear();

1
alethzero/MainWin.h

@ -83,6 +83,7 @@ public:
std::shared_ptr<dev::shh::WhisperHost> whisper() const { return m_webThree->whisper(); }
std::string lookupNatSpec(dev::h256 const& _contractHash) const;
std::string lookupNatSpecUserNotice(dev::h256 const& _contractHash, std::string const& _methodName);
QList<dev::KeyPair> owned() const { return m_myIdentities + m_myKeys; }

17
alethzero/NatspecHandler.cpp

@ -25,6 +25,8 @@
#include <libethereum/Defaults.h>
#include <libdevcore/Common.h>
#include <libdevcore/Log.h>
using namespace dev;
using namespace dev::eth;
@ -55,4 +57,19 @@ std::string NatspecHandler::retrieve(dev::h256 const& _contractHash) const
}
std::string NatspecHandler::getUserNotice(std::string const& json, std::string const& _methodName)
{
Json::Value natspec, userNotice;
std::string retStr;
m_reader.parse(json, natspec);
retStr = natspec["methods"][_methodName]["notice"].toStyledString();
return (retStr == "null\n") ? "" : retStr;
}
std::string NatspecHandler::getUserNotice(dev::h256 const& _contractHash, std::string const& _methodName)
{
return getUserNotice(retrieve(_contractHash), _methodName);
}

11
alethzero/NatspecHandler.h

@ -26,6 +26,7 @@
#pragma warning(disable: 4100 4267)
#include <leveldb/db.h>
#pragma warning(pop)
#include <jsoncpp/json/json.h>
#include <libdevcore/FixedHash.h>
namespace ldb = leveldb;
@ -35,11 +36,21 @@ class NatspecHandler
public:
NatspecHandler();
/// Stores locally in a levelDB a key value pair of contract code hash to natspec documentation
void add(dev::h256 const& _contractHash, std::string const& _doc);
/// Retrieves the natspec documentation as a string given a contract code hash
std::string retrieve(dev::h256 const& _contractHash) const;
/// Given a json natspec string, retrieve the user notice string
std::string getUserNotice(std::string const& json, std::string const& _methodName);
/// Given a contract code hash, retrieve the natspec documentation's user notice for that contract
/// @returns The user notice or an empty string if no natspec for the contract exists
/// or if the existing natspec does not document the @c _methodName
std::string getUserNotice(dev::h256 const& _contractHash, std::string const& _methodName);
private:
ldb::ReadOptions m_readOptions;
ldb::WriteOptions m_writeOptions;
ldb::DB* m_db;
Json::Reader m_reader;
};

33
alethzero/OurWebThreeStubServer.cpp

@ -43,8 +43,6 @@ std::string OurWebThreeStubServer::shh_newIdentity()
bool OurWebThreeStubServer::authenticate(dev::TransactionSkeleton const& _t) const
{
// return true;
// To get the balance of the sender
cnote << "Sender has ETH: " << m_web3->ethereum()->postState().balance(_t.from);
@ -57,18 +55,31 @@ bool OurWebThreeStubServer::authenticate(dev::TransactionSkeleton const& _t) con
return true; // or whatever.
}
std::string natspecJson = m_main->lookupNatSpec(contractCodeHash);
//LTODO: Just for debugging here
cnote << "Contract hash:\n" << contractCodeHash;
cnote << "Transaction Value:\n" << "0x" + toHex(_t.value);
cnote << "Transaction Data:\n" << "0x" + toHex(_t.data);
if (natspecJson.empty())
//LTODO: Actually find and use the method name here
std::string userNotice = m_main->lookupNatSpecUserNotice(contractCodeHash, "multiply");
if (userNotice.empty())
{
// TODO: HUGE warning - we don't know what this will do!
return false; // or whatever.
QMessageBox userInput;
userInput.setText("Unverified Pending Transaction");
userInput.setInformativeText("An undocumented transaction is about to be executed."
"Are you really sure you want to go through with it?");
userInput.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel);
userInput.setDefaultButton(QMessageBox::Cancel);
return userInput.exec() == QMessageBox::Ok;
}
// otherwise it's a transaction to contract for which we have the natspec:
// determine the actual message (embellish with real data) and ask user.
// QMessageBox::question();
// otherwise it's a transaction to a contract for which we have the natspec
QMessageBox userInput;
userInput.setText("Pending Transaction");
userInput.setInformativeText(QString::fromStdString(userNotice));
userInput.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel);
userInput.setDefaultButton(QMessageBox::Cancel);
return userInput.exec() == QMessageBox::Ok;
return true;
}

3
libdevcore/CommonData.h

@ -110,6 +110,9 @@ inline std::string toBigEndianString(u160 _val) { std::string ret(20, '\0'); toB
inline bytes toBigEndian(u256 _val) { bytes ret(32); toBigEndian(_val, ret); return ret; }
inline bytes toBigEndian(u160 _val) { bytes ret(20); toBigEndian(_val, ret); return ret; }
/// Convenience function for conversion of a u256 to hex
inline std::string toHex(u256 val) { return toHex(toBigEndian(val)); }
/// Convenience function for toBigEndian.
/// @returns a byte array just big enough to represent @a _val.
template <class _T>

76
libjsqrc/ethereumjs/example/natspec_contract.html

@ -0,0 +1,76 @@
<!doctype>
<html>
<head>
<script type="text/javascript" src="js/es6-promise/promise.min.js"></script>
<script type="text/javascript" src="../dist/ethereum.js"></script>
<script type="text/javascript">
var web3 = require('web3');
web3.setProvider(new web3.providers.AutoProvider());
// solidity source code
var source = "" +
"contract test {\n" +
" /// @notice This is an awesome function for multiplication. \n" +
" function multiply(uint a) returns(uint d) {\n" +
" return a * 7;\n" +
" }\n" +
"}\n";
// contract description, this will be autogenerated somehow
var desc = [{
"name": "multiply",
"inputs": [
{
"name": "a",
"type": "uint256"
}
],
"outputs": [
{
"name": "d",
"type": "uint256"
}
]
}];
var contract;
function createExampleContract() {
// hide create button
document.getElementById('create').style.visibility = 'hidden';
document.getElementById('source').innerText = source;
// create contract
web3.eth.transact({code: web3.eth.solidity(source)}).then(function (address) {
contract = web3.contract(address, desc);
document.getElementById('call').style.visibility = 'visible';
});
}
function callExampleContract() {
// this should be generated by ethereum
var param = parseInt(document.getElementById('value').value);
// call the contract
contract.multiply(param).transact().then(function(res) {
document.getElementById('result').innerText = res[0];
});
}
</script>
</head>
<body>
<h1>contract</h1>
<div id="source"></div>
<div id='create'>
<button type="button" onClick="createExampleContract();">create example contract</button>
</div>
<div id='call' style='visibility: hidden;'>
<input type="number" id="value" onkeyup='callExampleContract()'></input>
</div>
<div id="result"></div>
</body>
</html>

5
libsolidity/Compiler.cpp

@ -50,10 +50,9 @@ void Compiler::compileContract(ContractDefinition const& _contract, vector<Magic
function->accept(*this);
// Swap the runtime context with the creation-time context
CompilerContext runtimeContext;
swap(m_context, runtimeContext);
swap(m_context, m_runtimeContext);
initializeContext(_contract, _magicGlobals, _contracts);
packIntoContractCreator(_contract, runtimeContext);
packIntoContractCreator(_contract, m_runtimeContext);
}
void Compiler::initializeContext(ContractDefinition const& _contract, vector<MagicVariableDeclaration const*> const& _magicGlobals,

2
libsolidity/Compiler.h

@ -35,6 +35,7 @@ public:
void compileContract(ContractDefinition const& _contract, std::vector<MagicVariableDeclaration const*> const& _magicGlobals,
std::map<ContractDefinition const*, bytes const*> const& _contracts);
bytes getAssembledBytecode() { return m_context.getAssembledBytecode(m_optimize); }
bytes getRuntimeBytecode() { return m_runtimeContext.getAssembledBytecode(m_optimize);}
void streamAssembly(std::ostream& _stream) const { m_context.streamAssembly(_stream); }
private:
@ -70,6 +71,7 @@ private:
bool const m_optimize;
CompilerContext m_context;
CompilerContext m_runtimeContext;
std::vector<eth::AssemblyItem> m_breakTags; ///< tag to jump to for a "break" statement
std::vector<eth::AssemblyItem> m_continueTags; ///< tag to jump to for a "continue" statement
eth::AssemblyItem m_returnTag; ///< tag to jump to for a "return" statement

20
libsolidity/CompilerStack.cpp

@ -30,6 +30,8 @@
#include <libsolidity/CompilerStack.h>
#include <libsolidity/InterfaceHandler.h>
#include <libdevcrypto/SHA3.h>
using namespace std;
namespace dev
@ -117,6 +119,7 @@ void CompilerStack::compile(bool _optimize)
contractBytecode);
Contract& compiledContract = m_contracts[contract->getName()];
compiledContract.bytecode = compiler->getAssembledBytecode();
compiledContract.runtimeBytecode = compiler->getRuntimeBytecode();
compiledContract.compiler = move(compiler);
contractBytecode[compiledContract.contract] = &compiledContract.bytecode;
}
@ -134,6 +137,16 @@ bytes const& CompilerStack::getBytecode(string const& _contractName) const
return getContract(_contractName).bytecode;
}
bytes const& CompilerStack::getRuntimeBytecode(std::string const& _contractName) const
{
return getContract(_contractName).runtimeBytecode;
}
dev::h256 CompilerStack::getContractCodeHash(std::string const& _contractName) const
{
return dev::sha3(getRuntimeBytecode(_contractName));
}
void CompilerStack::streamAssembly(ostream& _outStream, string const& _contractName) const
{
getContract(_contractName).compiler->streamAssembly(_outStream);
@ -218,13 +231,6 @@ bytes CompilerStack::staticCompile(std::string const& _sourceCode, bool _optimiz
return stack.compile(_sourceCode, _optimize);
}
dev::h256 CompilerStack::getContractCodeHash(std::string const& _contractName)
{
//LTODO
(void) _contractName;
return dev::h256("");
}
void CompilerStack::reset(bool _keepSources)
{
m_parseSuccessful = false;

10
libsolidity/CompilerStack.h

@ -76,7 +76,13 @@ public:
/// @returns the compiled bytecode
bytes const& compile(std::string const& _sourceCode, bool _optimize = false);
/// Gets the assembled bytecode for a contract
bytes const& getBytecode(std::string const& _contractName = "") const;
/// Get the runtime context's bytecode for a contract
bytes const& getRuntimeBytecode(std::string const& _contractName = "") const;
/// Get the runtime context's code hash for a contract
dev::h256 getContractCodeHash(std::string const& _contractName = "") const;
/// Streams a verbose version of the assembly to @a _outStream.
/// Prerequisite: Successful compilation.
void streamAssembly(std::ostream& _outStream, std::string const& _contractName = "") const;
@ -108,9 +114,6 @@ public:
/// scanning the source code - this is useful for printing exception information.
static bytes staticCompile(std::string const& _sourceCode, bool _optimize = false);
/// Get the runtime context's code hash for a contract. LTODO
dev::h256 getContractCodeHash(std::string const& _contractName);
private:
/**
* Information pertaining to one source unit, filled gradually during parsing and compilation.
@ -128,6 +131,7 @@ private:
ContractDefinition const* contract = nullptr;
std::shared_ptr<Compiler> compiler;
bytes bytecode;
bytes runtimeBytecode;
std::shared_ptr<InterfaceHandler> interfaceHandler;
mutable std::unique_ptr<std::string const> interface;
mutable std::unique_ptr<std::string const> solidityInterface;

Loading…
Cancel
Save