Browse Source

Merge remote-tracking branch 'up/develop' into param_types

Conflicts:
	mix/ClientModel.cpp
	mix/ClientModel.h
	mix/qml/StateList.qml
	mix/qml/TransactionDialog.qml
	mix/res.qrc
cl-refactor
yann300 10 years ago
committed by yann300
parent
commit
591dc3fecc
  1. 10
      alethzero/MainWin.cpp
  2. 3
      libdevcore/FixedHash.h
  3. 12
      libdevcrypto/Common.cpp
  4. 6
      libdevcrypto/Common.h
  5. 3
      libethereum/Client.cpp
  6. 23
      libethereum/Client.h
  7. 2
      libjsqrc/ethereumjs/bower.json
  8. 1127
      libjsqrc/ethereumjs/dist/ethereum.js
  9. 22
      libjsqrc/ethereumjs/dist/ethereum.js.map
  10. 2
      libjsqrc/ethereumjs/dist/ethereum.min.js
  11. 1
      libjsqrc/ethereumjs/example/contract.html
  12. 76
      libjsqrc/ethereumjs/example/contract_with_array.html
  13. 120
      libjsqrc/ethereumjs/example/event.html
  14. 1
      libjsqrc/ethereumjs/example/natspec_contract.html
  15. 298
      libjsqrc/ethereumjs/lib/abi.js
  16. 33
      libjsqrc/ethereumjs/lib/const.js
  17. 199
      libjsqrc/ethereumjs/lib/contract.js
  18. 69
      libjsqrc/ethereumjs/lib/event.js
  19. 24
      libjsqrc/ethereumjs/lib/filter.js
  20. 154
      libjsqrc/ethereumjs/lib/formatters.js
  21. 6
      libjsqrc/ethereumjs/lib/providermanager.js
  22. 79
      libjsqrc/ethereumjs/lib/types.js
  23. 113
      libjsqrc/ethereumjs/lib/utils.js
  24. 60
      libjsqrc/ethereumjs/lib/web3.js
  25. 2
      libjsqrc/ethereumjs/package.json
  26. 37
      libjsqrc/ethereumjs/test/abi.parsers.js
  27. 201
      libjsqrc/ethereumjs/test/eth.contract.js
  28. 1
      libjsqrc/ethereumjs/test/eth.methods.js
  29. 124
      libjsqrc/ethereumjs/test/event.js
  30. 49
      libjsqrc/ethereumjs/test/utils.filters.js
  31. 3
      libjsqrc/ethereumjs/test/web3.methods.js
  32. 165
      libsolidity/AST.cpp
  33. 127
      libsolidity/AST.h
  34. 1
      libsolidity/ASTForward.h
  35. 12
      libsolidity/ASTPrinter.cpp
  36. 2
      libsolidity/ASTPrinter.h
  37. 4
      libsolidity/ASTVisitor.h
  38. 20
      libsolidity/AST_accept.h
  39. 16
      libsolidity/Compiler.cpp
  40. 56
      libsolidity/CompilerStack.cpp
  41. 4
      libsolidity/CompilerStack.h
  42. 81
      libsolidity/ExpressionCompiler.cpp
  43. 7
      libsolidity/ExpressionCompiler.h
  44. 86
      libsolidity/InterfaceHandler.cpp
  45. 13
      libsolidity/NameAndTypeResolver.cpp
  46. 22
      libsolidity/NameAndTypeResolver.h
  47. 88
      libsolidity/Parser.cpp
  48. 16
      libsolidity/Parser.h
  49. 2
      libsolidity/Token.h
  50. 69
      libsolidity/Types.cpp
  51. 28
      libsolidity/Types.h
  52. 24
      libweb3jsonrpc/WebThreeStubServerBase.cpp
  53. 24
      libwhisper/Common.cpp
  54. 20
      libwhisper/Common.h
  55. 3
      libwhisper/Interface.cpp
  56. 20
      libwhisper/Interface.h
  57. 64
      libwhisper/Message.cpp
  58. 28
      libwhisper/Message.h
  59. 11
      libwhisper/WhisperHost.cpp
  60. 8
      libwhisper/WhisperHost.h
  61. 2
      libwhisper/WhisperPeer.cpp
  62. 4
      libwhisper/WhisperPeer.h
  63. 10
      mix/AppContext.cpp
  64. 2
      mix/AppContext.h
  65. 7
      mix/AssemblyDebuggerControl.cpp
  66. 4
      mix/AssemblyDebuggerControl.h
  67. 7
      mix/CMakeLists.txt
  68. 278
      mix/ClientModel.cpp
  69. 90
      mix/ClientModel.h
  70. 1
      mix/CodeEditorExtensionManager.cpp
  71. 27
      mix/CodeModel.cpp
  72. 4
      mix/CodeModel.h
  73. 183
      mix/DebuggingStateWrapper.cpp
  74. 98
      mix/DebuggingStateWrapper.h
  75. 6
      mix/Exceptions.h
  76. 2
      mix/HttpServer.cpp
  77. 2
      mix/MixApplication.cpp
  78. 271
      mix/MixClient.cpp
  79. 50
      mix/MixClient.h
  80. 14
      mix/QContractDefinition.cpp
  81. 2
      mix/QContractDefinition.h
  82. 34
      mix/QFunctionDefinition.cpp
  83. 2
      mix/QFunctionDefinition.h
  84. 2
      mix/QVariableDeclaration.h
  85. 24
      mix/Web3Server.cpp
  86. 13
      mix/Web3Server.h
  87. 31
      mix/qml/CallStack.qml
  88. 27
      mix/qml/ContractLibrary.qml
  89. 68
      mix/qml/Debugger.qml
  90. 31
      mix/qml/Ether.qml
  91. 15
      mix/qml/ItemDelegateDataDump.qml
  92. 33
      mix/qml/MainContent.qml
  93. 6
      mix/qml/ProjectModel.qml
  94. 13
      mix/qml/StateDialog.qml
  95. 36
      mix/qml/StateList.qml
  96. 196
      mix/qml/StateListModel.qml
  97. 16
      mix/qml/StatusPane.qml
  98. 2
      mix/qml/StepActionImage.qml
  99. 9
      mix/qml/TransactionDialog.qml
  100. 84
      mix/qml/TransactionLog.qml

10
alethzero/MainWin.cpp

@ -1597,7 +1597,7 @@ void Main::on_destination_currentTextChanged()
// updateFee();
}
static shh::Topic topicFromText(QString _s)
static shh::FullTopic topicFromText(QString _s)
{
shh::BuildTopic ret;
while (_s.size())
@ -1674,7 +1674,7 @@ string const Main::getFunctionHashes(dev::solidity::CompilerStack const &_compil
{
ret += it.first.abridged();
ret += " :";
ret += it.second.getName() + "\n";
ret += it.second->getDeclaration().getName() + "\n";
}
return ret;
}
@ -2414,10 +2414,10 @@ void Main::refreshWhispers()
shh::Envelope const& e = w.second;
shh::Message m;
for (pair<Public, Secret> const& i: m_server->ids())
if (!!(m = e.open(i.second)))
if (!!(m = e.open(shh::FullTopic(), i.second)))
break;
if (!m)
m = e.open();
m = e.open(shh::FullTopic());
QString msg;
if (m.from())
@ -2430,7 +2430,7 @@ void Main::refreshWhispers()
time_t ex = e.expiry();
QString t(ctime(&ex));
t.chop(1);
QString item = QString("[%1 - %2s] *%3 %5 %4").arg(t).arg(e.ttl()).arg(e.workProved()).arg(toString(e.topics()).c_str()).arg(msg);
QString item = QString("[%1 - %2s] *%3 %5 %4").arg(t).arg(e.ttl()).arg(e.workProved()).arg(toString(e.topic()).c_str()).arg(msg);
ui->whispers->addItem(item);
}
}

3
libdevcore/FixedHash.h

@ -67,6 +67,9 @@ public:
/// Explicitly construct, copying from a byte array.
explicit FixedHash(bytes const& _b) { if (_b.size() == N) memcpy(m_data.data(), _b.data(), std::min<unsigned>(_b.size(), N)); }
/// Explicitly construct, copying from a byte array.
explicit FixedHash(bytesConstRef _b) { if (_b.size() == N) memcpy(m_data.data(), _b.data(), std::min<unsigned>(_b.size(), N)); }
/// Explicitly construct, copying from a bytes in memory with given pointer.
explicit FixedHash(byte const* _bs, ConstructFromPointerType) { memcpy(m_data.data(), _bs, N); }

12
libdevcrypto/Common.cpp

@ -80,6 +80,18 @@ bool dev::decrypt(Secret const& _k, bytesConstRef _cipher, bytes& o_plaintext)
return true;
}
void dev::encryptSym(Secret const& _k, bytesConstRef _plain, bytes& o_cipher)
{
// TOOD: @alex @subtly do this properly.
encrypt(KeyPair(_k).pub(), _plain, o_cipher);
}
bool dev::decryptSym(Secret const& _k, bytesConstRef _cipher, bytes& o_plain)
{
// TODO: @alex @subtly do this properly.
return decrypt(_k, _cipher, o_plain);
}
Public dev::recover(Signature const& _sig, h256 const& _message)
{
return s_secp256k1.recover(_sig, _message.ref());

6
libdevcrypto/Common.h

@ -87,6 +87,12 @@ void encrypt(Public const& _k, bytesConstRef _plain, bytes& o_cipher);
/// Decrypts cipher using Secret key.
bool decrypt(Secret const& _k, bytesConstRef _cipher, bytes& o_plaintext);
/// Symmetric encryption.
void encryptSym(Secret const& _k, bytesConstRef _plain, bytes& o_cipher);
/// Symmetric decryption.
bool decryptSym(Secret const& _k, bytesConstRef _cipher, bytes& o_plaintext);
/// Recovers Public key from signed message hash.
Public recover(Signature const& _sig, h256 const& _hash);

3
libethereum/Client.cpp

@ -226,11 +226,12 @@ void Client::uninstallWatch(unsigned _i)
void Client::noteChanged(h256Set const& _filters)
{
Guard l(m_filterLock);
cnote << "noteChanged(" << _filters << ")";
// accrue all changes left in each filter into the watches.
for (auto& i: m_watches)
if (_filters.count(i.second.id))
{
// cwatch << "!!!" << i.first << i.second.id;
cwatch << "!!!" << i.first << i.second.id;
if (m_filters.count(i.second.id))
i.second.changes += m_filters.at(i.second.id).changes;
else

23
libethereum/Client.h

@ -277,17 +277,7 @@ public:
/// Kills the blockchain. Just for debug use.
void killChain();
private:
/// Do some work. Handles blockchain maintenance and mining.
virtual void doWork();
virtual void doneWorking();
/// Overrides for being a mining host.
virtual void setupState(State& _s);
virtual bool turbo() const { return m_turboMining; }
virtual bool force() const { return m_forceMining; }
protected:
/// Collate the changed filters for the bloom filter of the given pending transaction.
/// Insert any filters that are activated into @a o_changed.
void appendFromNewPending(TransactionReceipt const& _receipt, h256Set& io_changed);
@ -300,6 +290,17 @@ private:
/// This doesn't actually make any callbacks, but incrememnts some counters in m_watches.
void noteChanged(h256Set const& _filters);
private:
/// Do some work. Handles blockchain maintenance and mining.
virtual void doWork();
virtual void doneWorking();
/// Overrides for being a mining host.
virtual void setupState(State& _s);
virtual bool turbo() const { return m_turboMining; }
virtual bool force() const { return m_forceMining; }
/// Return the actual block number of the block with the given int-number (positive is the same, INT_MIN is genesis block, < 0 is negative age, thus -1 is most recently mined, 0 is pending.
unsigned numberOf(int _b) const;

2
libjsqrc/ethereumjs/bower.json

@ -1,7 +1,7 @@
{
"name": "ethereum.js",
"namespace": "ethereum",
"version": "0.0.10",
"version": "0.0.11",
"description": "Ethereum Compatible JavaScript API",
"main": ["./dist/ethereum.js", "./dist/ethereum.min.js"],
"dependencies": {

1127
libjsqrc/ethereumjs/dist/ethereum.js

File diff suppressed because it is too large

22
libjsqrc/ethereumjs/dist/ethereum.js.map

File diff suppressed because one or more lines are too long

2
libjsqrc/ethereumjs/dist/ethereum.min.js

File diff suppressed because one or more lines are too long

1
libjsqrc/ethereumjs/example/contract.html

@ -20,6 +20,7 @@
// contract description, this will be autogenerated somehow
var desc = [{
"name": "multiply(uint256)",
"type": "function",
"inputs": [
{
"name": "a",

76
libjsqrc/ethereumjs/example/contract_with_array.html

@ -0,0 +1,76 @@
<!doctype>
<html>
<head>
<script type="text/javascript" src="js/bignumber.js/bignumber.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.HttpSyncProvider());
// solidity source code
var source = "" +
"contract test {\n" +
" function multiply(uint[] a) returns(uint d) {\n" +
" return a[0] + a[1];\n" +
" }\n" +
"}\n";
// contract description, this will be autogenerated somehow
var desc = [{
"name": "multiply(uint256[])",
"type": "function",
"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
var address = web3.eth.transact({code: web3.eth.solidity(source)});
contract = web3.eth.contract(address, desc);
document.getElementById('call').style.visibility = 'visible';
}
function callExampleContract() {
// this should be generated by ethereum
var param = parseInt(document.getElementById('value').value);
var param2 = parseInt(document.getElementById('value2').value);
// call the contract
var res = contract.call().multiply([param, param2]);
document.getElementById('result').innerText = res.toString(10);
}
</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>
<input type="number" id="value2" onkeyup='callExampleContract()'></input>
</div>
<div id="result"></div>
</body>
</html>

120
libjsqrc/ethereumjs/example/event.html

@ -0,0 +1,120 @@
<!doctype>
<html>
<head>
<script type="text/javascript" src="js/bignumber.js/bignumber.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.HttpSyncProvider('http://localhost:8080'));
var desc = [{
"type":"event",
"inputs": [{"name":"a","type":"uint256","indexed":true},{"name":"b","type":"hash256","indexed":false}],
"name":"Event"
}, {
"type":"event",
"inputs": [{"name":"a","type":"uint256","indexed":true},{"name":"b","type":"hash256","indexed":false}],
"name":"Event2"
}, {
"type":"function",
"inputs": [{"name":"a","type":"uint256"}],
"name":"foo",
"outputs": []
}];
var address = '0x01';
var contract = web3.eth.contract(address, desc);
function test1() {
// "{"topic":["0x83c9849c","0xc4d76332"],"address":"0x01"}"
web3.eth.watch(contract).changed(function (res) {
});
};
function test2() {
// "{"topic":["0x83c9849c"],"address":"0x01"}"
web3.eth.watch(contract.Event).changed(function (res) {
});
};
function test3() {
// "{"topic":["0x83c9849c"],"address":"0x01"}"
contract.Event().changed(function (res) {
});
};
function test4() {
// "{"topic":["0x83c9849c","0000000000000000000000000000000000000000000000000000000000000045"],"address":"0x01"}"
contract.Event({a: 69}).changed(function (res) {
});
};
function test5() {
// "{"topic":["0x83c9849c",["0000000000000000000000000000000000000000000000000000000000000045","000000000000000000000000000000000000000000000000000000000000002a"]],"address":"0x01"}"
contract.Event({a: [69, 42]}).changed(function (res) {
});
};
function test6() {
// "{"topic":["0x83c9849c","000000000000000000000000000000000000000000000000000000000000001e"],"max":100,"address":"0x01"}"
contract.Event({a: 30}, {max: 100}).changed(function (res) {
});
};
function test7() {
// "{"topic":["0x83c9849c","000000000000000000000000000000000000000000000000000000000000001e"],"address":"0x01"}"
web3.eth.watch(contract.Event, {a: 30}).changed(function (res) {
});
};
function test8() {
// "{"topic":["0x83c9849c","000000000000000000000000000000000000000000000000000000000000001e"],"max":100,"address":"0x01"}"
web3.eth.watch(contract.Event, {a: 30}, {max: 100}).changed(function (res) {
});
};
// not valid
// function testX() {
// web3.eth.watch([contract.Event, contract.Event2]).changed(function (res) {
// });
// };
</script>
</head>
<body>
<div>
<button type="button" onClick="test1();">test1</button>
</div>
<div>
<button type="button" onClick="test2();">test2</button>
</div>
<div>
<button type="button" onClick="test3();">test3</button>
</div>
<div>
<button type="button" onClick="test4();">test4</button>
</div>
<div>
<button type="button" onClick="test5();">test5</button>
</div>
<div>
<button type="button" onClick="test6();">test6</button>
</div>
<div>
<button type="button" onClick="test7();">test7</button>
</div>
<div>
<button type="button" onClick="test8();">test8</button>
</div>
</body>
</html>

1
libjsqrc/ethereumjs/example/natspec_contract.html

@ -21,6 +21,7 @@
// contract description, this will be autogenerated somehow
var desc = [{
"name": "multiply(uint256)",
"type": "function",
"inputs": [
{
"name": "a",

298
libjsqrc/ethereumjs/lib/abi.js

@ -21,175 +21,57 @@
* @date 2014
*/
// TODO: is these line is supposed to be here?
if (process.env.NODE_ENV !== 'build') {
var BigNumber = require('bignumber.js'); // jshint ignore:line
}
var web3 = require('./web3'); // jshint ignore:line
BigNumber.config({ ROUNDING_MODE: BigNumber.ROUND_DOWN });
var ETH_PADDING = 32;
/// method signature length in bytes
var ETH_METHOD_SIGNATURE_LENGTH = 4;
/// Finds first index of array element matching pattern
/// @param array
/// @param callback pattern
/// @returns index of element
var findIndex = function (array, callback) {
var end = false;
var i = 0;
for (; i < array.length && !end; i++) {
end = callback(array[i]);
}
return end ? i - 1 : -1;
};
/// @returns a function that is used as a pattern for 'findIndex'
var findMethodIndex = function (json, methodName) {
return findIndex(json, function (method) {
return method.name === methodName;
});
};
/// @returns method with given method name
var getMethodWithName = function (json, methodName) {
var index = findMethodIndex(json, methodName);
if (index === -1) {
console.error('method ' + methodName + ' not found in the abi');
return undefined;
}
return json[index];
};
/// @param string string to be padded
/// @param number of characters that result string should have
/// @param sign, by default 0
/// @returns right aligned string
var padLeft = function (string, chars, sign) {
return new Array(chars - string.length + 1).join(sign ? sign : "0") + string;
};
/// @param expected type prefix (string)
/// @returns function which checks if type has matching prefix. if yes, returns true, otherwise false
var prefixedType = function (prefix) {
return function (type) {
return type.indexOf(prefix) === 0;
};
};
var web3 = require('./web3');
var utils = require('./utils');
var types = require('./types');
var c = require('./const');
var f = require('./formatters');
/// @param expected type name (string)
/// @returns function which checks if type is matching expected one. if yes, returns true, otherwise false
var namedType = function (name) {
return function (type) {
return name === type;
};
var displayTypeError = function (type) {
console.error('parser does not support type: ' + type);
};
/// This method should be called if we want to check if givent type is an array type
/// @returns true if it is, otherwise false
var arrayType = function (type) {
return type.slice(-2) === '[]';
};
/// Formats input value to byte representation of int
/// If value is negative, return it's two's complement
/// If the value is floating point, round it down
/// @returns right-aligned byte representation of int
var formatInputInt = function (value) {
var padding = ETH_PADDING * 2;
if (value instanceof BigNumber || typeof value === 'number') {
if (typeof value === 'number')
value = new BigNumber(value);
value = value.round();
if (value.lessThan(0))
value = new BigNumber("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16).plus(value).plus(1);
value = value.toString(16);
}
else if (value.indexOf('0x') === 0)
value = value.substr(2);
else if (typeof value === 'string')
value = formatInputInt(new BigNumber(value));
else
value = (+value).toString(16);
return padLeft(value, padding);
};
/// Formats input value to byte representation of string
/// @returns left-algined byte representation of string
var formatInputString = function (value) {
return web3.fromAscii(value, ETH_PADDING).substr(2);
};
/// Formats input value to byte representation of bool
/// @returns right-aligned byte representation bool
var formatInputBool = function (value) {
return '000000000000000000000000000000000000000000000000000000000000000' + (value ? '1' : '0');
};
/// Formats input value to byte representation of real
/// Values are multiplied by 2^m and encoded as integers
/// @returns byte representation of real
var formatInputReal = function (value) {
return formatInputInt(new BigNumber(value).times(new BigNumber(2).pow(128)));
};
var dynamicTypeBytes = function (type, value) {
// TODO: decide what to do with array of strings
if (arrayType(type) || type === 'string') // only string itself that is dynamic; stringX is static length.
return formatInputInt(value.length);
return f.formatInputInt(value.length);
return "";
};
/// Setups input formatters for solidity types
/// @returns an array of input formatters
var setupInputTypes = function () {
return [
{ type: prefixedType('uint'), format: formatInputInt },
{ type: prefixedType('int'), format: formatInputInt },
{ type: prefixedType('hash'), format: formatInputInt },
{ type: prefixedType('string'), format: formatInputString },
{ type: prefixedType('real'), format: formatInputReal },
{ type: prefixedType('ureal'), format: formatInputReal },
{ type: namedType('address'), format: formatInputInt },
{ type: namedType('bool'), format: formatInputBool }
];
};
var inputTypes = setupInputTypes();
var inputTypes = types.inputTypes();
/// Formats input params to bytes
/// @param contract json abi
/// @param name of the method that we want to use
/// @param abi contract method inputs
/// @param array of params that will be formatted to bytes
/// @returns bytes representation of input params
var toAbiInput = function (json, methodName, params) {
var formatInput = function (inputs, params) {
var bytes = "";
var method = getMethodWithName(json, methodName);
var padding = ETH_PADDING * 2;
var padding = c.ETH_PADDING * 2;
/// first we iterate in search for dynamic
method.inputs.forEach(function (input, index) {
inputs.forEach(function (input, index) {
bytes += dynamicTypeBytes(input.type, params[index]);
});
method.inputs.forEach(function (input, i) {
inputs.forEach(function (input, i) {
var typeMatch = false;
for (var j = 0; j < inputTypes.length && !typeMatch; j++) {
typeMatch = inputTypes[j].type(method.inputs[i].type, params[i]);
typeMatch = inputTypes[j].type(inputs[i].type, params[i]);
}
if (!typeMatch) {
console.error('input parser does not support type: ' + method.inputs[i].type);
displayTypeError(inputs[i].type);
}
var formatter = inputTypes[j - 1].format;
var toAppend = "";
if (arrayType(method.inputs[i].type))
if (arrayType(inputs[i].type))
toAppend = params[i].reduce(function (acc, curr) {
return acc + formatter(curr);
}, "");
@ -201,116 +83,44 @@ var toAbiInput = function (json, methodName, params) {
return bytes;
};
/// Check if input value is negative
/// @param value is hex format
/// @returns true if it is negative, otherwise false
var signedIsNegative = function (value) {
return (new BigNumber(value.substr(0, 1), 16).toString(2).substr(0, 1)) === '1';
};
/// Formats input right-aligned input bytes to int
/// @returns right-aligned input bytes formatted to int
var formatOutputInt = function (value) {
// check if it's negative number
// it it is, return two's complement
if (signedIsNegative(value)) {
return new BigNumber(value, 16).minus(new BigNumber('ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', 16)).minus(1);
}
return new BigNumber(value, 16);
};
/// Formats big right-aligned input bytes to uint
/// @returns right-aligned input bytes formatted to uint
var formatOutputUInt = function (value) {
return new BigNumber(value, 16);
};
/// @returns input bytes formatted to real
var formatOutputReal = function (value) {
return formatOutputInt(value).dividedBy(new BigNumber(2).pow(128));
};
/// @returns input bytes formatted to ureal
var formatOutputUReal = function (value) {
return formatOutputUInt(value).dividedBy(new BigNumber(2).pow(128));
};
/// @returns right-aligned input bytes formatted to hex
var formatOutputHash = function (value) {
return "0x" + value;
};
/// @returns right-aligned input bytes formatted to bool
var formatOutputBool = function (value) {
return value === '0000000000000000000000000000000000000000000000000000000000000001' ? true : false;
};
/// @returns left-aligned input bytes formatted to ascii string
var formatOutputString = function (value) {
return web3.toAscii(value);
};
/// @returns right-aligned input bytes formatted to address
var formatOutputAddress = function (value) {
return "0x" + value.slice(value.length - 40, value.length);
};
var dynamicBytesLength = function (type) {
if (arrayType(type) || type === 'string') // only string itself that is dynamic; stringX is static length.
return ETH_PADDING * 2;
return c.ETH_PADDING * 2;
return 0;
};
/// Setups output formaters for solidity types
/// @returns an array of output formatters
var setupOutputTypes = function () {
return [
{ type: prefixedType('uint'), format: formatOutputUInt },
{ type: prefixedType('int'), format: formatOutputInt },
{ type: prefixedType('hash'), format: formatOutputHash },
{ type: prefixedType('string'), format: formatOutputString },
{ type: prefixedType('real'), format: formatOutputReal },
{ type: prefixedType('ureal'), format: formatOutputUReal },
{ type: namedType('address'), format: formatOutputAddress },
{ type: namedType('bool'), format: formatOutputBool }
];
};
var outputTypes = setupOutputTypes();
var outputTypes = types.outputTypes();
/// Formats output bytes back to param list
/// @param contract json abi
/// @param name of the method that we want to use
/// @param contract abi method outputs
/// @param bytes representtion of output
/// @returns array of output params
var fromAbiOutput = function (json, methodName, output) {
var formatOutput = function (outs, output) {
output = output.slice(2);
var result = [];
var method = getMethodWithName(json, methodName);
var padding = ETH_PADDING * 2;
var padding = c.ETH_PADDING * 2;
var dynamicPartLength = method.outputs.reduce(function (acc, curr) {
var dynamicPartLength = outs.reduce(function (acc, curr) {
return acc + dynamicBytesLength(curr.type);
}, 0);
var dynamicPart = output.slice(0, dynamicPartLength);
output = output.slice(dynamicPartLength);
method.outputs.forEach(function (out, i) {
outs.forEach(function (out, i) {
var typeMatch = false;
for (var j = 0; j < outputTypes.length && !typeMatch; j++) {
typeMatch = outputTypes[j].type(method.outputs[i].type);
typeMatch = outputTypes[j].type(outs[i].type);
}
if (!typeMatch) {
console.error('output parser does not support type: ' + method.outputs[i].type);
displayTypeError(outs[i].type);
}
var formatter = outputTypes[j - 1].format;
if (arrayType(method.outputs[i].type)) {
var size = formatOutputUInt(dynamicPart.slice(0, padding));
if (arrayType(outs[i].type)) {
var size = f.formatOutputUInt(dynamicPart.slice(0, padding));
dynamicPart = dynamicPart.slice(padding);
var array = [];
for (var k = 0; k < size; k++) {
@ -319,7 +129,7 @@ var fromAbiOutput = function (json, methodName, output) {
}
result.push(array);
}
else if (prefixedType('string')(method.outputs[i].type)) {
else if (types.prefixedType('string')(outs[i].type)) {
dynamicPart = dynamicPart.slice(padding);
result.push(formatter(output.slice(0, padding)));
output = output.slice(padding);
@ -332,30 +142,18 @@ var fromAbiOutput = function (json, methodName, output) {
return result;
};
/// @returns display name for method eg. multiply(uint256) -> multiply
var methodDisplayName = function (method) {
var length = method.indexOf('(');
return length !== -1 ? method.substr(0, length) : method;
};
/// @returns overloaded part of method's name
var methodTypeName = function (method) {
/// TODO: make it not vulnerable
var length = method.indexOf('(');
return length !== -1 ? method.substr(length + 1, method.length - 1 - (length + 1)) : "";
};
/// @param json abi for contract
/// @returns input parser object for given json abi
/// TODO: refactor creating the parser, do not double logic from contract
var inputParser = function (json) {
var parser = {};
json.forEach(function (method) {
var displayName = methodDisplayName(method.name);
var typeName = methodTypeName(method.name);
var displayName = utils.extractDisplayName(method.name);
var typeName = utils.extractTypeName(method.name);
var impl = function () {
var params = Array.prototype.slice.call(arguments);
return toAbiInput(json, method.name, params);
return formatInput(method.inputs, params);
};
if (parser[displayName] === undefined) {
@ -374,11 +172,11 @@ var outputParser = function (json) {
var parser = {};
json.forEach(function (method) {
var displayName = methodDisplayName(method.name);
var typeName = methodTypeName(method.name);
var displayName = utils.extractDisplayName(method.name);
var typeName = utils.extractTypeName(method.name);
var impl = function (output) {
return fromAbiOutput(json, method.name, output);
return formatOutput(method.outputs, output);
};
if (parser[displayName] === undefined) {
@ -391,18 +189,22 @@ var outputParser = function (json) {
return parser;
};
/// @param method name for which we want to get method signature
/// @returns (promise) contract method signature for method with given name
var methodSignature = function (name) {
return web3.sha3(web3.fromAscii(name)).slice(0, 2 + ETH_METHOD_SIGNATURE_LENGTH * 2);
/// @param function/event name for which we want to get signature
/// @returns signature of function/event with given name
var signatureFromAscii = function (name) {
return web3.sha3(web3.fromAscii(name)).slice(0, 2 + c.ETH_SIGNATURE_LENGTH * 2);
};
var eventSignatureFromAscii = function (name) {
return web3.sha3(web3.fromAscii(name));
};
module.exports = {
inputParser: inputParser,
outputParser: outputParser,
methodSignature: methodSignature,
methodDisplayName: methodDisplayName,
methodTypeName: methodTypeName,
getMethodWithName: getMethodWithName
formatInput: formatInput,
formatOutput: formatOutput,
signatureFromAscii: signatureFromAscii,
eventSignatureFromAscii: eventSignatureFromAscii
};

33
libjsqrc/ethereumjs/lib/const.js

@ -0,0 +1,33 @@
/*
This file is part of ethereum.js.
ethereum.js is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
ethereum.js is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with ethereum.js. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file const.js
* @authors:
* Marek Kotewicz <marek@ethdev.com>
* @date 2015
*/
/// required to define ETH_BIGNUMBER_ROUNDING_MODE
if (process.env.NODE_ENV !== 'build') {
var BigNumber = require('bignumber.js'); // jshint ignore:line
}
module.exports = {
ETH_PADDING: 32,
ETH_SIGNATURE_LENGTH: 4,
ETH_BIGNUMBER_ROUNDING_MODE: { ROUNDING_MODE: BigNumber.ROUND_DOWN }
};

199
libjsqrc/ethereumjs/lib/contract.js

@ -20,98 +20,78 @@
* @date 2014
*/
var web3 = require('./web3'); // jshint ignore:line
var web3 = require('./web3');
var abi = require('./abi');
var utils = require('./utils');
var eventImpl = require('./event');
/**
* This method should be called when we want to call / transact some solidity method from javascript
* it returns an object which has same methods available as solidity contract description
* usage example:
*
* var abi = [{
* name: 'myMethod',
* inputs: [{ name: 'a', type: 'string' }],
* outputs: [{name: 'd', type: 'string' }]
* }]; // contract abi
*
* var myContract = web3.eth.contract('0x0123123121', abi); // creation of contract object
*
* myContract.myMethod('this is test string param for call'); // myMethod call (implicit, default)
* myContract.call().myMethod('this is test string param for call'); // myMethod call (explicit)
* myContract.transact().myMethod('this is test string param for transact'); // myMethod transact
*
* @param address - address of the contract, which should be called
* @param desc - abi json description of the contract, which is being created
* @returns contract object
*/
var contract = function (address, desc) {
desc.forEach(function (method) {
// workaround for invalid assumption that method.name is the full anonymous prototype of the method.
// it's not. it's just the name. the rest of the code assumes it's actually the anonymous
// prototype, so we make it so as a workaround.
if (method.name.indexOf('(') === -1) {
var displayName = method.name;
var typeName = method.inputs.map(function(i){return i.type; }).join();
method.name = displayName + '(' + typeName + ')';
}
});
var inputParser = abi.inputParser(desc);
var outputParser = abi.outputParser(desc);
var exportNatspecGlobals = function (vars) {
// it's used byt natspec.js
// TODO: figure out better way to solve this
web3._currentContractAbi = vars.abi;
web3._currentContractAddress = vars.address;
web3._currentContractMethodName = vars.method;
web3._currentContractMethodParams = vars.params;
};
var result = {};
var addFunctionRelatedPropertiesToContract = function (contract) {
result.call = function (options) {
result._isTransact = false;
result._options = options;
return result;
contract.call = function (options) {
contract._isTransact = false;
contract._options = options;
return contract;
};
result.transact = function (options) {
result._isTransact = true;
result._options = options;
return result;
contract.transact = function (options) {
contract._isTransact = true;
contract._options = options;
return contract;
};
result._options = {};
contract._options = {};
['gas', 'gasPrice', 'value', 'from'].forEach(function(p) {
result[p] = function (v) {
result._options[p] = v;
return result;
contract[p] = function (v) {
contract._options[p] = v;
return contract;
};
});
};
desc.forEach(function (method) {
var addFunctionsToContract = function (contract, desc, address) {
var inputParser = abi.inputParser(desc);
var outputParser = abi.outputParser(desc);
// create contract functions
utils.filterFunctions(desc).forEach(function (method) {
var displayName = abi.methodDisplayName(method.name);
var typeName = abi.methodTypeName(method.name);
var displayName = utils.extractDisplayName(method.name);
var typeName = utils.extractTypeName(method.name);
var impl = function () {
var params = Array.prototype.slice.call(arguments);
var signature = abi.methodSignature(method.name);
var signature = abi.signatureFromAscii(method.name);
var parsed = inputParser[displayName][typeName].apply(null, params);
var options = result._options || {};
var options = contract._options || {};
options.to = address;
options.data = signature + parsed;
var isTransact = result._isTransact === true || (result._isTransact !== false && !method.constant);
var isTransact = contract._isTransact === true || (contract._isTransact !== false && !method.constant);
var collapse = options.collapse !== false;
// reset
result._options = {};
result._isTransact = null;
contract._options = {};
contract._isTransact = null;
if (isTransact) {
// it's used byt natspec.js
// TODO: figure out better way to solve this
web3._currentContractAbi = desc;
web3._currentContractAddress = address;
web3._currentContractMethodName = method.name;
web3._currentContractMethodParams = params;
exportNatspecGlobals({
abi: desc,
address: address,
method: method.name,
params: params
});
// transactions do not have any output, cause we do not know, when they will be processed
web3.eth.transact(options);
@ -130,13 +110,96 @@ var contract = function (address, desc) {
return ret;
};
if (result[displayName] === undefined) {
result[displayName] = impl;
if (contract[displayName] === undefined) {
contract[displayName] = impl;
}
result[displayName][typeName] = impl;
contract[displayName][typeName] = impl;
});
};
var addEventRelatedPropertiesToContract = function (contract, desc, address) {
contract.address = address;
Object.defineProperty(contract, 'topic', {
get: function() {
return utils.filterEvents(desc).map(function (e) {
return abi.eventSignatureFromAscii(e.name);
});
}
});
};
var addEventsToContract = function (contract, desc, address) {
// create contract events
utils.filterEvents(desc).forEach(function (e) {
var impl = function () {
var params = Array.prototype.slice.call(arguments);
var signature = abi.eventSignatureFromAscii(e.name);
var event = eventImpl(address, signature, e);
var o = event.apply(null, params);
return web3.eth.watch(o);
};
// this property should be used by eth.filter to check if object is an event
impl._isEvent = true;
var displayName = utils.extractDisplayName(e.name);
var typeName = utils.extractTypeName(e.name);
if (contract[displayName] === undefined) {
contract[displayName] = impl;
}
contract[displayName][typeName] = impl;
});
};
/**
* This method should be called when we want to call / transact some solidity method from javascript
* it returns an object which has same methods available as solidity contract description
* usage example:
*
* var abi = [{
* name: 'myMethod',
* inputs: [{ name: 'a', type: 'string' }],
* outputs: [{name: 'd', type: 'string' }]
* }]; // contract abi
*
* var myContract = web3.eth.contract('0x0123123121', abi); // creation of contract object
*
* myContract.myMethod('this is test string param for call'); // myMethod call (implicit, default)
* myContract.call().myMethod('this is test string param for call'); // myMethod call (explicit)
* myContract.transact().myMethod('this is test string param for transact'); // myMethod transact
*
* @param address - address of the contract, which should be called
* @param desc - abi json description of the contract, which is being created
* @returns contract object
*/
var contract = function (address, desc) {
// workaround for invalid assumption that method.name is the full anonymous prototype of the method.
// it's not. it's just the name. the rest of the code assumes it's actually the anonymous
// prototype, so we make it so as a workaround.
// TODO: we may not want to modify input params, maybe use copy instead?
desc.forEach(function (method) {
if (method.name.indexOf('(') === -1) {
var displayName = method.name;
var typeName = method.inputs.map(function(i){return i.type; }).join();
method.name = displayName + '(' + typeName + ')';
}
});
var result = {};
addFunctionRelatedPropertiesToContract(result);
addFunctionsToContract(result, desc, address);
addEventRelatedPropertiesToContract(result, desc, address);
addEventsToContract(result, desc, address);
return result;
};

69
libjsqrc/ethereumjs/lib/event.js

@ -0,0 +1,69 @@
/*
This file is part of ethereum.js.
ethereum.js is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
ethereum.js is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with ethereum.js. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file event.js
* @authors:
* Marek Kotewicz <marek@ethdev.com>
* @date 2014
*/
var abi = require('./abi');
var utils = require('./utils');
var inputWithName = function (inputs, name) {
var index = utils.findIndex(inputs, function (input) {
return input.name === name;
});
if (index === -1) {
console.error('indexed param with name ' + name + ' not found');
return undefined;
}
return inputs[index];
};
var indexedParamsToTopics = function (event, indexed) {
// sort keys?
return Object.keys(indexed).map(function (key) {
var inputs = [inputWithName(event.inputs, key)];
var value = indexed[key];
if (value instanceof Array) {
return value.map(function (v) {
return abi.formatInput(inputs, [v]);
});
}
return abi.formatInput(inputs, [value]);
});
};
var implementationOfEvent = function (address, signature, event) {
// valid options are 'earliest', 'latest', 'offset' and 'max', as defined for 'eth.watch'
return function (indexed, options) {
var o = options || {};
o.address = address;
o.topic = [];
o.topic.push(signature);
if (indexed) {
o.topic = o.topic.concat(indexedParamsToTopics(event, indexed));
}
return o;
};
};
module.exports = implementationOfEvent;

24
libjsqrc/ethereumjs/lib/filter.js

@ -27,7 +27,29 @@ var web3 = require('./web3'); // jshint ignore:line
/// should be used when we want to watch something
/// it's using inner polling mechanism and is notified about changes
/// TODO: change 'options' name cause it may be not the best matching one, since we have events
var Filter = function(options, impl) {
if (typeof options !== "string") {
// topics property is deprecated, warn about it!
if (options.topics) {
console.warn('"topics" is deprecated, use "topic" instead');
}
// evaluate lazy properties
options = {
to: options.to,
topic: options.topic,
earliest: options.earliest,
latest: options.latest,
max: options.max,
skip: options.skip,
address: options.address
};
}
this.impl = impl;
this.callbacks = [];
@ -48,7 +70,7 @@ Filter.prototype.changed = function(callback) {
/// trigger calling new message from people
Filter.prototype.trigger = function(messages) {
for (var i = 0; i < this.callbacks.length; i++) {
for (var j = 0; j < messages; j++) {
for (var j = 0; j < messages.length; j++) {
this.callbacks[i].call(this, messages[j]);
}
}

154
libjsqrc/ethereumjs/lib/formatters.js

@ -0,0 +1,154 @@
/*
This file is part of ethereum.js.
ethereum.js is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
ethereum.js is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with ethereum.js. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file formatters.js
* @authors:
* Marek Kotewicz <marek@ethdev.com>
* @date 2015
*/
if (process.env.NODE_ENV !== 'build') {
var BigNumber = require('bignumber.js'); // jshint ignore:line
}
var utils = require('./utils');
var c = require('./const');
/// @param string string to be padded
/// @param number of characters that result string should have
/// @param sign, by default 0
/// @returns right aligned string
var padLeft = function (string, chars, sign) {
return new Array(chars - string.length + 1).join(sign ? sign : "0") + string;
};
/// Formats input value to byte representation of int
/// If value is negative, return it's two's complement
/// If the value is floating point, round it down
/// @returns right-aligned byte representation of int
var formatInputInt = function (value) {
var padding = c.ETH_PADDING * 2;
if (value instanceof BigNumber || typeof value === 'number') {
if (typeof value === 'number')
value = new BigNumber(value);
BigNumber.config(c.ETH_BIGNUMBER_ROUNDING_MODE);
value = value.round();
if (value.lessThan(0))
value = new BigNumber("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16).plus(value).plus(1);
value = value.toString(16);
}
else if (value.indexOf('0x') === 0)
value = value.substr(2);
else if (typeof value === 'string')
value = formatInputInt(new BigNumber(value));
else
value = (+value).toString(16);
return padLeft(value, padding);
};
/// Formats input value to byte representation of string
/// @returns left-algined byte representation of string
var formatInputString = function (value) {
return utils.fromAscii(value, c.ETH_PADDING).substr(2);
};
/// Formats input value to byte representation of bool
/// @returns right-aligned byte representation bool
var formatInputBool = function (value) {
return '000000000000000000000000000000000000000000000000000000000000000' + (value ? '1' : '0');
};
/// Formats input value to byte representation of real
/// Values are multiplied by 2^m and encoded as integers
/// @returns byte representation of real
var formatInputReal = function (value) {
return formatInputInt(new BigNumber(value).times(new BigNumber(2).pow(128)));
};
/// Check if input value is negative
/// @param value is hex format
/// @returns true if it is negative, otherwise false
var signedIsNegative = function (value) {
return (new BigNumber(value.substr(0, 1), 16).toString(2).substr(0, 1)) === '1';
};
/// Formats input right-aligned input bytes to int
/// @returns right-aligned input bytes formatted to int
var formatOutputInt = function (value) {
value = value || "0";
// check if it's negative number
// it it is, return two's complement
if (signedIsNegative(value)) {
return new BigNumber(value, 16).minus(new BigNumber('ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', 16)).minus(1);
}
return new BigNumber(value, 16);
};
/// Formats big right-aligned input bytes to uint
/// @returns right-aligned input bytes formatted to uint
var formatOutputUInt = function (value) {
value = value || "0";
return new BigNumber(value, 16);
};
/// @returns input bytes formatted to real
var formatOutputReal = function (value) {
return formatOutputInt(value).dividedBy(new BigNumber(2).pow(128));
};
/// @returns input bytes formatted to ureal
var formatOutputUReal = function (value) {
return formatOutputUInt(value).dividedBy(new BigNumber(2).pow(128));
};
/// @returns right-aligned input bytes formatted to hex
var formatOutputHash = function (value) {
return "0x" + value;
};
/// @returns right-aligned input bytes formatted to bool
var formatOutputBool = function (value) {
return value === '0000000000000000000000000000000000000000000000000000000000000001' ? true : false;
};
/// @returns left-aligned input bytes formatted to ascii string
var formatOutputString = function (value) {
return utils.toAscii(value);
};
/// @returns right-aligned input bytes formatted to address
var formatOutputAddress = function (value) {
return "0x" + value.slice(value.length - 40, value.length);
};
module.exports = {
formatInputInt: formatInputInt,
formatInputString: formatInputString,
formatInputBool: formatInputBool,
formatInputReal: formatInputReal,
formatOutputInt: formatOutputInt,
formatOutputUInt: formatOutputUInt,
formatOutputReal: formatOutputReal,
formatOutputUReal: formatOutputUReal,
formatOutputHash: formatOutputHash,
formatOutputBool: formatOutputBool,
formatOutputString: formatOutputString,
formatOutputAddress: formatOutputAddress
};

6
libjsqrc/ethereumjs/lib/providermanager.js

@ -76,6 +76,12 @@ ProviderManager.prototype.send = function(data) {
//TODO: handle error here?
var result = this.provider.send(data);
result = JSON.parse(result);
if (result.error) {
console.log(result.error);
return null;
}
return result.result;
};

79
libjsqrc/ethereumjs/lib/types.js

@ -0,0 +1,79 @@
/*
This file is part of ethereum.js.
ethereum.js is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
ethereum.js is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with ethereum.js. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file types.js
* @authors:
* Marek Kotewicz <marek@ethdev.com>
* @date 2015
*/
var f = require('./formatters');
/// @param expected type prefix (string)
/// @returns function which checks if type has matching prefix. if yes, returns true, otherwise false
var prefixedType = function (prefix) {
return function (type) {
return type.indexOf(prefix) === 0;
};
};
/// @param expected type name (string)
/// @returns function which checks if type is matching expected one. if yes, returns true, otherwise false
var namedType = function (name) {
return function (type) {
return name === type;
};
};
/// Setups input formatters for solidity types
/// @returns an array of input formatters
var inputTypes = function () {
return [
{ type: prefixedType('uint'), format: f.formatInputInt },
{ type: prefixedType('int'), format: f.formatInputInt },
{ type: prefixedType('hash'), format: f.formatInputInt },
{ type: prefixedType('string'), format: f.formatInputString },
{ type: prefixedType('real'), format: f.formatInputReal },
{ type: prefixedType('ureal'), format: f.formatInputReal },
{ type: namedType('address'), format: f.formatInputInt },
{ type: namedType('bool'), format: f.formatInputBool }
];
};
/// Setups output formaters for solidity types
/// @returns an array of output formatters
var outputTypes = function () {
return [
{ type: prefixedType('uint'), format: f.formatOutputUInt },
{ type: prefixedType('int'), format: f.formatOutputInt },
{ type: prefixedType('hash'), format: f.formatOutputHash },
{ type: prefixedType('string'), format: f.formatOutputString },
{ type: prefixedType('real'), format: f.formatOutputReal },
{ type: prefixedType('ureal'), format: f.formatOutputUReal },
{ type: namedType('address'), format: f.formatOutputAddress },
{ type: namedType('bool'), format: f.formatOutputBool }
];
};
module.exports = {
prefixedType: prefixedType,
namedType: namedType,
inputTypes: inputTypes,
outputTypes: outputTypes
};

113
libjsqrc/ethereumjs/lib/utils.js

@ -0,0 +1,113 @@
/*
This file is part of ethereum.js.
ethereum.js is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
ethereum.js is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with ethereum.js. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file utils.js
* @authors:
* Marek Kotewicz <marek@ethdev.com>
* @date 2015
*/
/// Finds first index of array element matching pattern
/// @param array
/// @param callback pattern
/// @returns index of element
var findIndex = function (array, callback) {
var end = false;
var i = 0;
for (; i < array.length && !end; i++) {
end = callback(array[i]);
}
return end ? i - 1 : -1;
};
/// @returns ascii string representation of hex value prefixed with 0x
var toAscii = function(hex) {
// Find termination
var str = "";
var i = 0, l = hex.length;
if (hex.substring(0, 2) === '0x') {
i = 2;
}
for (; i < l; i+=2) {
var code = parseInt(hex.substr(i, 2), 16);
if (code === 0) {
break;
}
str += String.fromCharCode(code);
}
return str;
};
var toHex = function(str) {
var hex = "";
for(var i = 0; i < str.length; i++) {
var n = str.charCodeAt(i).toString(16);
hex += n.length < 2 ? '0' + n : n;
}
return hex;
};
/// @returns hex representation (prefixed by 0x) of ascii string
var fromAscii = function(str, pad) {
pad = pad === undefined ? 0 : pad;
var hex = toHex(str);
while (hex.length < pad*2)
hex += "00";
return "0x" + hex;
};
/// @returns display name for function/event eg. multiply(uint256) -> multiply
var extractDisplayName = function (name) {
var length = name.indexOf('(');
return length !== -1 ? name.substr(0, length) : name;
};
/// @returns overloaded part of function/event name
var extractTypeName = function (name) {
/// TODO: make it invulnerable
var length = name.indexOf('(');
return length !== -1 ? name.substr(length + 1, name.length - 1 - (length + 1)) : "";
};
/// Filters all function from input abi
/// @returns abi array with filtered objects of type 'function'
var filterFunctions = function (json) {
return json.filter(function (current) {
return current.type === 'function';
});
};
/// Filters all events form input abi
/// @returns abi array with filtered objects of type 'event'
var filterEvents = function (json) {
return json.filter(function (current) {
return current.type === 'event';
});
};
module.exports = {
findIndex: findIndex,
toAscii: toAscii,
fromAscii: fromAscii,
extractDisplayName: extractDisplayName,
extractTypeName: extractTypeName,
filterFunctions: filterFunctions,
filterEvents: filterEvents
};

60
libjsqrc/ethereumjs/lib/web3.js

@ -27,6 +27,8 @@ if (process.env.NODE_ENV !== 'build') {
var BigNumber = require('bignumber.js');
}
var utils = require('./utils');
var ETH_UNITS = [
'wei',
'Kwei',
@ -98,7 +100,6 @@ var ethProperties = function () {
{ name: 'listening', getter: 'eth_listening', setter: 'eth_setListening' },
{ name: 'mining', getter: 'eth_mining', setter: 'eth_setMining' },
{ name: 'gasPrice', getter: 'eth_gasPrice' },
{ name: 'account', getter: 'eth_account' },
{ name: 'accounts', getter: 'eth_accounts' },
{ name: 'peerCount', getter: 'eth_peerCount' },
{ name: 'defaultBlock', getter: 'eth_defaultBlock', setter: 'eth_setDefaultBlock' },
@ -193,47 +194,17 @@ var web3 = {
_events: {},
providers: {},
toHex: function(str) {
var hex = "";
for(var i = 0; i < str.length; i++) {
var n = str.charCodeAt(i).toString(16);
hex += n.length < 2 ? '0' + n : n;
}
return hex;
},
/// @returns ascii string representation of hex value prefixed with 0x
toAscii: function(hex) {
// Find termination
var str = "";
var i = 0, l = hex.length;
if (hex.substring(0, 2) === '0x')
i = 2;
for(; i < l; i+=2) {
var code = parseInt(hex.substr(i, 2), 16);
if(code === 0) {
break;
}
str += String.fromCharCode(code);
}
return str;
},
toAscii: utils.toAscii,
/// @returns hex representation (prefixed by 0x) of ascii string
fromAscii: function(str, pad) {
pad = pad === undefined ? 0 : pad;
var hex = this.toHex(str);
while(hex.length < pad*2)
hex += "00";
return "0x" + hex;
},
fromAscii: utils.fromAscii,
/// @returns decimal representaton of hex value prefixed by 0x
toDecimal: function (val) {
return (new BigNumber(val.substring(2), 16).toString(10));
// remove 0x and place 0, if it's required
val = val.length > 2 ? val.substring(2) : "0";
return (new BigNumber(val, 16).toString(10));
},
/// @returns hex representation (prefixed by 0x) of decimal value
@ -277,8 +248,15 @@ var web3 = {
return ret;
};
},
watch: function (params) {
return new web3.filter(params, ethWatch);
/// @param filter may be a string, object or event
/// @param indexed is optional, this is an object with optional event indexed params
/// @param options is optional, this is an object with optional event options ('max'...)
watch: function (filter, indexed, options) {
if (filter._isEvent) {
return filter(indexed, options);
}
return new web3.filter(filter, ethWatch);
}
},
@ -287,8 +265,10 @@ var web3 = {
/// shh object prototype
shh: {
watch: function (params) {
return new web3.filter(params, shhWatch);
/// @param filter may be a string, object or event
watch: function (filter, indexed) {
return new web3.filter(filter, shhWatch);
}
},

2
libjsqrc/ethereumjs/package.json

@ -1,7 +1,7 @@
{
"name": "ethereum.js",
"namespace": "ethereum",
"version": "0.0.10",
"version": "0.0.11",
"description": "Ethereum Compatible JavaScript API",
"main": "./index.js",
"directories": {

37
libjsqrc/ethereumjs/test/abi.parsers.js

@ -5,6 +5,7 @@ var clone = function (object) { return JSON.parse(JSON.stringify(object)); };
var description = [{
"name": "test",
"type": "function",
"inputs": [{
"name": "a",
"type": "uint256"
@ -339,10 +340,12 @@ describe('abi', function() {
// given
var d = [{
name: "test",
type: "function",
inputs: [{ type: "int" }],
outputs: [{ type: "int" }]
},{
name: "test2",
type: "function",
inputs: [{ type: "string" }],
outputs: [{ type: "string" }]
}];
@ -775,10 +778,12 @@ describe('abi', function() {
// given
var d = [{
name: "test",
type: "function",
inputs: [{ type: "int" }],
outputs: [{ type: "int" }]
},{
name: "test2",
type: "function",
inputs: [{ type: "string" }],
outputs: [{ type: "string" }]
}];
@ -823,6 +828,38 @@ describe('abi', function() {
});
it('should parse 0x value', function () {
// given
var d = clone(description);
d[0].outputs = [
{ type: 'int' }
];
// when
var parser = abi.outputParser(d);
// then
assert.equal(parser.test("0x")[0], 0);
});
it('should parse 0x value', function () {
// given
var d = clone(description);
d[0].outputs = [
{ type: 'uint' }
];
// when
var parser = abi.outputParser(d);
// then
assert.equal(parser.test("0x")[0], 0);
});
});
});

201
libjsqrc/ethereumjs/test/eth.contract.js

@ -0,0 +1,201 @@
var assert = require('assert');
var contract = require('../lib/contract.js');
describe('contract', function() {
it('should create simple contract with one method from abi with explicit type name', function () {
// given
var description = [{
"name": "test(uint256)",
"type": "function",
"inputs": [{
"name": "a",
"type": "uint256"
}
],
"outputs": [
{
"name": "d",
"type": "uint256"
}
]
}];
// when
var con = contract(null, description);
// then
assert.equal('function', typeof con.test);
assert.equal('function', typeof con.test['uint256']);
});
it('should create simple contract with one method from abi with implicit type name', function () {
// given
var description = [{
"name": "test",
"type": "function",
"inputs": [{
"name": "a",
"type": "uint256"
}
],
"outputs": [
{
"name": "d",
"type": "uint256"
}
]
}];
// when
var con = contract(null, description);
// then
assert.equal('function', typeof con.test);
assert.equal('function', typeof con.test['uint256']);
});
it('should create contract with multiple methods', function () {
// given
var description = [{
"name": "test",
"type": "function",
"inputs": [{
"name": "a",
"type": "uint256"
}
],
"outputs": [
{
"name": "d",
"type": "uint256"
}
],
}, {
"name": "test2",
"type": "function",
"inputs": [{
"name": "a",
"type": "uint256"
}
],
"outputs": [
{
"name": "d",
"type": "uint256"
}
]
}];
// when
var con = contract(null, description);
// then
assert.equal('function', typeof con.test);
assert.equal('function', typeof con.test['uint256']);
assert.equal('function', typeof con.test2);
assert.equal('function', typeof con.test2['uint256']);
});
it('should create contract with overloaded methods', function () {
// given
var description = [{
"name": "test",
"type": "function",
"inputs": [{
"name": "a",
"type": "uint256"
}
],
"outputs": [
{
"name": "d",
"type": "uint256"
}
],
}, {
"name": "test",
"type": "function",
"inputs": [{
"name": "a",
"type": "string"
}
],
"outputs": [
{
"name": "d",
"type": "uint256"
}
]
}];
// when
var con = contract(null, description);
// then
assert.equal('function', typeof con.test);
assert.equal('function', typeof con.test['uint256']);
assert.equal('function', typeof con.test['string']);
});
it('should create contract with no methods', function () {
// given
var description = [{
"name": "test(uint256)",
"inputs": [{
"name": "a",
"type": "uint256"
}
],
"outputs": [
{
"name": "d",
"type": "uint256"
}
]
}];
// when
var con = contract(null, description);
// then
assert.equal('undefined', typeof con.test);
});
it('should create contract with one event', function () {
// given
var description = [{
"name": "test",
"type": "event",
"inputs": [{
"name": "a",
"type": "uint256"
}
],
"outputs": [
{
"name": "d",
"type": "uint256"
}
]
}];
// when
var con = contract(null, description);
// then
assert.equal('function', typeof con.test);
assert.equal('function', typeof con.test['uint256']);
});
});

1
libjsqrc/ethereumjs/test/eth.methods.js

@ -24,7 +24,6 @@ describe('web3', function() {
u.propertyExists(web3.eth, 'listening');
u.propertyExists(web3.eth, 'mining');
u.propertyExists(web3.eth, 'gasPrice');
u.propertyExists(web3.eth, 'account');
u.propertyExists(web3.eth, 'accounts');
u.propertyExists(web3.eth, 'peerCount');
u.propertyExists(web3.eth, 'defaultBlock');

124
libjsqrc/ethereumjs/test/event.js

@ -0,0 +1,124 @@
var assert = require('assert');
var event = require('../lib/event.js');
var f = require('../lib/formatters.js');
describe('event', function () {
it('should create basic filter input object', function () {
// given
var address = '0x012345';
var signature = '0x987654';
var e = {
name: 'Event',
inputs: [{"name":"a","type":"uint256","indexed":true},{"name":"b","type":"hash256","indexed":false}]
};
// when
var impl = event(address, signature, e);
var result = impl();
// then
assert.equal(result.address, address);
assert.equal(result.topic.length, 1);
assert.equal(result.topic[0], signature);
});
it('should create filter input object with options', function () {
// given
var address = '0x012345';
var signature = '0x987654';
var options = {
earliest: 1,
latest: 2,
offset: 3,
max: 4
};
var e = {
name: 'Event',
inputs: [{"name":"a","type":"uint256","indexed":true},{"name":"b","type":"hash256","indexed":false}]
};
// when
var impl = event(address, signature, e);
var result = impl({}, options);
// then
assert.equal(result.address, address);
assert.equal(result.topic.length, 1);
assert.equal(result.topic[0], signature);
assert.equal(result.earliest, options.earliest);
assert.equal(result.latest, options.latest);
assert.equal(result.offset, options.offset);
assert.equal(result.max, options.max);
});
it('should create filter input object with indexed params', function () {
// given
var address = '0x012345';
var signature = '0x987654';
var options = {
earliest: 1,
latest: 2,
offset: 3,
max: 4
};
var e = {
name: 'Event',
inputs: [{"name":"a","type":"uint256","indexed":true},{"name":"b","type":"hash256","indexed":false}]
};
// when
var impl = event(address, signature, e);
var result = impl({a: 4}, options);
// then
assert.equal(result.address, address);
assert.equal(result.topic.length, 2);
assert.equal(result.topic[0], signature);
assert.equal(result.topic[1], f.formatInputInt(4));
assert.equal(result.earliest, options.earliest);
assert.equal(result.latest, options.latest);
assert.equal(result.offset, options.offset);
assert.equal(result.max, options.max);
});
it('should create filter input object with an array of indexed params', function () {
// given
var address = '0x012345';
var signature = '0x987654';
var options = {
earliest: 1,
latest: 2,
offset: 3,
max: 4
};
var e = {
name: 'Event',
inputs: [{"name":"a","type":"uint256","indexed":true},{"name":"b","type":"hash256","indexed":false}]
};
// when
var impl = event(address, signature, e);
var result = impl({a: [4, 69]}, options);
// then
assert.equal(result.address, address);
assert.equal(result.topic.length, 2);
assert.equal(result.topic[0], signature);
assert.equal(result.topic[1][0], f.formatInputInt(4));
assert.equal(result.topic[1][1], f.formatInputInt(69));
assert.equal(result.earliest, options.earliest);
assert.equal(result.latest, options.latest);
assert.equal(result.offset, options.offset);
assert.equal(result.max, options.max);
});
});

49
libjsqrc/ethereumjs/test/utils.filters.js

@ -0,0 +1,49 @@
var assert = require('assert');
var utils = require('../lib/utils.js');
describe('utils', function() {
it('should filter functions and events from input array properly', function () {
// given
var description = [{
"name": "test",
"type": "function",
"inputs": [{
"name": "a",
"type": "uint256"
}
],
"outputs": [
{
"name": "d",
"type": "uint256"
}
],
}, {
"name": "test2",
"type": "event",
"inputs": [{
"name": "a",
"type": "uint256"
}
],
"outputs": [
{
"name": "d",
"type": "uint256"
}
]
}];
// when
var events = utils.filterEvents(description);
var functions = utils.filterFunctions(description);
// then
assert.equal(events.length, 1);
assert.equal(events[0].name, 'test2');
assert.equal(functions.length, 1);
assert.equal(functions[0].name, 'test');
});
});

3
libjsqrc/ethereumjs/test/web3.methods.js

@ -6,8 +6,5 @@ describe('web3', function() {
u.methodExists(web3, 'sha3');
u.methodExists(web3, 'toAscii');
u.methodExists(web3, 'fromAscii');
u.methodExists(web3, 'toFixed');
u.methodExists(web3, 'fromFixed');
u.methodExists(web3, 'offset');
});

165
libsolidity/AST.cpp

@ -58,6 +58,11 @@ void ContractDefinition::checkTypeRequirements()
BOOST_THROW_EXCEPTION(constructor->getReturnParameterList()->createTypeError(
"Non-empty \"returns\" directive for constructor."));
FunctionDefinition const* fallbackFunction = getFallbackFunction();
if (fallbackFunction && fallbackFunction->getScope() == this && !fallbackFunction->getParameters().empty())
BOOST_THROW_EXCEPTION(fallbackFunction->getParameterList().createTypeError(
"Fallback function cannot take parameters."));
for (ASTPointer<ModifierDefinition> const& modifier: getFunctionModifiers())
modifier->checkTypeRequirements();
@ -66,24 +71,24 @@ void ContractDefinition::checkTypeRequirements()
// check for hash collisions in function signatures
set<FixedHash<4>> hashes;
for (auto const& hashAndFunction: getInterfaceFunctionList())
for (auto const& it: getInterfaceFunctionList())
{
FixedHash<4> const& hash = std::get<0>(hashAndFunction);
FixedHash<4> const& hash = it.first;
if (hashes.count(hash))
BOOST_THROW_EXCEPTION(createTypeError(
std::string("Function signature hash collision for ") +
std::get<1>(hashAndFunction)->getCanonicalSignature(std::get<2>(hashAndFunction)->getName())));
it.second->getCanonicalSignature()));
hashes.insert(hash);
}
}
map<FixedHash<4>, FunctionDescription> ContractDefinition::getInterfaceFunctions() const
map<FixedHash<4>, FunctionTypePointer> ContractDefinition::getInterfaceFunctions() const
{
auto exportedFunctionList = getInterfaceFunctionList();
map<FixedHash<4>, FunctionDescription> exportedFunctions;
map<FixedHash<4>, FunctionTypePointer> exportedFunctions;
for (auto const& it: exportedFunctionList)
exportedFunctions.insert(make_pair(std::get<0>(it), FunctionDescription(std::get<1>(it), std::get<2>(it))));
exportedFunctions.insert(it);
solAssert(exportedFunctionList.size() == exportedFunctions.size(),
"Hash collision at Function Definition Hash calculation");
@ -99,6 +104,15 @@ FunctionDefinition const* ContractDefinition::getConstructor() const
return nullptr;
}
FunctionDefinition const* ContractDefinition::getFallbackFunction() const
{
for (ContractDefinition const* contract: getLinearizedBaseContracts())
for (ASTPointer<FunctionDefinition> const& f: contract->getDefinedFunctions())
if (f->getName().empty())
return f.get();
return nullptr;
}
void ContractDefinition::checkIllegalOverrides() const
{
// TODO unify this at a later point. for this we need to put the constness and the access specifier
@ -138,20 +152,37 @@ void ContractDefinition::checkIllegalOverrides() const
}
}
vector<tuple<FixedHash<4>, std::shared_ptr<FunctionType const>, Declaration const*>> const& ContractDefinition::getInterfaceFunctionList() const
std::vector<ASTPointer<EventDefinition>> const& ContractDefinition::getInterfaceEvents() const
{
if (!m_interfaceEvents)
{
set<string> eventsSeen;
m_interfaceEvents.reset(new std::vector<ASTPointer<EventDefinition>>());
for (ContractDefinition const* contract: getLinearizedBaseContracts())
for (ASTPointer<EventDefinition> const& e: contract->getEvents())
if (eventsSeen.count(e->getName()) == 0)
{
eventsSeen.insert(e->getName());
m_interfaceEvents->push_back(e);
}
}
return *m_interfaceEvents;
}
vector<pair<FixedHash<4>, FunctionTypePointer>> const& ContractDefinition::getInterfaceFunctionList() const
{
if (!m_interfaceFunctionList)
{
set<string> functionsSeen;
m_interfaceFunctionList.reset(new vector<tuple<FixedHash<4>, std::shared_ptr<FunctionType const>, Declaration const*>>());
m_interfaceFunctionList.reset(new vector<pair<FixedHash<4>, FunctionTypePointer>>());
for (ContractDefinition const* contract: getLinearizedBaseContracts())
{
for (ASTPointer<FunctionDefinition> const& f: contract->getDefinedFunctions())
if (f->isPublic() && !f->isConstructor() && functionsSeen.count(f->getName()) == 0)
if (f->isPublic() && !f->isConstructor() && !f->getName().empty() && functionsSeen.count(f->getName()) == 0)
{
functionsSeen.insert(f->getName());
FixedHash<4> hash(dev::sha3(f->getCanonicalSignature()));
m_interfaceFunctionList->push_back(make_tuple(hash, make_shared<FunctionType>(*f, false), f.get()));
m_interfaceFunctionList->push_back(make_pair(hash, make_shared<FunctionType>(*f, false)));
}
for (ASTPointer<VariableDeclaration> const& v: contract->getStateVariables())
@ -160,7 +191,7 @@ vector<tuple<FixedHash<4>, std::shared_ptr<FunctionType const>, Declaration cons
FunctionType ftype(*v);
functionsSeen.insert(v->getName());
FixedHash<4> hash(dev::sha3(ftype.getCanonicalSignature(v->getName())));
m_interfaceFunctionList->push_back(make_tuple(hash, make_shared<FunctionType>(*v), v.get()));
m_interfaceFunctionList->push_back(make_pair(hash, make_shared<FunctionType>(*v)));
}
}
}
@ -271,6 +302,20 @@ void ModifierInvocation::checkTypeRequirements()
BOOST_THROW_EXCEPTION(createTypeError("Invalid type for argument in modifier invocation."));
}
void EventDefinition::checkTypeRequirements()
{
int numIndexed = 0;
for (ASTPointer<VariableDeclaration> const& var: getParameters())
{
if (var->isIndexed())
numIndexed++;
if (!var->getType()->canLiveOutsideStorage())
BOOST_THROW_EXCEPTION(var->createTypeError("Type is required to live outside storage."));
}
if (numIndexed > 3)
BOOST_THROW_EXCEPTION(createTypeError("More than 3 indexed arguments for event."));
}
void Block::checkTypeRequirements()
{
for (shared_ptr<Statement> const& statement: m_statements)
@ -519,103 +564,5 @@ void Literal::checkTypeRequirements()
BOOST_THROW_EXCEPTION(createTypeError("Invalid literal value."));
}
std::string const& ParamDescription::getName() const
{
return m_description.first;
}
std::string const& ParamDescription::getType() const
{
return m_description.second;
}
ASTPointer<ASTString> FunctionDescription::getDocumentation() const
{
auto function = dynamic_cast<FunctionDefinition const*>(m_description.second);
if (function)
return function->getDocumentation();
return ASTPointer<ASTString>();
}
string FunctionDescription::getSignature() const
{
return m_description.first->getCanonicalSignature(m_description.second->getName());
}
string FunctionDescription::getName() const
{
return m_description.second->getName();
}
bool FunctionDescription::isConstant() const
{
auto function = dynamic_cast<FunctionDefinition const*>(m_description.second);
if (function)
return function->isDeclaredConst();
return true;
}
vector<ParamDescription> const FunctionDescription::getParameters() const
{
auto function = dynamic_cast<FunctionDefinition const*>(m_description.second);
if (function)
{
vector<ParamDescription> paramsDescription;
for (auto const& param: function->getParameters())
paramsDescription.push_back(ParamDescription(param->getName(), param->getType()->toString()));
return paramsDescription;
}
// else for now let's assume no parameters to accessors
// LTODO: fix this for mapping types
return {};
}
vector<ParamDescription> const FunctionDescription::getReturnParameters() const
{
auto function = dynamic_cast<FunctionDefinition const*>(m_description.second);
if (function)
{
vector<ParamDescription> paramsDescription;
for (auto const& param: function->getReturnParameters())
paramsDescription.push_back(ParamDescription(param->getName(), param->getType()->toString()));
return paramsDescription;
}
auto vardecl = dynamic_cast<VariableDeclaration const*>(m_description.second);
return {ParamDescription(vardecl->getName(), vardecl->getType()->toString())};
}
Declaration const* FunctionDescription::getDeclaration() const
{
return m_description.second;
}
VariableDeclaration const* FunctionDescription::getVariableDeclaration() const
{
return dynamic_cast<VariableDeclaration const*>(m_description.second);
}
FunctionDefinition const* FunctionDescription::getFunctionDefinition() const
{
return dynamic_cast<FunctionDefinition const*>(m_description.second);
}
shared_ptr<FunctionType const> FunctionDescription::getFunctionTypeShared() const
{
return m_description.first;
}
FunctionType const* FunctionDescription::getFunctionType() const
{
return m_description.first.get();
}
}
}

127
libsolidity/AST.h

@ -156,71 +156,6 @@ private:
Declaration const* m_scope;
};
/// Traits and Helpers (@todo: move to their own header)
/// @{
/**
* Generic Parameter description used by @see FunctionDescription to return
* a descripton of its parameters.
*/
struct ParamDescription
{
ParamDescription(std::string const& _name, std::string const& _type):
m_description(_name, _type){}
std::string const& getName() const;
std::string const& getType() const;
std::pair<std::string, std::string> m_description;
};
/**
* Generic function description able to describe both normal functions and
* functions that should be made as accessors to state variables
*/
struct FunctionDescription
{
FunctionDescription(std::shared_ptr<FunctionType const> _type, Declaration const* _decl):
m_description(_type, _decl){}
/// constructor for a constructor's function definition. Used only inside mix.
FunctionDescription(Declaration const* _def):
m_description(nullptr, _def){}
FunctionDescription():
m_description(nullptr, nullptr){}
/// @returns the natspec documentation of the function if existing. Accessor (for now) don't have natspec doc
ASTPointer<ASTString> getDocumentation() const;
/// @returns the canonical signature of the function
std::string getSignature() const;
/// @returns the name of the function, basically that of the declaration
std::string getName() const;
/// @returns whether the function is constant. IF it's an accessor this is always true
bool isConstant() const;
/// @returns the argument parameters of the function
std::vector<ParamDescription> const getParameters() const;
/// @returns the return parameters of the function
std::vector<ParamDescription> const getReturnParameters() const;
/// @returns a generic Declaration AST Node pointer which can be either a FunctionDefinition or a VariableDeclaration
Declaration const* getDeclaration() const;
/// @returns the VariableDeclaration AST Node pointer or nullptr if it's not a VariableDeclaration
VariableDeclaration const* getVariableDeclaration() const;
/// @returns the FunctionDefinition AST Node pointer or nullptr if it's not a FunctionDefinition
FunctionDefinition const* getFunctionDefinition() const;
/// @returns a created shared pointer with the type of the function
std::shared_ptr<FunctionType> makeFunctionType() const;
/// @returns a pointer to the function type
FunctionType const* getFunctionType() const;
/// @returns a shared pointer to the function type
std::shared_ptr<FunctionType const> getFunctionTypeShared() const;
std::pair<std::shared_ptr<FunctionType const>, Declaration const*> m_description;
};
/**
* Abstract class that is added to each AST node that can store local variables.
*/
@ -252,7 +187,6 @@ protected:
/// @}
/**
* Definition of a contract. This is the only AST nodes where child nodes are not visited in
* document order. It first visits all struct declarations, then all variable declarations and
@ -268,13 +202,15 @@ public:
std::vector<ASTPointer<StructDefinition>> const& _definedStructs,
std::vector<ASTPointer<VariableDeclaration>> const& _stateVariables,
std::vector<ASTPointer<FunctionDefinition>> const& _definedFunctions,
std::vector<ASTPointer<ModifierDefinition>> const& _functionModifiers):
std::vector<ASTPointer<ModifierDefinition>> const& _functionModifiers,
std::vector<ASTPointer<EventDefinition>> const& _events):
Declaration(_location, _name), Documented(_documentation),
m_baseContracts(_baseContracts),
m_definedStructs(_definedStructs),
m_stateVariables(_stateVariables),
m_definedFunctions(_definedFunctions),
m_functionModifiers(_functionModifiers)
m_functionModifiers(_functionModifiers),
m_events(_events)
{}
virtual void accept(ASTVisitor& _visitor) override;
@ -285,6 +221,8 @@ public:
std::vector<ASTPointer<VariableDeclaration>> const& getStateVariables() const { return m_stateVariables; }
std::vector<ASTPointer<ModifierDefinition>> const& getFunctionModifiers() const { return m_functionModifiers; }
std::vector<ASTPointer<FunctionDefinition>> const& getDefinedFunctions() const { return m_definedFunctions; }
std::vector<ASTPointer<EventDefinition>> const& getEvents() const { return m_events; }
std::vector<ASTPointer<EventDefinition>> const& getInterfaceEvents() const;
virtual TypePointer getType(ContractDefinition const* m_currentContract) const override;
@ -294,29 +232,33 @@ public:
/// @returns a map of canonical function signatures to FunctionDefinitions
/// as intended for use by the ABI.
std::map<FixedHash<4>, FunctionDescription> getInterfaceFunctions() const;
std::map<FixedHash<4>, FunctionTypePointer> getInterfaceFunctions() const;
/// List of all (direct and indirect) base contracts in order from derived to base, including
/// the contract itself. Available after name resolution
std::vector<ContractDefinition const*> const& getLinearizedBaseContracts() const { return m_linearizedBaseContracts; }
void setLinearizedBaseContracts(std::vector<ContractDefinition const*> const& _bases) { m_linearizedBaseContracts = _bases; }
/// Returns the constructor or nullptr if no constructor was specified
/// Returns the constructor or nullptr if no constructor was specified.
FunctionDefinition const* getConstructor() const;
/// Returns the fallback function or nullptr if no constructor was specified.
FunctionDefinition const* getFallbackFunction() const;
private:
void checkIllegalOverrides() const;
std::vector<std::tuple<FixedHash<4>, std::shared_ptr<FunctionType const>, Declaration const*>> const& getInterfaceFunctionList() const;
std::vector<std::pair<FixedHash<4>, FunctionTypePointer>> const& getInterfaceFunctionList() const;
std::vector<ASTPointer<InheritanceSpecifier>> m_baseContracts;
std::vector<ASTPointer<StructDefinition>> m_definedStructs;
std::vector<ASTPointer<VariableDeclaration>> m_stateVariables;
std::vector<ASTPointer<FunctionDefinition>> m_definedFunctions;
std::vector<ASTPointer<ModifierDefinition>> m_functionModifiers;
std::vector<ASTPointer<EventDefinition>> m_events;
std::vector<ContractDefinition const*> m_linearizedBaseContracts;
mutable std::unique_ptr<std::vector<std::tuple<FixedHash<4>, std::shared_ptr<FunctionType const>, Declaration const*>>> m_interfaceFunctionList;
mutable std::unique_ptr<std::vector<std::pair<FixedHash<4>, FunctionTypePointer>>> m_interfaceFunctionList;
mutable std::unique_ptr<std::vector<ASTPointer<EventDefinition>>> m_interfaceEvents;
};
class InheritanceSpecifier: public ASTNode
@ -446,8 +388,10 @@ class VariableDeclaration: public Declaration
{
public:
VariableDeclaration(Location const& _location, ASTPointer<TypeName> const& _type,
ASTPointer<ASTString> const& _name, bool _isPublic, bool _isStateVar = false):
Declaration(_location, _name), m_typeName(_type), m_isPublic(_isPublic), m_isStateVariable(_isStateVar) {}
ASTPointer<ASTString> const& _name, bool _isPublic, bool _isStateVar = false,
bool _isIndexed = false):
Declaration(_location, _name), m_typeName(_type),
m_isPublic(_isPublic), m_isStateVariable(_isStateVar), m_isIndexed(_isIndexed) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
@ -462,12 +406,13 @@ public:
bool isLocalVariable() const { return !!dynamic_cast<FunctionDefinition const*>(getScope()); }
bool isPublic() const { return m_isPublic; }
bool isStateVariable() const { return m_isStateVariable; }
bool isIndexed() const { return m_isIndexed; }
private:
ASTPointer<TypeName> m_typeName; ///< can be empty ("var")
bool m_isPublic; ///< Whether there is an accessor for it or not
bool m_isStateVariable; ///< Whether or not this is a contract state variable
bool m_isIndexed; ///< Whether this is an indexed variable (used by events).
std::shared_ptr<Type const> m_type; ///< derived type, initially empty
};
@ -495,7 +440,6 @@ public:
virtual TypePointer getType(ContractDefinition const* = nullptr) const override;
void checkTypeRequirements();
private:
@ -526,6 +470,37 @@ private:
std::vector<ASTPointer<Expression>> m_arguments;
};
/**
* Definition of a (loggable) event.
*/
class EventDefinition: public Declaration, public VariableScope, public Documented
{
public:
EventDefinition(Location const& _location,
ASTPointer<ASTString> const& _name,
ASTPointer<ASTString> const& _documentation,
ASTPointer<ParameterList> const& _parameters):
Declaration(_location, _name), Documented(_documentation), m_parameters(_parameters) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
std::vector<ASTPointer<VariableDeclaration>> const& getParameters() const { return m_parameters->getParameters(); }
ParameterList const& getParameterList() const { return *m_parameters; }
Block const& getBody() const { return *m_body; }
virtual TypePointer getType(ContractDefinition const* = nullptr) const override
{
return std::make_shared<FunctionType>(*this);
}
void checkTypeRequirements();
private:
ASTPointer<ParameterList> m_parameters;
ASTPointer<Block> m_body;
};
/**
* Pseudo AST node that is used as declaration for "this", "msg", "tx", "block" and the global
* functions when such an identifier is encountered. Will never have a valid location in the source code.

1
libsolidity/ASTForward.h

@ -45,6 +45,7 @@ class FunctionDefinition;
class VariableDeclaration;
class ModifierDefinition;
class ModifierInvocation;
class EventDefinition;
class MagicVariableDeclaration;
class TypeName;
class ElementaryTypeName;

12
libsolidity/ASTPrinter.cpp

@ -108,6 +108,13 @@ bool ASTPrinter::visit(ModifierInvocation const& _node)
return goDeeper();
}
bool ASTPrinter::visit(EventDefinition const& _node)
{
writeLine("EventDefinition \"" + _node.getName() + "\"");
printSourcePart(_node);
return goDeeper();
}
bool ASTPrinter::visit(TypeName const& _node)
{
writeLine("TypeName");
@ -365,6 +372,11 @@ void ASTPrinter::endVisit(ModifierInvocation const&)
m_indentation--;
}
void ASTPrinter::endVisit(EventDefinition const&)
{
m_indentation--;
}
void ASTPrinter::endVisit(TypeName const&)
{
m_indentation--;

2
libsolidity/ASTPrinter.h

@ -51,6 +51,7 @@ public:
bool visit(VariableDeclaration const& _node) override;
bool visit(ModifierDefinition const& _node) override;
bool visit(ModifierInvocation const& _node) override;
bool visit(EventDefinition const& _node) override;
bool visit(TypeName const& _node) override;
bool visit(ElementaryTypeName const& _node) override;
bool visit(UserDefinedTypeName const& _node) override;
@ -89,6 +90,7 @@ public:
void endVisit(VariableDeclaration const&) override;
void endVisit(ModifierDefinition const&) override;
void endVisit(ModifierInvocation const&) override;
void endVisit(EventDefinition const&) override;
void endVisit(TypeName const&) override;
void endVisit(ElementaryTypeName const&) override;
void endVisit(UserDefinedTypeName const&) override;

4
libsolidity/ASTVisitor.h

@ -52,6 +52,7 @@ public:
virtual bool visit(VariableDeclaration&) { return true; }
virtual bool visit(ModifierDefinition&) { return true; }
virtual bool visit(ModifierInvocation&) { return true; }
virtual bool visit(EventDefinition&) { return true; }
virtual bool visit(TypeName&) { return true; }
virtual bool visit(ElementaryTypeName&) { return true; }
virtual bool visit(UserDefinedTypeName&) { return true; }
@ -92,6 +93,7 @@ public:
virtual void endVisit(VariableDeclaration&) { }
virtual void endVisit(ModifierDefinition&) { }
virtual void endVisit(ModifierInvocation&) { }
virtual void endVisit(EventDefinition&) { }
virtual void endVisit(TypeName&) { }
virtual void endVisit(ElementaryTypeName&) { }
virtual void endVisit(UserDefinedTypeName&) { }
@ -136,6 +138,7 @@ public:
virtual bool visit(VariableDeclaration const&) { return true; }
virtual bool visit(ModifierDefinition const&) { return true; }
virtual bool visit(ModifierInvocation const&) { return true; }
virtual bool visit(EventDefinition const&) { return true; }
virtual bool visit(TypeName const&) { return true; }
virtual bool visit(ElementaryTypeName const&) { return true; }
virtual bool visit(UserDefinedTypeName const&) { return true; }
@ -176,6 +179,7 @@ public:
virtual void endVisit(VariableDeclaration const&) { }
virtual void endVisit(ModifierDefinition const&) { }
virtual void endVisit(ModifierInvocation const&) { }
virtual void endVisit(EventDefinition const&) { }
virtual void endVisit(TypeName const&) { }
virtual void endVisit(ElementaryTypeName const&) { }
virtual void endVisit(UserDefinedTypeName const&) { }

20
libsolidity/AST_accept.h

@ -64,8 +64,9 @@ void ContractDefinition::accept(ASTVisitor& _visitor)
listAccept(m_baseContracts, _visitor);
listAccept(m_definedStructs, _visitor);
listAccept(m_stateVariables, _visitor);
listAccept(m_definedFunctions, _visitor);
listAccept(m_events, _visitor);
listAccept(m_functionModifiers, _visitor);
listAccept(m_definedFunctions, _visitor);
}
_visitor.endVisit(*this);
}
@ -77,8 +78,9 @@ void ContractDefinition::accept(ASTConstVisitor& _visitor) const
listAccept(m_baseContracts, _visitor);
listAccept(m_definedStructs, _visitor);
listAccept(m_stateVariables, _visitor);
listAccept(m_definedFunctions, _visitor);
listAccept(m_events, _visitor);
listAccept(m_functionModifiers, _visitor);
listAccept(m_definedFunctions, _visitor);
}
_visitor.endVisit(*this);
}
@ -219,6 +221,20 @@ void ModifierInvocation::accept(ASTConstVisitor& _visitor) const
_visitor.endVisit(*this);
}
void EventDefinition::accept(ASTVisitor& _visitor)
{
if (_visitor.visit(*this))
m_parameters->accept(_visitor);
_visitor.endVisit(*this);
}
void EventDefinition::accept(ASTConstVisitor& _visitor) const
{
if (_visitor.visit(*this))
m_parameters->accept(_visitor);
_visitor.endVisit(*this);
}
void TypeName::accept(ASTVisitor& _visitor)
{
_visitor.visit(*this);

16
libsolidity/Compiler.cpp

@ -142,11 +142,11 @@ void Compiler::appendConstructorCall(FunctionDefinition const& _constructor)
void Compiler::appendFunctionSelector(ContractDefinition const& _contract)
{
map<FixedHash<4>, FunctionDescription> interfaceFunctions = _contract.getInterfaceFunctions();
map<FixedHash<4>, FunctionTypePointer> interfaceFunctions = _contract.getInterfaceFunctions();
map<FixedHash<4>, const eth::AssemblyItem> callDataUnpackerEntryPoints;
// retrieve the function signature hash from the calldata
m_context << u256(1) << u256(0);
if (!interfaceFunctions.empty())
CompilerUtils(m_context).loadFromMemory(0, 4, false, true);
// stack now is: 1 0 <funhash>
@ -156,15 +156,23 @@ void Compiler::appendFunctionSelector(ContractDefinition const& _contract)
m_context << eth::dupInstruction(1) << u256(FixedHash<4>::Arith(it.first)) << eth::Instruction::EQ;
m_context.appendConditionalJumpTo(callDataUnpackerEntryPoints.at(it.first));
}
if (FunctionDefinition const* fallback = _contract.getFallbackFunction())
{
eth::AssemblyItem returnTag = m_context.pushNewTag();
fallback->accept(*this);
m_context << returnTag;
appendReturnValuePacker(FunctionType(*fallback).getReturnParameterTypes());
}
else
m_context << eth::Instruction::STOP; // function not found
for (auto const& it: interfaceFunctions)
{
FunctionType const* functionType = it.second.getFunctionType();
FunctionTypePointer const& functionType = it.second;
m_context << callDataUnpackerEntryPoints.at(it.first);
eth::AssemblyItem returnTag = m_context.pushNewTag();
appendCalldataUnpacker(functionType->getParameterTypes());
m_context.appendJumpTo(m_context.getFunctionEntryLabel(*it.second.getDeclaration()));
m_context.appendJumpTo(m_context.getFunctionEntryLabel(it.second->getDeclaration()));
m_context << returnTag;
appendReturnValuePacker(functionType->getReturnParameterTypes());
}

56
libsolidity/CompilerStack.cpp

@ -65,14 +65,14 @@ bool CompilerStack::addSource(string const& _name, string const& _content)
{
bool existed = m_sources.count(_name) != 0;
reset(true);
m_sources[_name].scanner = make_shared<Scanner>(CharStream(_content), _name);
m_sources[_name].scanner = make_shared<Scanner>(CharStream(expanded(_content)), _name);
return existed;
}
void CompilerStack::setSource(string const& _sourceCode)
{
reset();
addSource("", _sourceCode);
addSource("", expanded(_sourceCode));
}
void CompilerStack::parse()
@ -126,6 +126,58 @@ vector<string> CompilerStack::getContractNames() const
return contractNames;
}
////// BEGIN: TEMPORARY ONLY
///
/// NOTE: THIS INVALIDATES SOURCE POINTERS AND CAN CRASH THE COMPILER
///
/// remove once import works properly and we have genesis contracts
string CompilerStack::expanded(string const& _sourceCode)
{
const map<string, string> c_standardSources = map<string, string>{
{ "Config", "contract Config{function lookup(uint256 service)constant returns(address a){}function kill(){}function unregister(uint256 id){}function register(uint256 id,address service){}}" },
{ "Coin", "contract Coin{function isApprovedFor(address _target,address _proxy)constant returns(bool _r){}function isApproved(address _proxy)constant returns(bool _r){}function sendCoinFrom(address _from,uint256 _val,address _to){}function coinBalanceOf(address _a)constant returns(uint256 _r){}function sendCoin(uint256 _val,address _to){}function coinBalance()constant returns(uint256 _r){}function approve(address _a){}}"},
{ "CoinReg", "contract CoinReg{function count()constant returns(uint256 r){}function info(uint256 i)constant returns(address addr,string3 name,uint256 denom){}function register(string3 name,uint256 denom){}function unregister(){}}" },
{ "coin", "#require CoinReg\ncontract coin {function coin(string3 name, uint denom) {CoinReg(Config().lookup(3)).register(name, denom);}}" },
{ "service", "#require Config\ncontract service{function service(uint _n){Config().register(_n, this);}}" },
{ "owned", "contract owned{function owned(){owner = msg.sender;}modifier onlyowner(){if(msg.sender==owner)_}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 {function named(string32 name) {NameReg(Config().lookup(1)).register(name);}}" },
{ "std", "#require owned mortal Config NameReg named" },
};
string sub;
set<string> got;
function<string(string const&)> 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<string> 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_standardSources.count(r))
sub.append("\n" + localExpanded(c_standardSources.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(0xc6d9d2cd449a754c494264e1809c50e34d64562b)");
return ret;
};
return sub + localExpanded(_sourceCode);
}
////// END: TEMPORARY ONLY
void CompilerStack::compile(bool _optimize)
{
if (!m_parseSuccessful)

4
libsolidity/CompilerStack.h

@ -142,6 +142,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();

81
libsolidity/ExpressionCompiler.cpp

@ -23,6 +23,7 @@
#include <utility>
#include <numeric>
#include <libdevcore/Common.h>
#include <libdevcrypto/SHA3.h>
#include <libsolidity/AST.h>
#include <libsolidity/ExpressionCompiler.h>
#include <libsolidity/CompilerContext.h>
@ -304,10 +305,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
m_context << eth::Instruction::SUICIDE;
break;
case Location::SHA3:
arguments.front()->accept(*this);
appendTypeConversion(*arguments.front()->getType(), *function.getParameterTypes().front(), true);
// @todo move this once we actually use memory
CompilerUtils(m_context).storeInMemory(0);
appendExpressionCopyToMemory(*function.getParameterTypes().front(), *arguments.front());
m_context << u256(32) << u256(0) << eth::Instruction::SHA3;
break;
case Location::LOG0:
@ -317,14 +315,41 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
case Location::LOG4:
{
unsigned logNumber = int(function.getLocation()) - int(Location::LOG0);
for (int arg = logNumber; arg >= 0; --arg)
for (unsigned arg = logNumber; arg > 0; --arg)
{
arguments[arg]->accept(*this);
appendTypeConversion(*arguments[arg]->getType(), *function.getParameterTypes()[arg], true);
}
// @todo move this once we actually use memory
CompilerUtils(m_context).storeInMemory(0);
m_context << u256(32) << u256(0) << eth::logInstruction(logNumber);
unsigned length = appendExpressionCopyToMemory(*function.getParameterTypes().front(),
*arguments.front());
solAssert(length == 32, "Log data should be 32 bytes long (for now).");
m_context << u256(length) << u256(0) << eth::logInstruction(logNumber);
break;
}
case Location::EVENT:
{
_functionCall.getExpression().accept(*this);
auto const& event = dynamic_cast<EventDefinition const&>(function.getDeclaration());
// Copy all non-indexed arguments to memory (data)
unsigned numIndexed = 0;
unsigned memLength = 0;
for (unsigned arg = 0; arg < arguments.size(); ++arg)
if (!event.getParameters()[arg]->isIndexed())
memLength += appendExpressionCopyToMemory(*function.getParameterTypes()[arg],
*arguments[arg], memLength);
// All indexed arguments go to the stack
for (unsigned arg = arguments.size(); arg > 0; --arg)
if (event.getParameters()[arg - 1]->isIndexed())
{
++numIndexed;
arguments[arg - 1]->accept(*this);
appendTypeConversion(*arguments[arg - 1]->getType(),
*function.getParameterTypes()[arg - 1], true);
}
m_context << u256(h256::Arith(dev::sha3(function.getCanonicalSignature(event.getName()))));
++numIndexed;
solAssert(numIndexed <= 4, "Too many indexed arguments.");
m_context << u256(memLength) << u256(0) << eth::logInstruction(numIndexed);
break;
}
case Location::BLOCKHASH:
@ -459,14 +484,13 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess)
bool ExpressionCompiler::visit(IndexAccess const& _indexAccess)
{
_indexAccess.getBaseExpression().accept(*this);
_indexAccess.getIndexExpression().accept(*this);
appendTypeConversion(*_indexAccess.getIndexExpression().getType(),
*dynamic_cast<MappingType const&>(*_indexAccess.getBaseExpression().getType()).getKeyType(),
true);
TypePointer const& keyType = dynamic_cast<MappingType const&>(*_indexAccess.getBaseExpression().getType()).getKeyType();
unsigned length = appendExpressionCopyToMemory(*keyType, _indexAccess.getIndexExpression());
solAssert(length == 32, "Mapping key has to take 32 bytes in memory (for now).");
// @todo move this once we actually use memory
CompilerUtils(m_context).storeInMemory(0);
CompilerUtils(m_context).storeInMemory(32);
m_context << u256(64) << u256(0) << eth::Instruction::SHA3;
length += CompilerUtils(m_context).storeInMemory(length);
m_context << u256(length) << u256(0) << eth::Instruction::SHA3;
m_currentLValue = LValue(m_context, LValue::STORAGE, *_indexAccess.getType());
m_currentLValue.retrieveValueIfLValueNotRequested(_indexAccess);
@ -495,6 +519,10 @@ void ExpressionCompiler::endVisit(Identifier const& _identifier)
{
// no-op
}
else if (dynamic_cast<EventDefinition const*>(declaration))
{
// no-op
}
else
{
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Identifier type not expected in expression context."));
@ -791,20 +819,23 @@ unsigned ExpressionCompiler::appendArgumentCopyToMemory(TypePointers const& _typ
{
unsigned length = 0;
for (unsigned i = 0; i < _arguments.size(); ++i)
length += appendExpressionCopyToMemory(*_types[i], *_arguments[i], _memoryOffset + length);
return length;
}
unsigned ExpressionCompiler::appendExpressionCopyToMemory(Type const& _expectedType,
Expression const& _expression, unsigned _memoryOffset)
{
_arguments[i]->accept(*this);
appendTypeConversion(*_arguments[i]->getType(), *_types[i], true);
unsigned const c_numBytes = _types[i]->getCalldataEncodedSize();
_expression.accept(*this);
appendTypeConversion(*_expression.getType(), _expectedType, true);
unsigned const c_numBytes = CompilerUtils::getPaddedSize(_expectedType.getCalldataEncodedSize());
if (c_numBytes == 0 || c_numBytes > 32)
BOOST_THROW_EXCEPTION(CompilerError()
<< errinfo_sourceLocation(_arguments[i]->getLocation())
<< errinfo_comment("Type " + _types[i]->toString() + " not yet supported."));
bool const c_leftAligned = _types[i]->getCategory() == Type::Category::STRING;
<< errinfo_sourceLocation(_expression.getLocation())
<< errinfo_comment("Type " + _expectedType.toString() + " not yet supported."));
bool const c_leftAligned = _expectedType.getCategory() == Type::Category::STRING;
bool const c_padToWords = true;
length += CompilerUtils(m_context).storeInMemory(_memoryOffset + length, c_numBytes,
c_leftAligned, c_padToWords);
}
return length;
return CompilerUtils(m_context).storeInMemory(_memoryOffset, c_numBytes, c_leftAligned, c_padToWords);
}
void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const& _varDecl)

7
libsolidity/ExpressionCompiler.h

@ -94,7 +94,12 @@ private:
bool bare = false);
/// Appends code that copies the given arguments to memory (with optional offset).
/// @returns the number of bytes copied to memory
unsigned appendArgumentCopyToMemory(TypePointers const& _functionType, std::vector<ASTPointer<Expression const>> const& _arguments,
unsigned appendArgumentCopyToMemory(TypePointers const& _types,
std::vector<ASTPointer<Expression const>> const& _arguments,
unsigned _memoryOffset = 0);
/// Appends code that evaluates a single expression and copies it to memory (with optional offset).
/// @returns the number of bytes copied to memory
unsigned appendExpressionCopyToMemory(Type const& _expectedType, Expression const& _expression,
unsigned _memoryOffset = 0);
/// Appends code for a State Variable accessor function

86
libsolidity/InterfaceHandler.cpp

@ -37,34 +37,52 @@ std::unique_ptr<std::string> InterfaceHandler::getDocumentation(ContractDefiniti
std::unique_ptr<std::string> InterfaceHandler::getABIInterface(ContractDefinition const& _contractDef)
{
Json::Value methods(Json::arrayValue);
Json::Value abi(Json::arrayValue);
for (auto const& it: _contractDef.getInterfaceFunctions())
{
auto populateParameters = [](vector<string> const& _paramNames, vector<string> const& _paramTypes)
{
Json::Value params(Json::arrayValue);
solAssert(_paramNames.size() == _paramTypes.size(), "Names and types vector size does not match");
for (unsigned i = 0; i < _paramNames.size(); ++i)
{
Json::Value param;
param["name"] = _paramNames[i];
param["type"] = _paramTypes[i];
params.append(param);
}
return params;
};
Json::Value method;
Json::Value inputs(Json::arrayValue);
Json::Value outputs(Json::arrayValue);
method["type"] = "function";
method["name"] = it.second->getDeclaration().getName();
method["constant"] = it.second->isConstant();
method["inputs"] = populateParameters(it.second->getParameterNames(),
it.second->getParameterTypeNames());
method["outputs"] = populateParameters(it.second->getReturnParameterNames(),
it.second->getReturnParameterTypeNames());
abi.append(method);
}
auto populateParameters = [](vector<ParamDescription> const& _params)
for (auto const& it: _contractDef.getInterfaceEvents())
{
Json::Value event;
event["type"] = "event";
event["name"] = it->getName();
Json::Value params(Json::arrayValue);
for (auto const& param: _params)
for (auto const& p: it->getParameters())
{
Json::Value input;
input["name"] = param.getName();
input["type"] = param.getType();
input["name"] = p->getName();
input["type"] = p->getType()->toString();
input["indexed"] = p->isIndexed();
params.append(input);
}
return params;
};
method["name"] = it.second.getName();
method["constant"] = it.second.isConstant();
method["inputs"] = populateParameters(it.second.getParameters());
method["outputs"] = populateParameters(it.second.getReturnParameters());
methods.append(method);
event["inputs"] = params;
abi.append(event);
}
return std::unique_ptr<std::string>(new std::string(m_writer.write(methods)));
return std::unique_ptr<std::string>(new std::string(m_writer.write(abi)));
}
unique_ptr<string> InterfaceHandler::getABISolidityInterface(ContractDefinition const& _contractDef)
@ -72,20 +90,34 @@ unique_ptr<string> InterfaceHandler::getABISolidityInterface(ContractDefinition
string ret = "contract " + _contractDef.getName() + "{";
for (auto const& it: _contractDef.getInterfaceFunctions())
{
auto populateParameters = [](vector<ParamDescription> const& _params)
auto populateParameters = [](vector<string> const& _paramNames,
vector<string> const& _paramTypes)
{
string r = "";
for (auto const& param: _params)
r += (r.size() ? "," : "(") + param.getType() + " " + param.getName();
solAssert(_paramNames.size() == _paramTypes.size(), "Names and types vector size does not match");
for (unsigned i = 0; i < _paramNames.size(); ++i)
r += (r.size() ? "," : "(") + _paramTypes[i] + " " + _paramNames[i];
return r.size() ? r + ")" : "()";
};
ret += "function " + it.second.getName() + populateParameters(it.second.getParameters()) + (it.second.isConstant() ? "constant " : "");
if (it.second.getReturnParameters().size())
ret += "returns" + populateParameters(it.second.getReturnParameters());
ret += "function " + it.second->getDeclaration().getName() +
populateParameters(it.second->getParameterNames(), it.second->getParameterTypeNames()) +
(it.second->isConstant() ? "constant " : "");
if (it.second->getReturnParameterTypes().size())
ret += "returns" + populateParameters(it.second->getReturnParameterNames(), it.second->getReturnParameterTypeNames());
else if (ret.back() == ' ')
ret.pop_back();
ret += "{}";
}
for (auto const& it: _contractDef.getInterfaceEvents())
{
std::string params;
for (auto const& p: it->getParameters())
params += (params.empty() ? "(" : ",") + p->getType()->toString() + (p->isIndexed() ? " indexed " : " ") + p->getName();
if (!params.empty())
params += ")";
ret += "event " + it->getName() + params + ";";
}
return unique_ptr<string>(new string(ret + "}"));
}
@ -97,7 +129,7 @@ std::unique_ptr<std::string> InterfaceHandler::getUserDocumentation(ContractDefi
for (auto const& it: _contractDef.getInterfaceFunctions())
{
Json::Value user;
auto strPtr = it.second.getDocumentation();
auto strPtr = it.second->getDocumentation();
if (strPtr)
{
resetUser();
@ -105,7 +137,7 @@ std::unique_ptr<std::string> InterfaceHandler::getUserDocumentation(ContractDefi
if (!m_notice.empty())
{// since @notice is the only user tag if missing function should not appear
user["notice"] = Json::Value(m_notice);
methods[it.second.getSignature()] = user;
methods[it.second->getCanonicalSignature()] = user;
}
}
}
@ -138,7 +170,7 @@ std::unique_ptr<std::string> InterfaceHandler::getDevDocumentation(ContractDefin
for (auto const& it: _contractDef.getInterfaceFunctions())
{
Json::Value method;
auto strPtr = it.second.getDocumentation();
auto strPtr = it.second->getDocumentation();
if (strPtr)
{
resetDev();
@ -161,7 +193,7 @@ std::unique_ptr<std::string> InterfaceHandler::getDevDocumentation(ContractDefin
method["return"] = m_return;
if (!method.empty()) // add the function, only if we have any documentation to add
methods[it.second.getSignature()] = method;
methods[it.second->getCanonicalSignature()] = method;
}
}
doc["methods"] = methods;

13
libsolidity/NameAndTypeResolver.cpp

@ -60,6 +60,8 @@ void NameAndTypeResolver::resolveNamesAndTypes(ContractDefinition& _contract)
ReferencesResolver resolver(*structDef, *this, &_contract, nullptr);
for (ASTPointer<VariableDeclaration> const& variable: _contract.getStateVariables())
ReferencesResolver resolver(*variable, *this, &_contract, nullptr);
for (ASTPointer<EventDefinition> const& event: _contract.getEvents())
ReferencesResolver resolver(*event, *this, &_contract, nullptr);
for (ASTPointer<ModifierDefinition> const& modifier: _contract.getFunctionModifiers())
{
m_currentScope = &m_scopes[modifier.get()];
@ -259,6 +261,17 @@ bool DeclarationRegistrationHelper::visit(VariableDeclaration& _declaration)
return true;
}
bool DeclarationRegistrationHelper::visit(EventDefinition& _event)
{
registerDeclaration(_event, true);
return true;
}
void DeclarationRegistrationHelper::endVisit(EventDefinition&)
{
closeCurrentScope();
}
void DeclarationRegistrationHelper::enterNewSubScope(Declaration const& _declaration)
{
map<ASTNode const*, DeclarationContainer>::iterator iter;

22
libsolidity/NameAndTypeResolver.h

@ -94,16 +94,18 @@ public:
DeclarationRegistrationHelper(std::map<ASTNode const*, DeclarationContainer>& _scopes, ASTNode& _astRoot);
private:
bool visit(ContractDefinition& _contract);
void endVisit(ContractDefinition& _contract);
bool visit(StructDefinition& _struct);
void endVisit(StructDefinition& _struct);
bool visit(FunctionDefinition& _function);
void endVisit(FunctionDefinition& _function);
bool visit(ModifierDefinition& _modifier);
void endVisit(ModifierDefinition& _modifier);
void endVisit(VariableDefinition& _variableDefinition);
bool visit(VariableDeclaration& _declaration);
bool visit(ContractDefinition& _contract) override;
void endVisit(ContractDefinition& _contract) override;
bool visit(StructDefinition& _struct) override;
void endVisit(StructDefinition& _struct) override;
bool visit(FunctionDefinition& _function) override;
void endVisit(FunctionDefinition& _function) override;
bool visit(ModifierDefinition& _modifier) override;
void endVisit(ModifierDefinition& _modifier) override;
void endVisit(VariableDefinition& _variableDefinition) override;
bool visit(VariableDeclaration& _declaration) override;
bool visit(EventDefinition& _event) override;
void endVisit(EventDefinition& _event) override;
void enterNewSubScope(Declaration const& _declaration);
void closeCurrentScope();

88
libsolidity/Parser.cpp

@ -122,6 +122,7 @@ ASTPointer<ContractDefinition> Parser::parseContractDefinition()
vector<ASTPointer<VariableDeclaration>> stateVariables;
vector<ASTPointer<FunctionDefinition>> functions;
vector<ASTPointer<ModifierDefinition>> modifiers;
vector<ASTPointer<EventDefinition>> events;
if (m_scanner->getCurrentToken() == Token::IS)
do
{
@ -149,19 +150,23 @@ ASTPointer<ContractDefinition> Parser::parseContractDefinition()
else if (currentToken == Token::IDENTIFIER || currentToken == Token::MAPPING ||
Token::isElementaryTypeName(currentToken))
{
bool const allowVar = false;
stateVariables.push_back(parseVariableDeclaration(allowVar, visibilityIsPublic, true));
VarDeclParserOptions options;
options.isPublic = visibilityIsPublic;
options.isStateVariable = true;
stateVariables.push_back(parseVariableDeclaration(options));
expectToken(Token::SEMICOLON);
}
else if (currentToken == Token::MODIFIER)
modifiers.push_back(parseModifierDefinition());
else if (currentToken == Token::EVENT)
events.push_back(parseEventDefinition());
else
BOOST_THROW_EXCEPTION(createParserError("Function, variable, struct or modifier declaration expected."));
}
nodeFactory.markEndPosition();
expectToken(Token::RBRACE);
return nodeFactory.createNode<ContractDefinition>(name, docString, baseContracts, structs,
stateVariables, functions, modifiers);
stateVariables, functions, modifiers, events);
}
ASTPointer<InheritanceSpecifier> Parser::parseInheritanceSpecifier()
@ -189,7 +194,11 @@ ASTPointer<FunctionDefinition> Parser::parseFunctionDefinition(bool _isPublic, A
docstring = make_shared<ASTString>(m_scanner->getCurrentCommentLiteral());
expectToken(Token::FUNCTION);
ASTPointer<ASTString> name(expectIdentifierToken());
ASTPointer<ASTString> name;
if (m_scanner->getCurrentToken() == Token::LPAREN)
name = make_shared<ASTString>(); // anonymous function
else
name = expectIdentifierToken();
ASTPointer<ParameterList> parameters(parseParameterList());
bool isDeclaredConst = false;
vector<ASTPointer<ModifierInvocation>> modifiers;
@ -213,12 +222,7 @@ ASTPointer<FunctionDefinition> Parser::parseFunctionDefinition(bool _isPublic, A
returnParameters = parseParameterList(permitEmptyParameterList);
}
else
{
// create an empty parameter list at a zero-length location
ASTNodeFactory nodeFactory(*this);
nodeFactory.setLocationEmpty();
returnParameters = nodeFactory.createNode<ParameterList>(vector<ASTPointer<VariableDeclaration>>());
}
returnParameters = createEmptyParameterList();
ASTPointer<Block> block = parseBlock();
nodeFactory.setEndPositionFromNode(block);
bool const c_isConstructor = (_contractName && *name == *_contractName);
@ -236,8 +240,7 @@ ASTPointer<StructDefinition> Parser::parseStructDefinition()
expectToken(Token::LBRACE);
while (m_scanner->getCurrentToken() != Token::RBRACE)
{
bool const allowVar = false;
members.push_back(parseVariableDeclaration(allowVar));
members.push_back(parseVariableDeclaration());
expectToken(Token::SEMICOLON);
}
nodeFactory.markEndPosition();
@ -245,12 +248,20 @@ ASTPointer<StructDefinition> Parser::parseStructDefinition()
return nodeFactory.createNode<StructDefinition>(name, members);
}
ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration(bool _allowVar, bool _isPublic, bool _isStateVariable)
ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration(VarDeclParserOptions const& _options)
{
ASTNodeFactory nodeFactory(*this);
ASTPointer<TypeName> type = parseTypeName(_allowVar);
ASTPointer<TypeName> type = parseTypeName(_options.allowVar);
bool isIndexed = false;
if (_options.allowIndexed && m_scanner->getCurrentToken() == Token::INDEXED)
{
isIndexed = true;
m_scanner->next();
}
nodeFactory.markEndPosition();
return nodeFactory.createNode<VariableDeclaration>(type, expectIdentifierToken(), _isPublic, _isStateVariable);
return nodeFactory.createNode<VariableDeclaration>(type, expectIdentifierToken(),
_options.isPublic, _options.isStateVariable,
isIndexed);
}
ASTPointer<ModifierDefinition> Parser::parseModifierDefinition()
@ -269,17 +280,31 @@ ASTPointer<ModifierDefinition> Parser::parseModifierDefinition()
if (m_scanner->getCurrentToken() == Token::LPAREN)
parameters = parseParameterList();
else
{
// create an empty parameter list at a zero-length location
ASTNodeFactory nodeFactory(*this);
nodeFactory.setLocationEmpty();
parameters = nodeFactory.createNode<ParameterList>(vector<ASTPointer<VariableDeclaration>>());
}
parameters = createEmptyParameterList();
ASTPointer<Block> block = parseBlock();
nodeFactory.setEndPositionFromNode(block);
return nodeFactory.createNode<ModifierDefinition>(name, docstring, parameters, block);
}
ASTPointer<EventDefinition> Parser::parseEventDefinition()
{
ASTNodeFactory nodeFactory(*this);
ASTPointer<ASTString> docstring;
if (m_scanner->getCurrentCommentLiteral() != "")
docstring = make_shared<ASTString>(m_scanner->getCurrentCommentLiteral());
expectToken(Token::EVENT);
ASTPointer<ASTString> name(expectIdentifierToken());
ASTPointer<ParameterList> parameters;
if (m_scanner->getCurrentToken() == Token::LPAREN)
parameters = parseParameterList(true, true);
else
parameters = createEmptyParameterList();
nodeFactory.markEndPosition();
expectToken(Token::SEMICOLON);
return nodeFactory.createNode<EventDefinition>(name, docstring, parameters);
}
ASTPointer<ModifierInvocation> Parser::parseModifierInvocation()
{
ASTNodeFactory nodeFactory(*this);
@ -352,19 +377,20 @@ ASTPointer<Mapping> Parser::parseMapping()
return nodeFactory.createNode<Mapping>(keyType, valueType);
}
ASTPointer<ParameterList> Parser::parseParameterList(bool _allowEmpty)
ASTPointer<ParameterList> Parser::parseParameterList(bool _allowEmpty, bool _allowIndexed)
{
ASTNodeFactory nodeFactory(*this);
vector<ASTPointer<VariableDeclaration>> parameters;
VarDeclParserOptions options;
options.allowIndexed = _allowIndexed;
expectToken(Token::LPAREN);
if (!_allowEmpty || m_scanner->getCurrentToken() != Token::RPAREN)
{
bool const allowVar = false;
parameters.push_back(parseVariableDeclaration(allowVar));
parameters.push_back(parseVariableDeclaration(options));
while (m_scanner->getCurrentToken() != Token::RPAREN)
{
expectToken(Token::COMMA);
parameters.push_back(parseVariableDeclaration(allowVar));
parameters.push_back(parseVariableDeclaration(options));
}
}
nodeFactory.markEndPosition();
@ -506,8 +532,9 @@ ASTPointer<Statement> Parser::parseVarDefOrExprStmt()
ASTPointer<VariableDefinition> Parser::parseVariableDefinition()
{
ASTNodeFactory nodeFactory(*this);
bool const allowVar = true;
ASTPointer<VariableDeclaration> variable = parseVariableDeclaration(allowVar);
VarDeclParserOptions options;
options.allowVar = true;
ASTPointer<VariableDeclaration> variable = parseVariableDeclaration(options);
ASTPointer<Expression> value;
if (m_scanner->getCurrentToken() == Token::ASSIGN)
{
@ -736,6 +763,13 @@ ASTPointer<ASTString> Parser::getLiteralAndAdvance()
return identifier;
}
ASTPointer<ParameterList> Parser::createEmptyParameterList()
{
ASTNodeFactory nodeFactory(*this);
nodeFactory.setLocationEmpty();
return nodeFactory.createNode<ParameterList>(vector<ASTPointer<VariableDeclaration>>());
}
ParserError Parser::createParserError(string const& _description) const
{
return ParserError() << errinfo_sourceLocation(Location(getPosition(), getPosition(), getSourceName()))

16
libsolidity/Parser.h

@ -45,6 +45,14 @@ private:
/// End position of the current token
int getEndPosition() const;
struct VarDeclParserOptions {
VarDeclParserOptions() {}
bool allowVar = false;
bool isPublic = false;
bool isStateVariable = false;
bool allowIndexed = false;
};
///@{
///@name Parsing functions for the AST nodes
ASTPointer<ImportDirective> parseImportDirective();
@ -52,13 +60,14 @@ private:
ASTPointer<InheritanceSpecifier> parseInheritanceSpecifier();
ASTPointer<FunctionDefinition> parseFunctionDefinition(bool _isPublic, ASTString const* _contractName);
ASTPointer<StructDefinition> parseStructDefinition();
ASTPointer<VariableDeclaration> parseVariableDeclaration(bool _allowVar, bool _isPublic = false, bool _isStateVar = false);
ASTPointer<VariableDeclaration> parseVariableDeclaration(VarDeclParserOptions const& _options = VarDeclParserOptions());
ASTPointer<ModifierDefinition> parseModifierDefinition();
ASTPointer<EventDefinition> parseEventDefinition();
ASTPointer<ModifierInvocation> parseModifierInvocation();
ASTPointer<Identifier> parseIdentifier();
ASTPointer<TypeName> parseTypeName(bool _allowVar);
ASTPointer<Mapping> parseMapping();
ASTPointer<ParameterList> parseParameterList(bool _allowEmpty = true);
ASTPointer<ParameterList> parseParameterList(bool _allowEmpty = true, bool _allowIndexed = false);
ASTPointer<Block> parseBlock();
ASTPointer<Statement> parseStatement();
ASTPointer<IfStatement> parseIfStatement();
@ -88,6 +97,9 @@ private:
ASTPointer<ASTString> getLiteralAndAdvance();
///@}
/// Creates an empty ParameterList at the current location (used if parameters can be omitted).
ASTPointer<ParameterList> createEmptyParameterList();
/// Creates a @ref ParserError exception and annotates it with the current position and the
/// given @a _description.
ParserError createParserError(std::string const& _description) const;

2
libsolidity/Token.h

@ -153,7 +153,9 @@ namespace solidity
K(DEFAULT, "default", 0) \
K(DO, "do", 0) \
K(ELSE, "else", 0) \
K(EVENT, "event", 0) \
K(IS, "is", 0) \
K(INDEXED, "indexed", 0) \
K(FOR, "for", 0) \
K(FUNCTION, "function", 0) \
K(IF, "if", 0) \

69
libsolidity/Types.cpp

@ -494,12 +494,12 @@ MemberList const& ContractType::getMembers() const
{
for (ContractDefinition const* base: m_contract.getLinearizedBaseContracts())
for (ASTPointer<FunctionDefinition> const& function: base->getDefinedFunctions())
if (!function->isConstructor())
if (!function->isConstructor() && !function->getName().empty())
members.insert(make_pair(function->getName(), make_shared<FunctionType>(*function, true)));
}
else
for (auto const& it: m_contract.getInterfaceFunctions())
members[it.second.getName()] = it.second.getFunctionTypeShared();
members[it.second->getDeclaration().getName()] = it.second;
m_members.reset(new MemberList(members));
}
return *m_members;
@ -522,7 +522,7 @@ u256 ContractType::getFunctionIdentifier(string const& _functionName) const
{
auto interfaceFunctions = m_contract.getInterfaceFunctions();
for (auto const& it: m_contract.getInterfaceFunctions())
if (it.second.getName() == _functionName)
if (it.second->getDeclaration().getName() == _functionName)
return FixedHash<4>::Arith(it.first);
return Invalid256;
@ -589,12 +589,15 @@ u256 StructType::getStorageOffsetOfMember(string const& _name) const
}
FunctionType::FunctionType(FunctionDefinition const& _function, bool _isInternal):
m_location(_isInternal ? Location::INTERNAL : Location::EXTERNAL)
m_location(_isInternal ? Location::INTERNAL : Location::EXTERNAL),
m_isConstant(_function.isDeclaredConst()),
m_declaration(&_function)
{
TypePointers params;
vector<string> paramNames;
TypePointers retParams;
vector<string> retParamNames;
params.reserve(_function.getParameters().size());
paramNames.reserve(_function.getParameters().size());
for (ASTPointer<VariableDeclaration> const& var: _function.getParameters())
@ -616,7 +619,7 @@ FunctionType::FunctionType(FunctionDefinition const& _function, bool _isInternal
}
FunctionType::FunctionType(VariableDeclaration const& _varDecl):
m_location(Location::EXTERNAL)
m_location(Location::EXTERNAL), m_isConstant(true), m_declaration(&_varDecl)
{
TypePointers params({});
vector<string> paramNames({});
@ -630,6 +633,22 @@ FunctionType::FunctionType(VariableDeclaration const& _varDecl):
swap(retParamNames, m_returnParameterNames);
}
FunctionType::FunctionType(const EventDefinition& _event):
m_location(Location::EVENT), m_declaration(&_event)
{
TypePointers params;
vector<string> paramNames;
params.reserve(_event.getParameters().size());
paramNames.reserve(_event.getParameters().size());
for (ASTPointer<VariableDeclaration> const& var: _event.getParameters())
{
paramNames.push_back(var->getName());
params.push_back(var->getType());
}
swap(params, m_parameterTypes);
swap(paramNames, m_parameterNames);
}
bool FunctionType::operator==(Type const& _other) const
{
if (_other.getCategory() != getCategory())
@ -638,6 +657,9 @@ bool FunctionType::operator==(Type const& _other) const
if (m_location != other.m_location)
return false;
if (m_isConstant != other.isConstant())
return false;
if (m_parameterTypes.size() != other.m_parameterTypes.size() ||
m_returnParameterTypes.size() != other.m_returnParameterTypes.size())
return false;
@ -711,7 +733,13 @@ MemberList const& FunctionType::getMembers() const
string FunctionType::getCanonicalSignature(std::string const& _name) const
{
string ret = _name + "(";
std::string funcName = _name;
if (_name == "")
{
solAssert(m_declaration != nullptr, "Function type without name needs a declaration");
funcName = m_declaration->getName();
}
string ret = funcName + "(";
for (auto it = m_parameterTypes.cbegin(); it != m_parameterTypes.cend(); ++it)
ret += (*it)->toString() + (it + 1 == m_parameterTypes.cend() ? "" : ",");
@ -734,6 +762,33 @@ TypePointer FunctionType::copyAndSetGasOrValue(bool _setGas, bool _setValue) con
m_gasSet || _setGas, m_valueSet || _setValue);
}
vector<string> const FunctionType::getParameterTypeNames() const
{
vector<string> names;
for (TypePointer const& t: m_parameterTypes)
names.push_back(t->toString());
return names;
}
vector<string> const FunctionType::getReturnParameterTypeNames() const
{
vector<string> names;
for (TypePointer const& t: m_returnParameterTypes)
names.push_back(t->toString());
return names;
}
ASTPointer<ASTString> FunctionType::getDocumentation() const
{
auto function = dynamic_cast<Documented const*>(m_declaration);
if (function)
return function->getDocumentation();
return ASTPointer<ASTString>();
}
bool MappingType::operator==(Type const& _other) const
{
if (_other.getCategory() != getCategory())
@ -769,7 +824,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<FunctionDefinition> const& f: contract.getDefinedFunctions())
if (!f->isConstructor())
if (!f->isConstructor() && !f->getName().empty())
members[f->getName()] = make_shared<FunctionType>(*f);
}
m_members.reset(new MemberList(members));

28
libsolidity/Types.h

@ -41,6 +41,7 @@ namespace solidity
class Type; // forward
class FunctionType; // forward
using TypePointer = std::shared_ptr<Type const>;
using FunctionTypePointer = std::shared_ptr<FunctionType const>;
using TypePointers = std::vector<TypePointer>;
/**
@ -295,7 +296,7 @@ public:
/// Returns the function type of the constructor. Note that the location part of the function type
/// is not used, as this type cannot be the type of a variable or expression.
std::shared_ptr<FunctionType const> const& getConstructorType() const;
FunctionTypePointer const& getConstructorType() const;
/// @returns the identifier of the function with the given name or Invalid256 if such a name does
/// not exist.
@ -307,7 +308,7 @@ private:
/// members.
bool m_super;
/// Type of the constructor, @see getConstructorType. Lazily initialized.
mutable std::shared_ptr<FunctionType const> m_constructorType;
mutable FunctionTypePointer m_constructorType;
/// List of member types, will be lazy-initialized because of recursive references.
mutable std::unique_ptr<MemberList> m_members;
};
@ -349,16 +350,18 @@ public:
/// INTERNAL: jump tag, EXTERNAL: contract address + function identifier,
/// BARE: contract address (non-abi contract call)
/// OTHERS: special virtual function, nothing on the stack
/// @todo This documentation is outdated, and Location should rather be named "Type"
enum class Location { INTERNAL, EXTERNAL, CREATION, SEND,
SHA3, SUICIDE,
ECRECOVER, SHA256, RIPEMD160,
LOG0, LOG1, LOG2, LOG3, LOG4,
LOG0, LOG1, LOG2, LOG3, LOG4, EVENT,
SET_GAS, SET_VALUE, BLOCKHASH,
BARE };
virtual Category getCategory() const override { return Category::FUNCTION; }
explicit FunctionType(FunctionDefinition const& _function, bool _isInternal = true);
explicit FunctionType(VariableDeclaration const& _varDecl);
explicit FunctionType(EventDefinition const& _event);
FunctionType(strings const& _parameterTypes, strings const& _returnParameterTypes,
Location _location = Location::INTERNAL):
FunctionType(parseElementaryTypeVector(_parameterTypes), parseElementaryTypeVector(_returnParameterTypes),
@ -371,8 +374,10 @@ public:
TypePointers const& getParameterTypes() const { return m_parameterTypes; }
std::vector<std::string> const& getParameterNames() const { return m_parameterNames; }
std::vector<std::string> const getParameterTypeNames() const;
TypePointers const& getReturnParameterTypes() const { return m_returnParameterTypes; }
std::vector<std::string> const& getReturnParameterNames() const { return m_returnParameterNames; }
std::vector<std::string> const getReturnParameterTypeNames() const;
virtual bool operator==(Type const& _other) const override;
virtual std::string toString() const override;
@ -383,7 +388,20 @@ public:
virtual MemberList const& getMembers() const override;
Location const& getLocation() const { return m_location; }
std::string getCanonicalSignature(std::string const& _name) const;
/// @returns the canonical signature of this function type given the function name
/// If @a _name is not provided (empty string) then the @c m_declaration member of the
/// function type is used
std::string getCanonicalSignature(std::string const& _name = "") const;
Declaration const& getDeclaration() const
{
solAssert(m_declaration, "Requested declaration from a FunctionType that has none");
return *m_declaration;
}
bool hasDeclaration() const { return !!m_declaration; }
bool isConstant() const { return m_isConstant; }
/// @return A shared pointer of an ASTString.
/// Can contain a nullptr in which case indicates absence of documentation
ASTPointer<ASTString> getDocumentation() const;
bool gasSet() const { return m_gasSet; }
bool valueSet() const { return m_valueSet; }
@ -402,7 +420,9 @@ private:
Location const m_location;
bool const m_gasSet = false; ///< true iff the gas value to be used is on the stack
bool const m_valueSet = false; ///< true iff the value to be sent is on the stack
bool m_isConstant;
mutable std::unique_ptr<MemberList> m_members;
Declaration const* m_declaration = nullptr;
};
/**

24
libweb3jsonrpc/WebThreeStubServerBase.cpp

@ -77,7 +77,7 @@ static Json::Value toJson(dev::eth::LocalisedLogEntry const& _e)
res["data"] = jsFromBinary(_e.data);
res["address"] = toJS(_e.address);
for (auto const& t: _e.topics)
res["topics"].append(toJS(t));
res["topic"].append(toJS(t));
res["number"] = _e.number;
return res;
}
@ -123,10 +123,10 @@ static dev::eth::LogFilter toLogFilter(Json::Value const& _json) // commented to
else if (_json["address"].isString())
filter.address(jsToAddress(_json["address"].asString()));
}
if (!_json["topics"].empty() && _json["topics"].isArray())
if (!_json["topic"].empty() && _json["topic"].isArray())
{
unsigned i = 0;
for (auto t: _json["topics"])
for (auto t: _json["topic"])
{
if (t.isArray())
for (auto tt: t)
@ -173,9 +173,9 @@ static shh::Envelope toSealed(Json::Value const& _json, shh::Message const& _m,
return _m.seal(_from, bt, ttl, workToProve);
}
static pair<shh::TopicMask, Public> toWatch(Json::Value const& _json)
static pair<shh::FullTopic, Public> toWatch(Json::Value const& _json)
{
shh::BuildTopicMask bt;
shh::BuildTopic bt;
Public to;
if (_json["to"].isString())
@ -190,7 +190,7 @@ static pair<shh::TopicMask, Public> toWatch(Json::Value const& _json)
if (i.isString())
bt.shift(jsToBytes(i.asString()));
}
return make_pair(bt.toTopicMask(), to);
return make_pair(bt, to);
}
static Json::Value toJson(h256 const& _h, shh::Envelope const& _e, shh::Message const& _m)
@ -201,8 +201,8 @@ static Json::Value toJson(h256 const& _h, shh::Envelope const& _e, shh::Message
res["sent"] = (int)_e.sent();
res["ttl"] = (int)_e.ttl();
res["workProved"] = (int)_e.workProved();
for (auto const& t: _e.topics())
res["topics"].append(toJS(t));
for (auto const& t: _e.topic())
res["topic"].append(toJS(t));
res["payload"] = toJS(_m.payload());
res["from"] = toJS(_m.from());
res["to"] = toJS(_m.to());
@ -576,12 +576,12 @@ Json::Value WebThreeStubServerBase::shh_changed(int const& _id)
if (pub)
{
cwarn << "Silently decrypting message from identity" << pub.abridged() << ": User validation hook goes here.";
m = e.open(m_ids[pub]);
if (!m)
continue;
m = e.open(face()->fullTopic(_id), m_ids[pub]);
}
else
m = e.open();
m = e.open(face()->fullTopic(_id));
if (!m)
continue;
ret.append(toJson(h, e, m));
}

24
libwhisper/Common.cpp

@ -28,12 +28,26 @@ using namespace dev;
using namespace dev::p2p;
using namespace dev::shh;
Topic BuildTopic::toTopic() const
CollapsedTopicPart dev::shh::collapse(FullTopicPart const& _p)
{
Topic ret;
return CollapsedTopicPart(sha3(_p));
}
CollapsedTopic dev::shh::collapse(FullTopic const& _fullTopic)
{
CollapsedTopic ret;
ret.reserve(_fullTopic.size());
for (auto const& ft: _fullTopic)
ret.push_back(collapse(ft));
return ret;
}
CollapsedTopic BuildTopic::toTopic() const
{
CollapsedTopic ret;
ret.reserve(m_parts.size());
for (auto const& h: m_parts)
ret.push_back(TopicPart(h));
ret.push_back(collapse(h));
return ret;
}
@ -56,7 +70,7 @@ bool TopicFilter::matches(Envelope const& _e) const
{
for (unsigned i = 0; i < t.size(); ++i)
{
for (auto et: _e.topics())
for (auto et: _e.topic())
if (((t[i].first ^ et) & t[i].second) == 0)
goto NEXT_TOPICPART;
// failed to match topicmask against any topics: move on to next mask
@ -75,7 +89,7 @@ TopicMask BuildTopicMask::toTopicMask() const
TopicMask ret;
ret.reserve(m_parts.size());
for (auto const& h: m_parts)
ret.push_back(make_pair(TopicPart(h), ~TopicPart()));
ret.push_back(make_pair(collapse(h), ~CollapsedTopicPart()));
return ret;
}

20
libwhisper/Common.h

@ -59,9 +59,14 @@ enum WhisperPacket
PacketCount
};
using TopicPart = FixedHash<4>;
using CollapsedTopicPart = FixedHash<4>;
using FullTopicPart = h256;
using Topic = std::vector<TopicPart>;
using CollapsedTopic = std::vector<CollapsedTopicPart>;
using FullTopic = h256s;
CollapsedTopicPart collapse(FullTopicPart const& _fullTopicPart);
CollapsedTopic collapse(FullTopic const& _fullTopic);
class BuildTopic
{
@ -74,8 +79,10 @@ public:
BuildTopic& shiftRaw(h256 const& _part) { m_parts.push_back(_part); return *this; }
operator Topic() const { return toTopic(); }
Topic toTopic() const;
operator CollapsedTopic() const { return toTopic(); }
operator FullTopic() const { return toFullTopic(); }
CollapsedTopic toTopic() const;
FullTopic toFullTopic() const { return m_parts; }
protected:
BuildTopic& shiftBytes(bytes const& _b);
@ -83,13 +90,14 @@ protected:
h256s m_parts;
};
using TopicMask = std::vector<std::pair<TopicPart, TopicPart>>;
using TopicMask = std::vector<std::pair<CollapsedTopicPart, CollapsedTopicPart>>;
using TopicMasks = std::vector<TopicMask>;
class TopicFilter
{
public:
TopicFilter() {}
TopicFilter(FullTopic const& _m) { m_topicMasks.push_back(TopicMask()); for (auto const& h: _m) m_topicMasks.back().push_back(std::make_pair(collapse(h), h ? ~CollapsedTopicPart() : CollapsedTopicPart())); }
TopicFilter(TopicMask const& _m): m_topicMasks(1, _m) {}
TopicFilter(TopicMasks const& _m): m_topicMasks(_m) {}
TopicFilter(RLP const& _r)//: m_topicMasks(_r.toVector<std::vector<>>())
@ -123,7 +131,9 @@ public:
template <class T> BuildTopicMask& operator()(T const& _t) { shift(_t); return *this; }
operator TopicMask() const { return toTopicMask(); }
operator FullTopic() const { return toFullTopic(); }
TopicMask toTopicMask() const;
FullTopic toFullTopic() const { return m_parts; }
};
}

3
libwhisper/Interface.cpp

@ -34,7 +34,6 @@ using namespace dev::shh;
#endif
#define clogS(X) dev::LogOutputStream<X, true>(false) << "| " << std::setw(2) << session()->socketId() << "] "
unsigned Interface::installWatch(TopicMask const& _mask)
Interface::~Interface()
{
return installWatch(TopicFilter(_mask));
}

20
libwhisper/Interface.h

@ -47,8 +47,9 @@ class Watch;
struct InstalledFilter
{
InstalledFilter(TopicFilter const& _f): filter(_f) {}
InstalledFilter(FullTopic const& _f): full(_f), filter(_f) {}
FullTopic full;
TopicFilter filter;
unsigned refCount = 1;
};
@ -65,12 +66,12 @@ struct ClientWatch
class Interface
{
public:
virtual ~Interface() {}
virtual ~Interface();
virtual void inject(Envelope const& _m, WhisperPeer* _from = nullptr) = 0;
unsigned installWatch(TopicMask const& _mask);
virtual unsigned installWatch(TopicFilter const& _filter) = 0;
virtual FullTopic const& fullTopic(unsigned _id) const = 0;
virtual unsigned installWatch(FullTopic const& _mask) = 0;
virtual unsigned installWatchOnId(h256 _filterId) = 0;
virtual void uninstallWatch(unsigned _watchId) = 0;
virtual h256s peekWatch(unsigned _watchId) const = 0;
@ -79,10 +80,10 @@ public:
virtual Envelope envelope(h256 _m) const = 0;
void post(bytes const& _payload, Topic _topic, unsigned _ttl = 50, unsigned _workToProve = 50) { inject(Message(_payload).seal(_topic, _ttl, _workToProve)); }
void post(Public _to, bytes const& _payload, Topic _topic, unsigned _ttl = 50, unsigned _workToProve = 50) { inject(Message(_payload).sealTo(_to, _topic, _ttl, _workToProve)); }
void post(Secret _from, bytes const& _payload, Topic _topic, unsigned _ttl = 50, unsigned _workToProve = 50) { inject(Message(_payload).seal(_from, _topic, _ttl, _workToProve)); }
void post(Secret _from, Public _to, bytes const& _payload, Topic _topic, unsigned _ttl = 50, unsigned _workToProve = 50) { inject(Message(_payload).sealTo(_from, _to, _topic, _ttl, _workToProve)); }
void post(bytes const& _payload, FullTopic _topic, unsigned _ttl = 50, unsigned _workToProve = 50) { inject(Message(_payload).seal(_topic, _ttl, _workToProve)); }
void post(Public _to, bytes const& _payload, FullTopic _topic, unsigned _ttl = 50, unsigned _workToProve = 50) { inject(Message(_payload).sealTo(_to, _topic, _ttl, _workToProve)); }
void post(Secret _from, bytes const& _payload, FullTopic _topic, unsigned _ttl = 50, unsigned _workToProve = 50) { inject(Message(_payload).seal(_from, _topic, _ttl, _workToProve)); }
void post(Secret _from, Public _to, bytes const& _payload, FullTopic _topic, unsigned _ttl = 50, unsigned _workToProve = 50) { inject(Message(_payload).sealTo(_from, _to, _topic, _ttl, _workToProve)); }
};
struct WatshhChannel: public dev::LogChannel { static const char* name() { return "shh"; } static const int verbosity = 1; };
@ -104,8 +105,7 @@ class Watch: public boost::noncopyable
public:
Watch() {}
Watch(Interface& _c, TopicMask const& _f): m_c(&_c), m_id(_c.installWatch(_f)) {}
Watch(Interface& _c, TopicFilter const& _tf): m_c(&_c), m_id(_c.installWatch(_tf)) {}
Watch(Interface& _c, FullTopic const& _f): m_c(&_c), m_id(_c.installWatch(_f)) {}
~Watch() { if (m_c) m_c->uninstallWatch(m_id); }
h256s check() { return m_c ? m_c->checkWatch(m_id) : h256s(); }

64
libwhisper/Message.cpp

@ -26,7 +26,7 @@ using namespace dev;
using namespace dev::p2p;
using namespace dev::shh;
Message::Message(Envelope const& _e, Secret const& _s)
Message::Message(Envelope const& _e, FullTopic const& _fk, Secret const& _s)
{
try
{
@ -34,7 +34,38 @@ Message::Message(Envelope const& _e, Secret const& _s)
if (_s)
if (!decrypt(_s, &(_e.data()), b))
return;
if (populate(_s ? b : _e.data()))
else{}
else
{
// public - need to get the key through combining with the topic/topicIndex we know.
unsigned topicIndex = 0;
Secret topicSecret;
// determine topicSecret/topicIndex from knowledge of the collapsed topics (which give the order) and our full-size filter topic.
CollapsedTopic knownTopic = collapse(_fk);
for (unsigned ti = 0; ti < _fk.size() && !topicSecret; ++ti)
for (unsigned i = 0; i < _e.topic().size(); ++i)
if (_e.topic()[i] == knownTopic[ti])
{
topicSecret = _fk[ti];
topicIndex = i;
break;
}
if (_e.data().size() < _e.topic().size() * 32)
return;
// get key from decrypted topic key: just xor
h256 tk = h256(bytesConstRef(&(_e.data())).cropped(32 * topicIndex, 32));
bytesConstRef cipherText = bytesConstRef(&(_e.data())).cropped(32 * _e.topic().size());
cnote << "Decrypting(" << topicIndex << "): " << topicSecret << tk << (topicSecret ^ tk) << toHex(cipherText);
if (!decryptSym(topicSecret ^ tk, cipherText, b))
return;
cnote << "Got: " << toHex(b);
}
if (populate(b))
if (_s)
m_to = KeyPair(_s).pub();
}
catch (...) // Invalid secret? TODO: replace ... with InvalidSecret
@ -63,9 +94,10 @@ bool Message::populate(bytes const& _data)
return true;
}
Envelope Message::seal(Secret _from, Topic const& _topic, unsigned _ttl, unsigned _workToProve) const
Envelope Message::seal(Secret _from, FullTopic const& _fullTopic, unsigned _ttl, unsigned _workToProve) const
{
Envelope ret(time(0) + _ttl, _ttl, _topic);
CollapsedTopic topic = collapse(_fullTopic);
Envelope ret(time(0) + _ttl, _ttl, topic);
bytes input(1 + m_payload.size());
input[0] = 0;
@ -83,7 +115,25 @@ Envelope Message::seal(Secret _from, Topic const& _topic, unsigned _ttl, unsigne
if (m_to)
encrypt(m_to, &input, ret.m_data);
else
swap(ret.m_data, input);
{
// create the shared secret and encrypt
Secret s = Secret::random();
for (h256 const& t: _fullTopic)
ret.m_data += (t ^ s).asBytes();
bytes d;
encryptSym(s, &input, d);
ret.m_data += d;
for (unsigned i = 0; i < _fullTopic.size(); ++i)
{
bytes b;
h256 tk = h256(bytesConstRef(&(ret.m_data)).cropped(32 * i, 32));
bytesConstRef cipherText = bytesConstRef(&(ret.m_data)).cropped(32 * ret.topic().size());
cnote << "Test decrypting(" << i << "): " << _fullTopic[i] << tk << (_fullTopic[i] ^ tk) << toHex(cipherText);
assert(decryptSym(_fullTopic[i] ^ tk, cipherText, b));
cnote << "Got: " << toHex(b);
}
}
ret.proveWork(_workToProve);
return ret;
@ -98,9 +148,9 @@ Envelope::Envelope(RLP const& _m)
m_nonce = _m[4].toInt<u256>();
}
Message Envelope::open(Secret const& _s) const
Message Envelope::open(FullTopic const& _ft, Secret const& _s) const
{
return Message(*this, _s);
return Message(*this, _ft, _s);
}
unsigned Envelope::workProved() const

28
libwhisper/Message.h

@ -39,6 +39,16 @@ namespace shh
class Message;
static const unsigned Undefined = (unsigned)-1;
struct FilterKey
{
FilterKey() {}
FilterKey(unsigned _tI, Secret const& _k): topicIndex(_tI), key(_k) {}
unsigned topicIndex = Undefined;
Secret key;
};
enum IncludeNonce
{
WithoutNonce = 0,
@ -61,22 +71,22 @@ public:
unsigned sent() const { return m_expiry - m_ttl; }
unsigned expiry() const { return m_expiry; }
unsigned ttl() const { return m_ttl; }
Topic const& topics() const { return m_topic; }
CollapsedTopic const& topic() const { return m_topic; }
bytes const& data() const { return m_data; }
Message open(Secret const& _s = Secret()) const;
Message open(FullTopic const& _ft, Secret const& _s = Secret()) const;
unsigned workProved() const;
void proveWork(unsigned _ms);
private:
Envelope(unsigned _exp, unsigned _ttl, Topic const& _topic): m_expiry(_exp), m_ttl(_ttl), m_topic(_topic) {}
Envelope(unsigned _exp, unsigned _ttl, CollapsedTopic const& _topic): m_expiry(_exp), m_ttl(_ttl), m_topic(_topic) {}
unsigned m_expiry = 0;
unsigned m_ttl = 0;
u256 m_nonce;
Topic m_topic;
CollapsedTopic m_topic;
bytes m_data;
};
@ -91,7 +101,7 @@ class Message
{
public:
Message() {}
Message(Envelope const& _e, Secret const& _s = Secret());
Message(Envelope const& _e, FullTopic const& _ft, Secret const& _s = Secret());
Message(bytes const& _payload): m_payload(_payload) {}
Message(bytesConstRef _payload): m_payload(_payload.toBytes()) {}
Message(bytes&& _payload) { std::swap(_payload, m_payload); }
@ -108,11 +118,11 @@ public:
operator bool() const { return !!m_payload.size() || m_from || m_to; }
/// Turn this message into a ditributable Envelope.
Envelope seal(Secret _from, Topic const& _topic, unsigned _workToProve = 50, unsigned _ttl = 50) const;
Envelope seal(Secret _from, FullTopic const& _topic, unsigned _workToProve = 50, unsigned _ttl = 50) const;
// Overloads for skipping _from or specifying _to.
Envelope seal(Topic const& _topic, unsigned _ttl = 50, unsigned _workToProve = 50) const { return seal(Secret(), _topic, _workToProve, _ttl); }
Envelope sealTo(Public _to, Topic const& _topic, unsigned _workToProve = 50, unsigned _ttl = 50) { m_to = _to; return seal(Secret(), _topic, _workToProve, _ttl); }
Envelope sealTo(Secret _from, Public _to, Topic const& _topic, unsigned _workToProve = 50, unsigned _ttl = 50) { m_to = _to; return seal(_from, _topic, _workToProve, _ttl); }
Envelope seal(FullTopic const& _topic, unsigned _ttl = 50, unsigned _workToProve = 50) const { return seal(Secret(), _topic, _workToProve, _ttl); }
Envelope sealTo(Public _to, FullTopic const& _topic, unsigned _workToProve = 50, unsigned _ttl = 50) { m_to = _to; return seal(Secret(), _topic, _workToProve, _ttl); }
Envelope sealTo(Secret _from, Public _to, FullTopic const& _topic, unsigned _workToProve = 50, unsigned _ttl = 50) { m_to = _to; return seal(_from, _topic, _workToProve, _ttl); }
private:
bool populate(bytes const& _data);

11
libwhisper/WhisperHost.cpp

@ -49,14 +49,14 @@ void WhisperHost::streamMessage(h256 _m, RLPStream& _s) const
{
UpgradeGuard ll(l);
auto const& m = m_messages.at(_m);
cnote << "streamRLP: " << m.expiry() << m.ttl() << m.topics() << toHex(m.data());
cnote << "streamRLP: " << m.expiry() << m.ttl() << m.topic() << toHex(m.data());
m.streamRLP(_s);
}
}
void WhisperHost::inject(Envelope const& _m, WhisperPeer* _p)
{
cnote << "inject: " << _m.expiry() << _m.ttl() << _m.topics() << toHex(_m.data());
cnote << this << ": inject: " << _m.expiry() << _m.ttl() << _m.topic() << toHex(_m.data());
if (_m.expiry() <= time(0))
return;
@ -104,14 +104,15 @@ unsigned WhisperHost::installWatchOnId(h256 _h)
return ret;
}
unsigned WhisperHost::installWatch(shh::TopicFilter const& _f)
unsigned WhisperHost::installWatch(shh::FullTopic const& _ft)
{
Guard l(m_filterLock);
h256 h = _f.sha3();
InstalledFilter f(_ft);
h256 h = f.filter.sha3();
if (!m_filters.count(h))
m_filters.insert(make_pair(h, _f));
m_filters.insert(make_pair(h, f));
return installWatchOnId(h);
}

8
libwhisper/WhisperHost.h

@ -39,6 +39,8 @@ namespace dev
namespace shh
{
static const FullTopic EmptyFullTopic;
class WhisperHost: public HostCapability<WhisperPeer>, public Interface, public Worker
{
friend class WhisperPeer;
@ -47,12 +49,12 @@ public:
WhisperHost();
virtual ~WhisperHost();
unsigned protocolVersion() const { return 1; }
unsigned protocolVersion() const { return 2; }
virtual void inject(Envelope const& _e, WhisperPeer* _from = nullptr) override;
using Interface::installWatch;
virtual unsigned installWatch(TopicFilter const& _filter) override;
virtual FullTopic const& fullTopic(unsigned _id) const { try { return m_filters.at(m_watches.at(_id).id).full; } catch (...) { return EmptyFullTopic; } }
virtual unsigned installWatch(FullTopic const& _filter) override;
virtual unsigned installWatchOnId(h256 _filterId) override;
virtual void uninstallWatch(unsigned _watchId) override;
virtual h256s peekWatch(unsigned _watchId) const override { dev::Guard l(m_filterLock); try { return m_watches.at(_watchId).changes; } catch (...) { return h256s(); } }

2
libwhisper/WhisperPeer.cpp

@ -106,7 +106,7 @@ void WhisperPeer::sendMessages()
}
}
void WhisperPeer::noteNewMessage(h256 _h, Message const& _m)
void WhisperPeer::noteNewMessage(h256 _h, Envelope const& _m)
{
Guard l(x_unseen);
m_unseen.insert(make_pair(rating(_m), _h));

4
libwhisper/WhisperPeer.h

@ -63,8 +63,8 @@ private:
void sendMessages();
unsigned rating(Message const&) const { return 0; } // TODO
void noteNewMessage(h256 _h, Message const& _m);
unsigned rating(Envelope const&) const { return 0; } // TODO
void noteNewMessage(h256 _h, Envelope const& _m);
mutable dev::Mutex x_unseen;
std::multimap<unsigned, h256> m_unseen; ///< Rated according to what they want.

10
mix/AppContext.cpp

@ -23,9 +23,11 @@
*/
#include <QMessageBox>
#include <QClipboard>
#include <QQmlComponent>
#include <QQmlContext>
#include <QQmlApplicationEngine>
#include <QQuickWindow>
#include "CodeModel.h"
#include "FileIo.h"
#include "ClientModel.h"
@ -79,6 +81,8 @@ void AppContext::load()
qmlRegisterType<CodeEditorExtensionManager>("CodeEditorExtensionManager", 1, 0, "CodeEditorExtensionManager");
qmlRegisterType<HttpServer>("HttpServer", 1, 0, "HttpServer");
m_applicationEngine->load(QUrl("qrc:/qml/main.qml"));
QQuickWindow *window = qobject_cast<QQuickWindow *>(m_applicationEngine->rootObjects().at(0));
window->setIcon(QIcon(":/res/mix_256x256x32.png"));
appLoaded();
}
@ -99,3 +103,9 @@ void AppContext::displayMessageDialog(QString _title, QString _message)
dialogWin->findChild<QObject*>("messageContent", Qt::FindChildrenRecursively)->setProperty("text", _message);
QMetaObject::invokeMethod(dialogWin, "open");
}
void AppContext::toClipboard(QString _text)
{
QClipboard *clipboard = QApplication::clipboard();
clipboard->setText(_text);
}

2
mix/AppContext.h

@ -62,6 +62,8 @@ public:
ClientModel* clientModel() { return m_clientModel.get(); }
/// Display an alert message.
void displayMessageDialog(QString _title, QString _message);
/// Copy text to clipboard
Q_INVOKABLE void toClipboard(QString _text);
signals:
/// Triggered once components have been loaded

7
mix/AssemblyDebuggerControl.cpp

@ -29,7 +29,6 @@ using namespace dev::mix;
AssemblyDebuggerControl::AssemblyDebuggerControl(AppContext* _context):
Extension(_context, ExtensionDisplayBehavior::RightView)
{
connect(_context->clientModel(), &ClientModel::showDebuggerWindow, this, &AssemblyDebuggerControl::showDebugger, Qt::QueuedConnection);
}
QString AssemblyDebuggerControl::contentUrl() const
@ -45,9 +44,3 @@ QString AssemblyDebuggerControl::title() const
void AssemblyDebuggerControl::start() const
{
}
void AssemblyDebuggerControl::showDebugger()
{
QObject* debugPanel = m_view->findChild<QObject*>("debugPanel", Qt::FindChildrenRecursively);
QMetaObject::invokeMethod(debugPanel, "update", Q_ARG(QVariant, true));
}

4
mix/AssemblyDebuggerControl.h

@ -42,10 +42,6 @@ public:
void start() const override;
QString title() const override;
QString contentUrl() const override;
private slots:
/// Update UI with machine states result. Displayed in the right side tab.
void showDebugger();
};
}

7
mix/CMakeLists.txt

@ -13,7 +13,7 @@ aux_source_directory(. SRC_LIST)
include_directories(..)
find_package (Qt5WebEngine QUIET)
qt5_add_resources(UI_RESOURCES qml.qrc)
qt5_add_resources(UI_RESOURCES res.qrc)
file(GLOB HEADERS "*.h")
@ -62,6 +62,7 @@ eth_install_executable(${EXECUTABLE}
QMLDIR ${CMAKE_CURRENT_SOURCE_DIR}/qml
)
#add qml files to project tree in Qt creator
#add qml asnd stdc files to project tree in Qt creator
file(GLOB_RECURSE QMLFILES "qml/*.*")
add_custom_target(dummy SOURCES ${QMLFILES})
file(GLOB_RECURSE SOLFILES "stdc/*.*")
add_custom_target(dummy SOURCES ${QMLFILES} ${SOLFILES})

278
mix/ClientModel.cpp

@ -27,6 +27,7 @@
#include <libethereum/Transaction.h>
#include "AppContext.h"
#include "DebuggingStateWrapper.h"
#include "Exceptions.h"
#include "QContractDefinition.h"
#include "QVariableDeclaration.h"
#include "ContractCallDataEncoder.h"
@ -60,8 +61,9 @@ private:
QString m_response;
};
ClientModel::ClientModel(AppContext* _context):
m_context(_context), m_running(false), m_rpcConnector(new RpcConnector())
m_context(_context), m_running(false), m_rpcConnector(new RpcConnector()), m_contractAddress(Address())
{
qRegisterMetaType<QBigInt*>("QBigInt*");
qRegisterMetaType<QIntType*>("QIntType*");
@ -74,12 +76,17 @@ ClientModel::ClientModel(AppContext* _context):
qRegisterMetaType<QList<QVariableDefinition*>>("QList<QVariableDefinition*>");
qRegisterMetaType<QList<QVariableDeclaration*>>("QList<QVariableDeclaration*>");
qRegisterMetaType<QVariableDeclaration*>("QVariableDeclaration*");
qRegisterMetaType<AssemblyDebuggerData>("AssemblyDebuggerData");
qRegisterMetaType<QMachineState*>("QMachineState");
qRegisterMetaType<QInstruction*>("QInstruction");
qRegisterMetaType<QCode*>("QCode");
qRegisterMetaType<QCallData*>("QCallData");
qRegisterMetaType<TransactionLogEntry*>("TransactionLogEntry");
connect(this, &ClientModel::dataAvailable, this, &ClientModel::showDebugger, Qt::QueuedConnection);
connect(this, &ClientModel::runComplete, this, &ClientModel::showDebugger, Qt::QueuedConnection);
m_client.reset(new MixClient());
m_web3Server.reset(new Web3Server(*m_rpcConnector.get(), std::vector<dev::KeyPair> { m_client->userAccount() }, m_client.get()));
connect(m_web3Server.get(), &Web3Server::newTransaction, this, &ClientModel::onNewTransaction, Qt::DirectConnection);
_context->appEngine()->rootContext()->setContextProperty("clientModel", this);
}
@ -94,10 +101,15 @@ QString ClientModel::apiCall(QString const& _message)
return m_rpcConnector->response();
}
void ClientModel::mine()
{
m_client->mine();
newBlock();
}
QString ClientModel::contractAddress() const
{
QString address = QString::fromStdString(dev::toJS(m_client->lastContractAddress()));
return address;
return QString::fromStdString(dev::toJS(m_contractAddress));
}
void ClientModel::debugDeployment()
@ -105,13 +117,12 @@ void ClientModel::debugDeployment()
executeSequence(std::vector<TransactionSettings>(), 10000000 * ether);
}
void ClientModel::debugState(QVariantMap _state)
void ClientModel::setupState(QVariantMap _state)
{
u256 balance = (qvariant_cast<QEther*>(_state.value("balance")))->toU256Wei();
QVariantList transactions = _state.value("transactions").toList();
std::vector<TransactionSettings> transactionSequence;
TransactionSettings constructorTr;
for (auto const& t: transactions)
{
QVariantMap transaction = t.toMap();
@ -119,8 +130,21 @@ void ClientModel::debugState(QVariantMap _state)
u256 gas = (qvariant_cast<QEther*>(transaction.value("gas")))->toU256Wei();
u256 value = (qvariant_cast<QEther*>(transaction.value("value")))->toU256Wei();
u256 gasPrice = (qvariant_cast<QEther*>(transaction.value("gasPrice")))->toU256Wei();
TransactionSettings transactionSettings(functionId, value, gas, gasPrice);
bool isStdContract = (transaction.value("stdContract").toBool());
if (isStdContract)
{
TransactionSettings transactionSettings(functionId, transaction.value("url").toString());
transactionSettings.gasPrice = 10000000000000;
transactionSettings.gas = 125000;
transactionSettings.value = 100;
transactionSequence.push_back(transactionSettings);
}
else
{
QVariantList qParams = transaction.value("qType").toList();
TransactionSettings transactionSettings(functionId, value, gas, gasPrice);
for (QVariant const& variant: qParams)
{
QVariableDefinition* param = qvariant_cast<QVariableDefinition*>(variant);
@ -128,23 +152,24 @@ void ClientModel::debugState(QVariantMap _state)
}
if (transaction.value("executeConstructor").toBool())
constructorTr = transactionSettings;
else
transactionSettings.functionId.clear();
transactionSequence.push_back(transactionSettings);
}
executeSequence(transactionSequence, balance, constructorTr);
}
executeSequence(transactionSequence, balance);
}
void ClientModel::executeSequence(std::vector<TransactionSettings> const& _sequence, u256 _balance, TransactionSettings const& ctrTransaction)
void ClientModel::executeSequence(std::vector<TransactionSettings> const& _sequence, u256 _balance)
{
if (m_running)
throw (std::logic_error("debugging already running"));
auto compilerRes = m_context->codeModel()->code();
BOOST_THROW_EXCEPTION(ExecutionStateException());
CompilationResult* compilerRes = m_context->codeModel()->code();
std::shared_ptr<QContractDefinition> contractDef = compilerRes->sharedContract();
m_running = true;
emit runStarted();
emit stateChanged();
emit runStateChanged();
//run sequence
QtConcurrent::run([=]()
@ -152,59 +177,67 @@ void ClientModel::executeSequence(std::vector<TransactionSettings> const& _seque
try
{
bytes contractCode = compilerRes->bytes();
std::vector<dev::bytes> transactonData;
QFunctionDefinition* f = nullptr;
ContractCallDataEncoder c;
//encode data for all transactions
for (auto const& t: _sequence)
m_client->resetState(_balance);
onStateReset();
for (TransactionSettings const& transaction: _sequence)
{
f = nullptr;
for (int tf = 0; tf < contractDef->functionsList().size(); tf++)
ContractCallDataEncoder encoder;
QFunctionDefinition const* f = nullptr;
if (!transaction.stdContractUrl.isEmpty())
{
//std contract
dev::bytes const& stdContractCode = m_context->codeModel()->getStdContractCode(transaction.functionId, transaction.stdContractUrl);
Address address = deployContract(stdContractCode, transaction);
m_stdContractAddresses[transaction.functionId] = address;
m_stdContractNames[address] = transaction.functionId;
}
else
{
if (contractDef->functionsList().at(tf)->name() == t.functionId)
//encode data
f = nullptr;
if (transaction.functionId.isEmpty())
f = contractDef->constructor();
else
for (QFunctionDefinition const* tf: contractDef->functionsList())
if (tf->name() == transaction.functionId)
{
f = contractDef->functionsList().at(tf);
f = tf;
break;
}
}
if (!f)
throw std::runtime_error("function " + t.functionId.toStdString() + " not found");
BOOST_THROW_EXCEPTION(FunctionNotFoundException() << FunctionName(transaction.functionId.toStdString()));
for (int k = 0; k < f->parametersList() .size(); k++)
{
if (f->parametersList().at(k)->type() != t.parameterValues.at(k)->declaration()->type())
throw std::runtime_error("list of parameters has been changed. Need to update this transaction");
if (f->parametersList().at(k)->type() != transaction.parameterValues.at(k)->declaration()->type())
BOOST_THROW_EXCEPTION(ParameterChangedException() << FunctionName(f->parametersList().at(k)->type().toStdString()));
}
c.encode(f);
for (int p = 0; p < t.parameterValues.size(); p++)
c.push(t.parameterValues.at(p)->encodeValue());
transactonData.emplace_back(c.encodedData());
}
encoder.encode(f);
for (int p = 0; p < transaction.parameterValues.size(); p++)
encoder.push(transaction.parameterValues.at(p)->encodeValue());
//run contract creation first
m_client->resetState(_balance);
ExecutionResult debuggingContent = deployContract(contractCode, ctrTransaction);
Address address = debuggingContent.contractAddress;
for (unsigned i = 0; i < _sequence.size(); ++i)
debuggingContent = callContract(address, transactonData.at(i), _sequence.at(i));
QList<QVariableDefinition*> returnParameters;
if (f)
returnParameters = c.decode(f->returnParameters(), debuggingContent.returnValue);
//we need to wrap states in a QObject before sending to QML.
QList<QObject*> wStates;
for (unsigned i = 0; i < debuggingContent.machineStates.size(); i++)
if (transaction.functionId.isEmpty())
{
Address newAddress = deployContract(contractCode, transaction);
if (newAddress != m_contractAddress)
{
QPointer<DebuggingStateWrapper> s(new DebuggingStateWrapper(debuggingContent.executionCode, debuggingContent.executionData.toBytes()));
s->setState(debuggingContent.machineStates[i]);
wStates.append(s);
m_contractAddress = newAddress;
contractAddressChanged();
}
//collect states for last transaction
AssemblyDebuggerData code = DebuggingStateWrapper::getHumanReadableCode(debuggingContent.executionCode);
emit dataAvailable(returnParameters, wStates, code);
}
else
{
callContract(m_contractAddress, encoder.encodedData(), transaction);
// Used to log return values. TODO move this to QML.
ExecutionResult const& last = m_client->record().back().transactions.back();
encoder.decode(f->returnParameters(), last.returnValue);
}
}
onNewTransaction();
}
m_running = false;
emit runComplete();
}
catch(boost::exception const&)
@ -217,17 +250,47 @@ void ClientModel::executeSequence(std::vector<TransactionSettings> const& _seque
emit runFailed(e.what());
}
m_running = false;
emit stateChanged();
emit runStateChanged();
});
}
void ClientModel::showDebugger(QList<QVariableDefinition*> const& _returnParam, QList<QObject*> const& _wStates, AssemblyDebuggerData const& _code)
void ClientModel::showDebugger()
{
m_context->appEngine()->rootContext()->setContextProperty("debugStates", QVariant::fromValue(_wStates));
m_context->appEngine()->rootContext()->setContextProperty("humanReadableExecutionCode", QVariant::fromValue(std::get<0>(_code)));
m_context->appEngine()->rootContext()->setContextProperty("bytesCodeMapping", QVariant::fromValue(std::get<1>(_code)));
m_context->appEngine()->rootContext()->setContextProperty("contractCallReturnParameters", QVariant::fromValue(new QVariableDefinitionList(_returnParam)));
showDebuggerWindow();
ExecutionResult const& last = m_client->record().back().transactions.back();
showDebuggerForTransaction(last);
}
void ClientModel::showDebuggerForTransaction(ExecutionResult const& _t)
{
//we need to wrap states in a QObject before sending to QML.
QDebugData* debugData = new QDebugData();
QQmlEngine::setObjectOwnership(debugData, QQmlEngine::JavaScriptOwnership);
QList<QCode*> codes;
for (bytes const& code: _t.executionCode)
codes.push_back(QMachineState::getHumanReadableCode(debugData, code));
QList<QCallData*> data;
for (bytes const& d: _t.transactionData)
data.push_back(QMachineState::getDebugCallData(debugData, d));
QVariantList states;
for (MachineState const& s: _t.machineStates)
states.append(QVariant::fromValue(new QMachineState(debugData, s, codes[s.codeIndex], data[s.dataIndex])));
debugData->setStates(std::move(states));
//QList<QVariableDefinition*> returnParameters;
//returnParameters = encoder.decode(f->returnParameters(), debuggingContent.returnValue);
//collect states for last transaction
debugDataReady(debugData);
}
void ClientModel::debugTransaction(unsigned _block, unsigned _index)
{
auto const& t = m_client->record().at(_block).transactions.at(_index);
showDebuggerForTransaction(t);
}
void ClientModel::showDebugError(QString const& _error)
@ -236,32 +299,87 @@ void ClientModel::showDebugError(QString const& _error)
m_context->displayMessageDialog(tr("Debugger"), _error);
}
ExecutionResult ClientModel::deployContract(bytes const& _code, TransactionSettings const& _ctrTransaction)
Address ClientModel::deployContract(bytes const& _code, TransactionSettings const& _ctrTransaction)
{
Address newAddress;
if (!_ctrTransaction.isEmpty())
newAddress = m_client->transact(m_client->userAccount().secret(), _ctrTransaction.value, _code, _ctrTransaction.gas, _ctrTransaction.gasPrice);
else
Address newAddress = m_client->transact(m_client->userAccount().secret(), _ctrTransaction.value, _code, _ctrTransaction.gas, _ctrTransaction.gasPrice);
return newAddress;
}
void ClientModel::callContract(Address const& _contract, bytes const& _data, TransactionSettings const& _tr)
{
u256 gasPrice = 10000000000000;
u256 gas = 125000;
u256 amount = 100;
newAddress = m_client->transact(m_client->userAccount().secret(), amount, _code, gas, gasPrice);
m_client->transact(m_client->userAccount().secret(), _tr.value, _contract, _data, _tr.gas, _tr.gasPrice);
}
Address lastAddress = m_client->lastContractAddress();
ExecutionResult r = m_client->lastExecutionResult();
if (newAddress != lastAddress)
contractAddressChanged();
return r;
void ClientModel::onStateReset()
{
m_contractAddress = dev::Address();
m_stdContractAddresses.clear();
m_stdContractNames.clear();
emit stateCleared();
}
ExecutionResult ClientModel::callContract(Address const& _contract, bytes const& _data, TransactionSettings const& _tr)
void ClientModel::onNewTransaction()
{
m_client->transact(m_client->userAccount().secret(), _tr.value, _contract, _data, _tr.gas, _tr.gasPrice);
ExecutionResult r = m_client->lastExecutionResult();
r.contractAddress = _contract;
return r;
unsigned block = m_client->number();
unsigned index = m_client->record().back().transactions.size() - 1;
ExecutionResult const& tr = m_client->record().back().transactions.back();
QString address = QString::fromStdString(toJS(tr.address));
QString value = QString::fromStdString(dev::toString(tr.value));
QString contract = address;
QString function;
QString returned;
bool creation = tr.contractAddress != 0;
if (creation)
returned = QString::fromStdString(toJS(tr.contractAddress));
else
returned = QString::fromStdString(toJS(tr.returnValue));
//TODO: handle value transfer
FixedHash<4> functionHash;
bool call = false;
if (creation)
{
//contract creation
auto const stdContractName = m_stdContractNames.find(tr.contractAddress);
if (stdContractName != m_stdContractNames.end())
{
function = stdContractName->second;
contract = function;
}
else
function = QObject::tr("Constructor");
}
else
{
//call
if (tr.transactionData.size() > 0 && tr.transactionData.front().size() >= 4)
{
functionHash = FixedHash<4>(tr.transactionData.front().data(), FixedHash<4>::ConstructFromPointer);
function = QString::fromStdString(toJS(functionHash));
call = true;
}
else
function = QObject::tr("<none>");
}
if (m_contractAddress != 0 && (tr.address == m_contractAddress || tr.contractAddress == m_contractAddress))
{
auto compilerRes = m_context->codeModel()->code();
QContractDefinition* def = compilerRes->contract();
contract = def->name();
if (call)
{
QFunctionDefinition* funcDef = def->getFunction(functionHash);
if (funcDef)
function = funcDef->name();
}
}
TransactionLogEntry* log = new TransactionLogEntry(block, index, contract, function, value, address, returned);
QQmlEngine::setObjectOwnership(log, QQmlEngine::JavaScriptOwnership);
emit newTransaction(log);
}
}

90
mix/ClientModel.h

@ -24,13 +24,10 @@
#pragma once
#include <atomic>
#include "DebuggingStateWrapper.h"
#include <map>
#include <QString>
#include "MixClient.h"
using AssemblyDebuggerData = std::tuple<QList<QObject*>, dev::mix::QQMLMap*>;
Q_DECLARE_METATYPE(AssemblyDebuggerData)
Q_DECLARE_METATYPE(dev::mix::ExecutionResult)
#include "QVariableDefinition.h"
namespace dev
{
@ -40,6 +37,8 @@ namespace mix
class AppContext;
class Web3Server;
class RpcConnector;
class QEther;
class QDebugData;
/// Backend transaction config class
struct TransactionSettings
@ -47,6 +46,10 @@ struct TransactionSettings
TransactionSettings() {}
TransactionSettings(QString const& _functionId, u256 _value, u256 _gas, u256 _gasPrice):
functionId(_functionId), value(_value), gas(_gas), gasPrice(_gasPrice) {}
TransactionSettings(u256 _value, u256 _gas, u256 _gasPrice):
value(_value), gas(_gas), gasPrice(_gasPrice) {}
TransactionSettings(QString const& _stdContractName, QString const& _stdContractUrl):
functionId(_stdContractName), stdContractUrl(_stdContractUrl) {}
/// Contract function name
QString functionId;
@ -58,12 +61,45 @@ struct TransactionSettings
u256 gasPrice;
/// Mapping from contract function parameter name to value
QList<QVariableDefinition*> parameterValues;
/// Standard contract url
QString stdContractUrl;
};
/// UI Transaction log record
class TransactionLogEntry: public QObject
{
Q_OBJECT
/// Transaction block number
Q_PROPERTY(unsigned block MEMBER m_block CONSTANT)
/// Transaction index within the block
Q_PROPERTY(unsigned tindex MEMBER m_index CONSTANT)
/// Contract name if any
Q_PROPERTY(QString contract MEMBER m_contract CONSTANT)
/// Function name if any
Q_PROPERTY(QString function MEMBER m_function CONSTANT)
/// Transaction value
Q_PROPERTY(QString value MEMBER m_value CONSTANT)
/// Receiving address
Q_PROPERTY(QString address MEMBER m_address CONSTANT)
/// Returned value or transaction address in case of creation
Q_PROPERTY(QString returned MEMBER m_returned CONSTANT)
public:
/// @returns true if the functionId has not be set
bool isEmpty() const { return functionId.isNull() || functionId.isEmpty(); }
};
TransactionLogEntry():
m_block(0), m_index(0) {}
TransactionLogEntry(int _block, int _index, QString _contract, QString _function, QString _value, QString _address, QString _returned):
m_block(_block), m_index(_index), m_contract(_contract), m_function(_function), m_value(_value), m_address(_address), m_returned(_returned) {}
private:
unsigned m_block;
unsigned m_index;
QString m_contract;
QString m_function;
QString m_value;
QString m_address;
QString m_returned;
};
/**
* @brief Ethereum state control
@ -76,7 +112,7 @@ public:
ClientModel(AppContext* _context);
~ClientModel();
/// @returns true if currently executing contract code
Q_PROPERTY(bool running MEMBER m_running NOTIFY stateChanged)
Q_PROPERTY(bool running MEMBER m_running NOTIFY runStateChanged)
/// @returns address of the last executed contract
Q_PROPERTY(QString contractAddress READ contractAddress NOTIFY contractAddressChanged)
/// ethereum.js RPC request entry point
@ -84,16 +120,21 @@ public:
/// @returns RPC response in Json format
Q_INVOKABLE QString apiCall(QString const& _message);
/// Simulate mining. Creates a new block
Q_INVOKABLE void mine();
public slots:
/// Run the contract constructor and show debugger window.
void debugDeployment();
/// Setup state, run transaction sequence, show debugger for the last transaction
/// @param _state JS object with state configuration
void debugState(QVariantMap _state);
void setupState(QVariantMap _state);
/// Show the debugger for a specified transaction
Q_INVOKABLE void debugTransaction(unsigned _block, unsigned _index);
private slots:
/// Update UI with machine states result. Display a modal dialog.
void showDebugger(QList<QVariableDefinition*> const& _returnParams = QList<QVariableDefinition*>(), QList<QObject*> const& _wStates = QList<QObject*>(), AssemblyDebuggerData const& _code = AssemblyDebuggerData());
void showDebugger();
/// Update UI with transaction run error.
void showDebugError(QString const& _error);
@ -108,27 +149,36 @@ signals:
/// Contract address changed
void contractAddressChanged();
/// Execution state changed
void stateChanged();
void newBlock();
/// Execution state changed
void runStateChanged();
/// Show debugger window request
void showDebuggerWindow();
void debugDataReady(QObject* _debugData);
/// ethereum.js RPC response ready
/// @param _message RPC response in Json format
void apiResponse(QString const& _message);
/// Emited when machine states are available.
void dataAvailable(QList<QVariableDefinition*> const& _returnParams = QList<QVariableDefinition*>(), QList<QObject*> const& _wStates = QList<QObject*>(), AssemblyDebuggerData const& _code = AssemblyDebuggerData());
/// New transaction log entry
void newTransaction(TransactionLogEntry* _tr);
/// State (transaction log) cleared
void stateCleared();
private:
QString contractAddress() const;
void executeSequence(std::vector<TransactionSettings> const& _sequence, u256 _balance, TransactionSettings const& _ctrTransaction = TransactionSettings());
ExecutionResult deployContract(bytes const& _code, TransactionSettings const& _tr = TransactionSettings());
ExecutionResult callContract(Address const& _contract, bytes const& _data, TransactionSettings const& _tr);
void executeSequence(std::vector<TransactionSettings> const& _sequence, u256 _balance);
dev::Address deployContract(bytes const& _code, TransactionSettings const& _tr = TransactionSettings());
void callContract(Address const& _contract, bytes const& _data, TransactionSettings const& _tr);
void onNewTransaction();
void onStateReset();
void showDebuggerForTransaction(ExecutionResult const& _t);
AppContext* m_context;
std::atomic<bool> m_running;
std::unique_ptr<MixClient> m_client;
std::unique_ptr<RpcConnector> m_rpcConnector;
std::unique_ptr<Web3Server> m_web3Server;
Address m_contractAddress;
std::map<QString, Address> m_stdContractAddresses;
std::map<Address, QString> m_stdContractNames;
};
}

1
mix/CodeEditorExtensionManager.cpp

@ -51,7 +51,6 @@ void CodeEditorExtensionManager::loadEditor(QQuickItem* _editor)
{
if (!_editor)
return;
}
void CodeEditorExtensionManager::initExtensions()

27
mix/CodeModel.cpp

@ -32,6 +32,7 @@
#include "QFunctionDefinition.h"
#include "QVariableDeclaration.h"
#include "CodeHighlighter.h"
#include "FileIo.h"
#include "CodeModel.h"
using namespace dev::mix;
@ -132,7 +133,7 @@ void CodeModel::runCompilationJob(int _jobId, QString const& _code)
if (_jobId != m_backgroundJobId)
return; //obsolete job
solidity::CompilerStack cs;
solidity::CompilerStack cs(true);
std::unique_ptr<CompilationResult> result;
std::string source = _code.toStdString();
@ -141,10 +142,12 @@ void CodeModel::runCompilationJob(int _jobId, QString const& _code)
auto codeHighlighter = std::make_shared<CodeHighlighter>();
codeHighlighter->processSource(source);
cs.addSource("configUser", R"(contract configUser{function configAddr()constant returns(address a){ return 0xf025d81196b72fba60a1d4dddad12eeb8360d828;}})");
// run compilation
try
{
cs.setSource(source);
cs.addSource("", source);
cs.compile(false);
codeHighlighter->processAST(cs.getAST());
result.reset(new CompilationResult(cs));
@ -188,3 +191,23 @@ void CodeModel::updateFormatting(QTextDocument* _document)
{
m_result->codeHighlighter()->updateFormatting(_document, *m_codeHighlighterSettings);
}
dev::bytes const& CodeModel::getStdContractCode(const QString& _contractName, const QString& _url)
{
auto cached = m_compiledContracts.find(_contractName);
if (cached != m_compiledContracts.end())
return cached->second;
FileIo fileIo;
std::string source = fileIo.readFile(_url).toStdString();
solidity::CompilerStack cs(false);
cs.setSource(source);
cs.compile(false);
for (std::string const& name: cs.getContractNames())
{
dev::bytes code = cs.getBytecode(name);
m_compiledContracts.insert(std::make_pair(QString::fromStdString(name), std::move(code)));
}
return m_compiledContracts.at(_contractName);
}

4
mix/CodeModel.h

@ -24,6 +24,7 @@
#include <memory>
#include <atomic>
#include <map>
#include <QObject>
#include <QThread>
#include <libdevcore/Common.h>
@ -131,6 +132,8 @@ public:
bool hasContract() const;
/// Apply text document formatting. @todo Move this to editor module
void updateFormatting(QTextDocument* _document);
/// Get contract code by url. Contract is compiled on first access and cached
dev::bytes const& getStdContractCode(QString const& _contractName, QString const& _url);
signals:
/// Emited on compilation state change
@ -163,6 +166,7 @@ private:
QThread m_backgroundThread;
BackgroundWorker m_backgroundWorker;
int m_backgroundJobId = 0; //protects from starting obsolete compilation job
std::map<QString, dev::bytes> m_compiledContracts; //by name
friend class BackgroundWorker;
};

183
mix/DebuggingStateWrapper.cpp

@ -20,10 +20,11 @@
* Used to translate c++ type (u256, bytes, ...) into friendly value (to be used by QML).
*/
#include <QApplication>
#include <tuple>
#include <QDebug>
#include <QPointer>
#include <QQmlEngine>
#include <QVariantList>
#include <libevmcore/Instruction.h>
#include <libdevcore/CommonJS.h>
#include <libdevcrypto/Common.h>
@ -35,10 +36,42 @@ using namespace dev;
using namespace dev::eth;
using namespace dev::mix;
std::tuple<QList<QObject*>, QQMLMap*> DebuggingStateWrapper::getHumanReadableCode(const bytes& _code)
namespace
{
QList<QObject*> codeStr;
QMap<int, int> codeMapping;
static QVariantList memDumpToList(bytes const& _bytes, unsigned _width)
{
QVariantList dumpList;
for (unsigned i = 0; i < _bytes.size(); i += _width)
{
std::stringstream ret;
for (unsigned j = i; j < i + _width; ++j)
if (j < _bytes.size())
if (_bytes[j] >= 32 && _bytes[j] < 127)
ret << (char)_bytes[j];
else
ret << '?';
else
ret << ' ';
QString strPart = QString::fromStdString(ret.str());
ret.clear();
ret.str(std::string());
for (unsigned j = i; j < i + _width && j < _bytes.size(); ++j)
ret << std::setfill('0') << std::setw(2) << std::hex << (unsigned)_bytes[j] << " ";
QString hexPart = QString::fromStdString(ret.str());
QStringList line = { strPart, hexPart };
dumpList.push_back(line);
}
return dumpList;
}
}
QCode* QMachineState::getHumanReadableCode(QObject* _owner, const bytes& _code)
{
QVariantList codeStr;
for (unsigned i = 0; i <= _code.size(); ++i)
{
byte b = i < _code.size() ? _code[i] : 0;
@ -47,7 +80,6 @@ std::tuple<QList<QObject*>, QQMLMap*> DebuggingStateWrapper::getHumanReadableCod
QString s = QString::fromStdString(instructionInfo((Instruction)b).name);
std::ostringstream out;
out << std::hex << std::setw(4) << std::setfill('0') << i;
codeMapping[i] = codeStr.size();
int line = i;
if (b >= (byte)Instruction::PUSH1 && b <= (byte)Instruction::PUSH32)
{
@ -55,8 +87,7 @@ std::tuple<QList<QObject*>, QQMLMap*> DebuggingStateWrapper::getHumanReadableCod
s = "PUSH 0x" + QString::fromStdString(toHex(bytesConstRef(&_code[i + 1], bc)));
i += bc;
}
QPointer<HumanReadableCode> humanCode(new HumanReadableCode(QString::fromStdString(out.str()) + " " + s, line));
codeStr.append(humanCode);
codeStr.append(QVariant::fromValue(new QInstruction(_owner, QString::fromStdString(out.str()) + " " + s, line)));
}
catch (...)
{
@ -65,160 +96,76 @@ std::tuple<QList<QObject*>, QQMLMap*> DebuggingStateWrapper::getHumanReadableCod
break; // probably hit data segment
}
}
return std::make_tuple(codeStr, QPointer<QQMLMap>(new QQMLMap(codeMapping)));
return new QCode(_owner, std::move(codeStr));
}
QBigInt* DebuggingStateWrapper::gasCost()
QBigInt* QMachineState::gasCost()
{
return new QBigInt(m_state.gasCost);
}
QBigInt* DebuggingStateWrapper::gas()
QBigInt* QMachineState::gas()
{
return new QBigInt(m_state.gas);
}
QBigInt* DebuggingStateWrapper::newMemSize()
QBigInt* QMachineState::newMemSize()
{
return new QBigInt(m_state.newMemSize);
}
QStringList DebuggingStateWrapper::debugStack()
QStringList QMachineState::debugStack()
{
QStringList stack;
for (std::vector<u256>::reverse_iterator i = m_state.stack.rbegin(); i != m_state.stack.rend(); ++i)
stack.append(QString::fromStdString(prettyU256(*i)));
return fillList(stack, "");
return stack;
}
QStringList DebuggingStateWrapper::debugStorage()
QStringList QMachineState::debugStorage()
{
QStringList storage;
for (auto const& i: m_state.storage)
{
std::stringstream s;
s << "@" << prettyU256(i.first) << " " << prettyU256(i.second);
s << "@" << prettyU256(i.first) << "\t" << prettyU256(i.second);
storage.append(QString::fromStdString(s.str()));
}
return fillList(storage, "@ -");
return storage;
}
QVariantList DebuggingStateWrapper::debugMemory()
QVariantList QMachineState::debugMemory()
{
std::vector<std::vector<std::string>> dump = memDumpToList(m_state.memory, 16);
QStringList filled;
filled.append(" ");
filled.append(" ");
filled.append(" ");
return fillList(qVariantDump(dump), QVariant(filled));
return memDumpToList(m_state.memory, 16);
}
QVariantList DebuggingStateWrapper::debugCallData()
QCallData* QMachineState::getDebugCallData(QObject* _owner, bytes const& _data)
{
std::vector<std::vector<std::string>> dump = memDumpToList(m_data, 16);
QStringList filled;
filled.append(" ");
filled.append(" ");
filled.append(" ");
return fillList(qVariantDump(dump), QVariant(filled));
return new QCallData(_owner, memDumpToList(_data, 16));
}
std::vector<std::vector<std::string>> DebuggingStateWrapper::memDumpToList(bytes const& _bytes, unsigned _width)
QVariantList QMachineState::levels()
{
std::vector<std::vector<std::string>> dump;
for (unsigned i = 0; i < _bytes.size(); i += _width)
{
std::stringstream ret;
std::vector<std::string> dumpLine;
ret << std::hex << std::setw(4) << std::setfill('0') << i << " ";
dumpLine.push_back(ret.str());
ret.str(std::string());
ret.clear();
for (unsigned j = i; j < i + _width; ++j)
if (j < _bytes.size())
if (_bytes[j] >= 32 && _bytes[j] < 127)
ret << (char)_bytes[j];
else
ret << '?';
else
ret << ' ';
dumpLine.push_back(ret.str());
ret.str(std::string());
ret.clear();
for (unsigned j = i; j < i + _width && j < _bytes.size(); ++j)
ret << std::setfill('0') << std::setw(2) << std::hex << (unsigned)_bytes[j] << " ";
dumpLine.push_back(ret.str());
dump.push_back(dumpLine);
}
return dump;
}
QVariantList DebuggingStateWrapper::qVariantDump(std::vector<std::vector<std::string>> const& _dump)
{
QVariantList ret;
for (std::vector<std::string> const& line: _dump)
{
QStringList qLine;
for (std::string const& cell: line)
qLine.push_back(QString::fromStdString(cell));
ret.append(QVariant(qLine));
}
return ret;
}
QStringList DebuggingStateWrapper::fillList(QStringList& _list, QString const& _emptyValue)
{
if (_list.size() < 20)
{
for (int k = _list.size(); k < 20 - _list.size(); k++)
_list.append(_emptyValue);
}
return _list;
}
QVariantList DebuggingStateWrapper::fillList(QVariantList _list, QVariant const& _emptyValue)
{
if (_list.size() < 20)
{
for (int k = _list.size(); k < 20 - _list.size(); k++)
_list.append(_emptyValue);
}
return _list;
}
QStringList DebuggingStateWrapper::levels()
{
QStringList levelsStr;
for (unsigned i = 0; i <= m_state.levels.size(); ++i)
{
std::ostringstream out;
out << m_state.cur.abridged();
if (i)
out << " " << instructionInfo(m_state.inst).name << " @0x" << std::hex << m_state.curPC;
levelsStr.append(QString::fromStdString(out.str()));
}
return levelsStr;
QVariantList levelList;
for (unsigned l: m_state.levels)
levelList.push_back(l);
return levelList;
}
QString DebuggingStateWrapper::headerInfo()
QString QMachineState::address()
{
std::ostringstream ss;
ss << std::dec << " " << QApplication::tr("STEP").toStdString() << " : " << m_state.steps << " | PC: 0x" << std::hex << m_state.curPC << " : " << dev::eth::instructionInfo(m_state.inst).name << " | ADDMEM: " << std::dec << m_state.newMemSize << " " << QApplication::tr("words").toStdString() << " | " << QApplication::tr("COST").toStdString() << " : " << std::dec << m_state.gasCost << " | " << QApplication::tr("GAS").toStdString() << " : " << std::dec << m_state.gas;
return QString::fromStdString(ss.str());
return QString::fromStdString(toString(m_state.address));
}
QString DebuggingStateWrapper::instruction()
QString QMachineState::instruction()
{
return QString::fromStdString(dev::eth::instructionInfo(m_state.inst).name);
}
QString DebuggingStateWrapper::endOfDebug()
QString QMachineState::endOfDebug()
{
if (m_state.gasCost > m_state.gas)
return QApplication::tr("OUT-OF-GAS");
return QObject::tr("OUT-OF-GAS");
else if (m_state.inst == Instruction::RETURN && m_state.stack.size() >= 2)
{
unsigned from = (unsigned)m_state.stack.back();
@ -227,12 +174,12 @@ QString DebuggingStateWrapper::endOfDebug()
bytes out(size, 0);
for (; o < size && from + o < m_state.memory.size(); ++o)
out[o] = m_state.memory[from + o];
return QApplication::tr("RETURN") + " " + QString::fromStdString(dev::memDump(out, 16, false));
return QObject::tr("RETURN") + " " + QString::fromStdString(dev::memDump(out, 16, false));
}
else if (m_state.inst == Instruction::STOP)
return QApplication::tr("STOP");
return QObject::tr("STOP");
else if (m_state.inst == Instruction::SUICIDE && m_state.stack.size() >= 1)
return QApplication::tr("SUICIDE") + " 0x" + QString::fromStdString(toString(right160(m_state.stack.back())));
return QObject::tr("SUICIDE") + " 0x" + QString::fromStdString(toString(right160(m_state.stack.back())));
else
return QApplication::tr("EXCEPTION");
return QObject::tr("EXCEPTION");
}

98
mix/DebuggingStateWrapper.h

@ -39,45 +39,70 @@ namespace mix
/**
* @brief Contains the line nb of the assembly code and the corresponding index in the code bytes array.
*/
class HumanReadableCode: public QObject
class QInstruction: public QObject
{
Q_OBJECT
Q_PROPERTY(QString line READ line CONSTANT)
Q_PROPERTY(int processIndex READ processIndex CONSTANT)
Q_PROPERTY(QString line MEMBER m_line CONSTANT)
Q_PROPERTY(int processIndex MEMBER m_processIndex CONSTANT)
public:
HumanReadableCode(QString _line, int _processIndex): QObject(), m_line(_line), m_processIndex(_processIndex) {}
/// Get the assembly code line.
QString line() { return m_line; }
/// Get corresponding index.
int processIndex() { return m_processIndex; }
QInstruction(QObject* _owner, QString _line, int _processIndex): QObject(_owner), m_line(_line), m_processIndex(_processIndex) {}
private:
QString m_line;
int m_processIndex;
};
/**
* @brief Shared container for lines
*/
class QCode: public QObject
{
Q_OBJECT
Q_PROPERTY(QVariantList instructions MEMBER m_instructions CONSTANT)
public:
QCode(QObject* _owner, QVariantList&& _instrunctions): QObject(_owner), m_instructions(_instrunctions) {}
private:
QVariantList m_instructions;
};
/**
* @brief Shared container for call data
*/
class QCallData: public QObject
{
Q_OBJECT
Q_PROPERTY(QVariantList items MEMBER m_items CONSTANT)
public:
QCallData(QObject* _owner, QVariantList&& _items): QObject(_owner), m_items(_items) {}
private:
QVariantList m_items;
};
/**
* @brief Publish QMap type to QML.
* @brief Shared container for machine states
*/
class QQMLMap: public QObject
class QDebugData: public QObject
{
Q_OBJECT
Q_PROPERTY(QVariantList states MEMBER m_states CONSTANT)
public:
QQMLMap(QMap<int, int> _map): QObject(), m_map(_map) { }
/// Get the value associated with _key store in n_map.
Q_INVOKABLE int getValue(int _key) { return m_map.value(_key); }
QDebugData() { }
void setStates(QVariantList&& _states) { m_states = _states; }
private:
QMap<int, int> m_map;
QVariantList m_states;
};
/**
* @brief Wrap DebuggingState in QObject
* @brief Wrap MachineState in QObject
*/
class DebuggingStateWrapper: public QObject
class QMachineState: public QObject
{
Q_OBJECT
Q_PROPERTY(int step READ step CONSTANT)
@ -85,21 +110,31 @@ class DebuggingStateWrapper: public QObject
Q_PROPERTY(QBigInt* gasCost READ gasCost CONSTANT)
Q_PROPERTY(QBigInt* gas READ gas CONSTANT)
Q_PROPERTY(QString instruction READ instruction CONSTANT)
Q_PROPERTY(QString address READ address CONSTANT)
Q_PROPERTY(QStringList debugStack READ debugStack CONSTANT)
Q_PROPERTY(QStringList debugStorage READ debugStorage CONSTANT)
Q_PROPERTY(QVariantList debugMemory READ debugMemory CONSTANT)
Q_PROPERTY(QVariantList debugCallData READ debugCallData CONSTANT)
Q_PROPERTY(QString headerInfo READ headerInfo CONSTANT)
Q_PROPERTY(QObject* code MEMBER m_code CONSTANT)
Q_PROPERTY(QObject* callData MEMBER m_callData CONSTANT)
Q_PROPERTY(QString endOfDebug READ endOfDebug CONSTANT)
Q_PROPERTY(QBigInt* newMemSize READ newMemSize CONSTANT)
Q_PROPERTY(QStringList levels READ levels CONSTANT)
Q_PROPERTY(QVariantList levels READ levels CONSTANT)
Q_PROPERTY(unsigned codeIndex READ codeIndex CONSTANT)
Q_PROPERTY(unsigned dataIndex READ dataIndex CONSTANT)
public:
DebuggingStateWrapper(bytes _code, bytes _data): QObject(), m_code(_code), m_data(_data) {}
QMachineState(QObject* _owner, MachineState const& _state, QCode* _code, QCallData* _callData):
QObject(_owner), m_state(_state), m_code(_code), m_callData(_callData) {}
/// Get the step of this machine states.
int step() { return (int)m_state.steps; }
/// Get the proccessed code index.
int curPC() { return (int)m_state.curPC; }
/// Get the code id
unsigned codeIndex() { return m_state.codeIndex; }
/// Get the call data id
unsigned dataIndex() { return m_state.dataIndex; }
/// Get address for call stack
QString address();
/// Get gas cost.
QBigInt* gasCost();
/// Get gas used.
@ -110,10 +145,6 @@ public:
QStringList debugStorage();
/// Get memory.
QVariantList debugMemory();
/// Get call data.
QVariantList debugCallData();
/// Get info to be displayed in the header.
QString headerInfo();
/// get end of debug information.
QString endOfDebug();
/// Get the new memory size.
@ -121,25 +152,20 @@ public:
/// Get current instruction
QString instruction();
/// Get all previous steps.
QStringList levels();
QVariantList levels();
/// Get the current processed machine state.
MachineState state() { return m_state; }
/// Set the current processed machine state.
void setState(MachineState _state) { m_state = _state; }
/// Convert all machine state in human readable code.
static std::tuple<QList<QObject*>, QQMLMap*> getHumanReadableCode(bytes const& _code);
/// Convert all machine states in human readable code.
static QCode* getHumanReadableCode(QObject* _owner, bytes const& _code);
/// Convert call data into human readable form
static QCallData* getDebugCallData(QObject* _owner, bytes const& _data);
private:
MachineState m_state;
bytes m_code;
bytes m_data;
QStringList fillList(QStringList& _list, QString const& _emptyValue);
QVariantList fillList(QVariantList _list, QVariant const& _emptyValue);
QVariantList qVariantDump(std::vector<std::vector<std::string>> const& _dump);
/// Nicely renders the given bytes to a string, store the content in an array.
/// @a _bytes: bytes array to be rendered as string. @a _width of a bytes line.
std::vector<std::vector<std::string>> memDumpToList(bytes const& _bytes, unsigned _width);
QCode* m_code;
QCallData* m_callData;
};
}

6
mix/Exceptions.h

@ -35,9 +35,15 @@ namespace mix
struct QmlLoadException: virtual Exception {};
struct FileIoException: virtual Exception {};
struct InvalidBlockException: virtual Exception {};
struct FunctionNotFoundException: virtual Exception {};
struct ExecutionStateException: virtual Exception {};
struct ParameterChangedException: virtual Exception {};
typedef boost::error_info<struct tagQmlError, QQmlError> QmlErrorInfo;
typedef boost::error_info<struct tagFileError, std::string> FileError;
typedef boost::error_info<struct tagBlockIndex, unsigned> BlockIndex;
typedef boost::error_info<struct tagFunctionName, std::string> FunctionName;
}
}

2
mix/HttpServer.cpp

@ -72,7 +72,7 @@ void HttpServer::setPort(int _port)
QString HttpServer::errorString() const
{
return this->errorString();
return QTcpServer::errorString();
}
void HttpServer::setListen(bool _listen)

2
mix/MixApplication.cpp

@ -36,7 +36,7 @@ using namespace dev::mix;
MixApplication::MixApplication(int _argc, char* _argv[]):
QApplication(_argc, _argv), m_engine(new QQmlApplicationEngine()), m_appContext(new AppContext(m_engine.get()))
{
setOrganizationName(tr("Ethreum"));
setOrganizationName(tr("Ethereum"));
setOrganizationDomain(tr("ethereum.org"));
setApplicationName(tr("Mix"));
setApplicationVersion("0.1");

271
mix/MixClient.cpp

@ -29,14 +29,17 @@
#include <libethereum/ExtVM.h>
#include <libevm/VM.h>
#include "Exceptions.h"
#include "MixClient.h"
using namespace dev;
using namespace dev::eth;
using namespace dev::mix;
const Secret c_stdSecret = Secret("cb73d9408c4720e230387d956eb0f829d8a4dd2c1055f96257167e14e7169074");
MixClient::MixClient():
m_userAccount(KeyPair::create())
m_userAccount(c_stdSecret)
{
resetState(10000000 * ether);
}
@ -44,38 +47,71 @@ MixClient::MixClient():
void MixClient::resetState(u256 _balance)
{
WriteGuard l(x_state);
m_state = eth::State(Address(), m_stateDB, BaseState::Empty);
Guard fl(m_filterLock);
m_filters.clear();
m_watches.clear();
m_state = eth::State(m_userAccount.address(), m_stateDB, BaseState::Empty);
m_state.addBalance(m_userAccount.address(), _balance);
Block genesis;
genesis.state = m_state;
Block open;
m_blocks = Blocks { genesis, open }; //last block contains a list of pending transactions to be finalized
}
void MixClient::executeTransaction(bytesConstRef _rlp, State& _state)
void MixClient::executeTransaction(Transaction const& _t, State& _state)
{
bytes rlp = _t.rlp();
Executive execution(_state, LastHashes(), 0);
execution.setup(_rlp);
bytes code;
bytesConstRef data;
bool firstIteration = true;
execution.setup(&rlp);
std::vector<MachineState> machineStates;
std::vector<MachineState const*> levels;
std::vector<unsigned> levels;
std::vector<bytes> codes;
std::map<bytes const*, unsigned> codeIndexes;
std::vector<bytes> data;
std::map<bytesConstRef const*, unsigned> dataIndexes;
bytes const* lastCode = nullptr;
bytesConstRef const* lastData = nullptr;
unsigned codeIndex = 0;
unsigned dataIndex = 0;
auto onOp = [&](uint64_t steps, Instruction inst, dev::bigint newMemSize, dev::bigint gasCost, void* voidVM, void const* voidExt)
{
VM& vm = *(VM*)voidVM;
ExtVM const& ext = *(ExtVM const*)voidExt;
if (lastCode == nullptr || lastCode != &ext.code)
{
auto const& iter = codeIndexes.find(&ext.code);
if (iter != codeIndexes.end())
codeIndex = iter->second;
else
{
codeIndex = codes.size();
codes.push_back(ext.code);
codeIndexes[&ext.code] = codeIndex;
}
lastCode = &ext.code;
}
if (firstIteration)
if (lastData == nullptr || lastData != &ext.data)
{
auto const& iter = dataIndexes.find(&ext.data);
if (iter != dataIndexes.end())
dataIndex = iter->second;
else
{
code = ext.code;
data = ext.data;
firstIteration = false;
dataIndex = data.size();
data.push_back(ext.data.toBytes());
dataIndexes[&ext.data] = dataIndex;
}
lastData = &ext.data;
}
if (levels.size() < ext.depth)
levels.push_back(&machineStates.back());
levels.push_back(machineStates.size() - 1);
else
levels.resize(ext.depth);
machineStates.push_back(MachineState({steps, ext.myAddress, vm.curPC(), inst, newMemSize, vm.gas(),
vm.stack(), vm.memory(), gasCost, ext.state().storage(ext.myAddress), levels}));
machineStates.emplace_back(MachineState({steps, ext.myAddress, vm.curPC(), inst, newMemSize, vm.gas(),
vm.stack(), vm.memory(), gasCost, ext.state().storage(ext.myAddress), levels, codeIndex, dataIndex}));
};
execution.go(onOp);
@ -84,18 +120,63 @@ void MixClient::executeTransaction(bytesConstRef _rlp, State& _state)
ExecutionResult d;
d.returnValue = execution.out().toVector();
d.machineStates = machineStates;
d.executionCode = code;
d.executionData = data;
d.contentAvailable = true;
d.message = "ok";
d.contractAddress = m_lastExecutionResult.contractAddress;
m_lastExecutionResult = d;
d.executionCode = std::move(codes);
d.transactionData = std::move(data);
d.address = _t.receiveAddress();
d.sender = _t.sender();
d.value = _t.value();
if (_t.isCreation())
d.contractAddress = right160(sha3(rlpList(_t.sender(), _t.nonce())));
d.receipt = TransactionReceipt(m_state.rootHash(), execution.gasUsed(), execution.logs()); //TODO: track gas usage
m_blocks.back().transactions.emplace_back(d);
h256Set changed;
Guard l(m_filterLock);
for (std::pair<h256 const, eth::InstalledFilter>& i: m_filters)
if ((unsigned)i.second.filter.latest() > m_blocks.size() - 1)
{
// acceptable number.
auto m = i.second.filter.matches(d.receipt);
if (m.size())
{
// filter catches them
for (LogEntry const& l: m)
i.second.changes.push_back(LocalisedLogEntry(l, m_blocks.size()));
changed.insert(i.first);
}
}
changed.insert(dev::eth::PendingChangedFilter);
noteChanged(changed);
}
void MixClient::validateBlock(int _block) const
{
//TODO: throw exception here if _block != 0
(void)_block;
if (_block != -1 && _block != 0 && (unsigned)_block >= m_blocks.size() - 1)
BOOST_THROW_EXCEPTION(InvalidBlockException() << BlockIndex(_block));
}
void MixClient::mine()
{
WriteGuard l(x_state);
Block& block = m_blocks.back();
m_state.completeMine();
block.state = m_state;
block.info = m_state.info();
block.hash = block.info.hash;
m_blocks.push_back(Block());
h256Set changed { dev::eth::PendingChangedFilter, dev::eth::ChainChangedFilter };
noteChanged(changed);
}
State const& MixClient::asOf(int _block) const
{
validateBlock(_block);
if (_block == 0)
return m_blocks[m_blocks.size() - 2].state;
else if (_block == -1)
return m_state;
else
return m_blocks[_block].state;
}
void MixClient::transact(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice)
@ -103,8 +184,7 @@ void MixClient::transact(Secret _secret, u256 _value, Address _dest, bytes const
WriteGuard l(x_state);
u256 n = m_state.transactionsFrom(toAddress(_secret));
Transaction t(_value, _gasPrice, _gas, _dest, _data, n, _secret);
bytes rlp = t.rlp();
executeTransaction(&rlp, m_state);
executeTransaction(t, m_state);
}
Address MixClient::transact(Secret _secret, u256 _endowment, bytes const& _init, u256 _gas, u256 _gasPrice)
@ -112,17 +192,16 @@ Address MixClient::transact(Secret _secret, u256 _endowment, bytes const& _init,
WriteGuard l(x_state);
u256 n = m_state.transactionsFrom(toAddress(_secret));
eth::Transaction t(_endowment, _gasPrice, _gas, _init, n, _secret);
bytes rlp = t.rlp();
executeTransaction(&rlp, m_state);
executeTransaction(t, m_state);
Address address = right160(sha3(rlpList(t.sender(), t.nonce())));
m_lastExecutionResult.contractAddress = address;
return address;
}
void MixClient::inject(bytesConstRef _rlp)
{
WriteGuard l(x_state);
executeTransaction(_rlp, m_state);
eth::Transaction t(_rlp, CheckSignature::None);
executeTransaction(t, m_state);
}
void MixClient::flushTransactions()
@ -140,92 +219,154 @@ bytes MixClient::call(Secret _secret, u256 _value, Address _dest, bytes const& _
}
Transaction t(_value, _gasPrice, _gas, _dest, _data, n, _secret);
bytes rlp = t.rlp();
WriteGuard lw(x_state); //TODO: lock is required only for last executoin state
executeTransaction(&rlp, temp);
return m_lastExecutionResult.returnValue;
WriteGuard lw(x_state); //TODO: lock is required only for last execution state
executeTransaction(t, temp);
return m_blocks.back().transactions.back().returnValue;
}
u256 MixClient::balanceAt(Address _a, int _block) const
{
validateBlock(_block);
ReadGuard l(x_state);
return m_state.balance(_a);
return asOf(_block).balance(_a);
}
u256 MixClient::countAt(Address _a, int _block) const
{
validateBlock(_block);
ReadGuard l(x_state);
return m_state.transactionsFrom(_a);
return asOf(_block).transactionsFrom(_a);
}
u256 MixClient::stateAt(Address _a, u256 _l, int _block) const
{
validateBlock(_block);
ReadGuard l(x_state);
return m_state.storage(_a, _l);
return asOf(_block).storage(_a, _l);
}
bytes MixClient::codeAt(Address _a, int _block) const
{
validateBlock(_block);
ReadGuard l(x_state);
return m_state.code(_a);
return asOf(_block).code(_a);
}
std::map<u256, u256> MixClient::storageAt(Address _a, int _block) const
{
validateBlock(_block);
ReadGuard l(x_state);
return m_state.storage(_a);
return asOf(_block).storage(_a);
}
eth::LocalisedLogEntries MixClient::logs(unsigned _watchId) const
{
(void)_watchId;
return LocalisedLogEntries();
Guard l(m_filterLock);
h256 h = m_watches.at(_watchId).id;
auto filterIter = m_filters.find(h);
if (filterIter != m_filters.end())
return logs(filterIter->second.filter);
return eth::LocalisedLogEntries();
}
eth::LocalisedLogEntries MixClient::logs(eth::LogFilter const& _filter) const
eth::LocalisedLogEntries MixClient::logs(eth::LogFilter const& _f) const
{
(void)_filter;
return LocalisedLogEntries();
LocalisedLogEntries ret;
unsigned lastBlock = m_blocks.size() - 1; //last block contains pending transactions
unsigned block = std::min<unsigned>(lastBlock, (unsigned)_f.latest());
unsigned end = std::min(lastBlock, std::min(block, (unsigned)_f.earliest()));
for (; ret.size() != _f.max() && block != end; block--)
{
bool pendingBlock = (block == lastBlock);
if (pendingBlock || _f.matches(m_blocks[block].info.logBloom))
for (ExecutionResult const& t: m_blocks[block].transactions)
if (pendingBlock || _f.matches(t.receipt.bloom()))
{
LogEntries logEntries = _f.matches(t.receipt);
if (logEntries.size())
{
for (unsigned entry = _f.skip(); entry < logEntries.size() && ret.size() != _f.max(); ++entry)
ret.insert(ret.begin(), LocalisedLogEntry(logEntries[entry], block));
}
}
}
return ret;
}
unsigned MixClient::installWatch(eth::LogFilter const& _filter)
unsigned MixClient::installWatch(h256 _h)
{
unsigned ret;
{
(void)_filter;
BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::Interface::installWatch"));
Guard l(m_filterLock);
ret = m_watches.size() ? m_watches.rbegin()->first + 1 : 0;
m_watches[ret] = ClientWatch(_h);
}
auto ch = logs(ret);
if (ch.empty())
ch.push_back(eth::InitialChange);
{
Guard l(m_filterLock);
swap(m_watches[ret].changes, ch);
}
return ret;
}
unsigned MixClient::installWatch(h256 _filterId)
unsigned MixClient::installWatch(eth::LogFilter const& _f)
{
h256 h = _f.sha3();
{
(void)_filterId;
BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::Interface::installWatch"));
Guard l(m_filterLock);
m_filters.insert(std::make_pair(h, _f));
}
return installWatch(h);
}
void MixClient::uninstallWatch(unsigned _i)
{
Guard l(m_filterLock);
auto it = m_watches.find(_i);
if (it == m_watches.end())
return;
auto id = it->second.id;
m_watches.erase(it);
auto fit = m_filters.find(id);
if (fit != m_filters.end())
if (!--fit->second.refCount)
m_filters.erase(fit);
}
void MixClient::uninstallWatch(unsigned _watchId)
void MixClient::noteChanged(h256Set const& _filters)
{
for (auto& i: m_watches)
if (_filters.count(i.second.id))
{
(void)_watchId;
BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::Interface::uninstallWatch"));
if (m_filters.count(i.second.id))
i.second.changes += m_filters.at(i.second.id).changes;
else
i.second.changes.push_back(LocalisedLogEntry(SpecialLogEntry, 0));
}
for (auto& i: m_filters)
i.second.changes.clear();
}
eth::LocalisedLogEntries MixClient::peekWatch(unsigned _watchId) const
LocalisedLogEntries MixClient::peekWatch(unsigned _watchId) const
{
(void)_watchId;
BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::Interface::peekWatch"));
Guard l(m_filterLock);
if (_watchId < m_watches.size())
return m_watches.at(_watchId).changes;
return LocalisedLogEntries();
}
eth::LocalisedLogEntries MixClient::checkWatch(unsigned _watchId)
LocalisedLogEntries MixClient::checkWatch(unsigned _watchId)
{
(void)_watchId;
BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::Interface::checkWatch"));
Guard l(m_filterLock);
LocalisedLogEntries ret;
if (_watchId < m_watches.size())
std::swap(ret, m_watches.at(_watchId).changes);
return ret;
}
h256 MixClient::hashFromNumber(unsigned _number) const
{
(void)_number;
BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::Interface::hashFromNumber"));
validateBlock(_number);
return m_blocks[_number].hash;
}
eth::BlockInfo MixClient::blockInfo(h256 _hash) const
@ -256,7 +397,7 @@ eth::BlockInfo MixClient::uncle(h256 _blockHash, unsigned _i) const
unsigned MixClient::number() const
{
return 0;
return m_blocks.size() - 1;
}
eth::Transactions MixClient::pending() const

50
mix/MixClient.h

@ -23,7 +23,9 @@
#pragma once
#include <vector>
#include <libethereum/Interface.h>
#include <libethereum/Client.h>
namespace dev
{
@ -36,7 +38,7 @@ namespace mix
struct MachineState
{
uint64_t steps;
dev::Address cur;
dev::Address address;
dev::u256 curPC;
dev::eth::Instruction inst;
dev::bigint newMemSize;
@ -45,7 +47,9 @@ struct MachineState
dev::bytes memory;
dev::bigint gasCost;
std::map<dev::u256, dev::u256> storage;
std::vector<MachineState const*> levels;
std::vector<unsigned> levels;
unsigned codeIndex;
unsigned dataIndex;
};
/**
@ -53,24 +57,41 @@ struct MachineState
*/
struct ExecutionResult
{
ExecutionResult(): receipt(dev::h256(), dev::h256(), dev::eth::LogEntries()) {}
std::vector<MachineState> machineStates;
bytes executionCode;
bytesConstRef executionData;
Address contractAddress;
bool contentAvailable;
std::string message;
std::vector<bytes> transactionData;
std::vector<bytes> executionCode;
bytes returnValue;
dev::Address address;
dev::Address sender;
dev::Address contractAddress;
dev::u256 value;
dev::eth::TransactionReceipt receipt;
};
using ExecutionResults = std::vector<ExecutionResult>;
struct Block
{
ExecutionResults transactions;
h256 hash;
dev::eth::State state;
dev::eth::BlockInfo info;
};
using Blocks = std::vector<Block>;
class MixClient: public dev::eth::Interface
{
public:
MixClient();
/// Reset state to the empty state with given balance.
void resetState(u256 _balance);
const KeyPair& userAccount() const { return m_userAccount; }
const ExecutionResult lastExecutionResult() const { ReadGuard l(x_state); return m_lastExecutionResult; }
const Address lastContractAddress() const { ReadGuard l(x_state); return m_lastExecutionResult.contractAddress; }
KeyPair const& userAccount() const { return m_userAccount; }
void mine();
Blocks const& record() const { return m_blocks; }
//dev::eth::Interface
void transact(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice) override;
@ -111,14 +132,19 @@ public:
eth::MineProgress miningProgress() const override;
private:
void executeTransaction(bytesConstRef _rlp, eth::State& _state);
void executeTransaction(dev::eth::Transaction const& _t, eth::State& _state);
void validateBlock(int _block) const;
void noteChanged(h256Set const& _filters);
dev::eth::State const& asOf(int _block) const;
KeyPair m_userAccount;
eth::State m_state;
OverlayDB m_stateDB;
mutable boost::shared_mutex x_state;
ExecutionResult m_lastExecutionResult;
mutable std::mutex m_filterLock;
std::map<h256, dev::eth::InstalledFilter> m_filters;
std::map<unsigned, dev::eth::ClientWatch> m_watches;
Blocks m_blocks;
};
}

14
mix/QContractDefinition.cpp

@ -34,13 +34,19 @@ using namespace dev::mix;
QContractDefinition::QContractDefinition(dev::solidity::ContractDefinition const* _contract): QBasicNodeDefinition(_contract)
{
if (_contract->getConstructor() != nullptr)
{
FunctionDescription desc(_contract->getConstructor());
m_constructor = new QFunctionDefinition(desc);
}
m_constructor = new QFunctionDefinition(ContractType(*_contract).getConstructorType());
else
m_constructor = new QFunctionDefinition();
for (auto const& it: _contract->getInterfaceFunctions())
m_functions.append(new QFunctionDefinition(it.second));}
QFunctionDefinition* QContractDefinition::getFunction(dev::FixedHash<4> _hash)
{
for (auto const& f: m_functions)
if (f->hash() == _hash)
return f;
return nullptr;
}

2
mix/QContractDefinition.h

@ -46,6 +46,8 @@ public:
/// Get the constructor of the contract.
QFunctionDefinition* constructor() const { return m_constructor; }
QList<QFunctionDefinition*> const& functionsList() const { return m_functions; }
/// Find function by hash, returns nullptr if not found
QFunctionDefinition* getFunction(dev::FixedHash<4> _hash);
private:
QList<QFunctionDefinition*> m_functions;
QFunctionDefinition* m_constructor;

34
mix/QFunctionDefinition.cpp

@ -28,29 +28,15 @@
using namespace dev::solidity;
using namespace dev::mix;
QFunctionDefinition::QFunctionDefinition(dev::solidity::FunctionDescription const& _f): QBasicNodeDefinition(_f.getDeclaration()), m_hash(dev::sha3(_f.getSignature()))
QFunctionDefinition::QFunctionDefinition(dev::solidity::FunctionTypePointer const& _f): QBasicNodeDefinition(&_f->getDeclaration()), m_hash(dev::sha3(_f->getCanonicalSignature()))
{
FunctionDefinition const* funcDef;
VariableDeclaration const* varDecl;
if ((funcDef = _f.getFunctionDefinition()))
{
std::vector<std::shared_ptr<VariableDeclaration>> parameters = funcDef->getParameterList().getParameters();
for (unsigned i = 0; i < parameters.size(); i++)
m_parameters.append(new QVariableDeclaration(parameters.at(i).get()));
std::vector<std::shared_ptr<VariableDeclaration>> returnParameters = funcDef->getReturnParameters();
for (unsigned i = 0; i < returnParameters.size(); i++)
m_returnParameters.append(new QVariableDeclaration(returnParameters.at(i).get()));
}
else
{
if (!(varDecl = _f.getVariableDeclaration()))
BOOST_THROW_EXCEPTION(Exception() << errinfo_comment("Malformed FunctionDescription. Should never happen."));
// only the return parameter for now.
// TODO: change this for other state variables like mapping and maybe abstract this inside solidity and not here
auto returnParams = _f.getReturnParameters();
m_returnParameters.append(new QVariableDeclaration(returnParams[0]));
}
auto paramNames = _f->getParameterNames();
auto paramTypes = _f->getParameterTypeNames();
auto returnNames = _f->getReturnParameterNames();
auto returnTypes = _f->getReturnParameterTypeNames();
for (unsigned i = 0; i < paramNames.size(); ++i)
m_parameters.append(new QVariableDeclaration(paramNames[i], paramTypes[i]));
for (unsigned i = 0; i < returnNames.size(); ++i)
m_returnParameters.append(new QVariableDeclaration(returnNames[i], returnTypes[i]));
}

2
mix/QFunctionDefinition.h

@ -39,7 +39,7 @@ class QFunctionDefinition: public QBasicNodeDefinition
public:
QFunctionDefinition() {}
QFunctionDefinition(solidity::FunctionDescription const& _f);
QFunctionDefinition(solidity::FunctionTypePointer const& _f);
/// Get all input parameters of this function.
QList<QVariableDeclaration*> const& parametersList() const { return m_parameters; }
/// Get all input parameters of this function as QML property.

2
mix/QVariableDeclaration.h

@ -39,7 +39,7 @@ class QVariableDeclaration: public QBasicNodeDefinition
public:
QVariableDeclaration() {}
QVariableDeclaration(solidity::VariableDeclaration const* _v): QBasicNodeDefinition(_v), m_type(QString::fromStdString(_v->getType()->toString())) {}
QVariableDeclaration(solidity::ParamDescription const& _v): QBasicNodeDefinition(_v.getName()), m_type(QString::fromStdString(_v.getType())) {}
QVariableDeclaration(std::string const& _name, std::string const& _type): QBasicNodeDefinition(_name), m_type(QString::fromStdString(_type)) {}
QString type() const { return m_type; }
private:

24
mix/Web3Server.cpp

@ -20,8 +20,9 @@
* Ethereum IDE client.
*/
#include <libdevcore/Exceptions.h>
#include <libdevcore/Log.h>
#include <libethereum/Interface.h>
#include "Web3Server.h"
using namespace dev::mix;
@ -53,3 +54,24 @@ void Web3Server::put(std::string const& _name, std::string const& _key, std::str
std::string k(_name + "/" + _key);
m_db[k] = _value;
}
Json::Value Web3Server::eth_changed(int const& _id)
{
cnote << "eth_changed(" << _id << ") ->" << client()->peekWatch(_id).size();
return WebThreeStubServerBase::eth_changed(_id);
}
std::string Web3Server::eth_transact(Json::Value const& _json)
{
std::string ret = WebThreeStubServerBase::eth_transact(_json);
emit newTransaction();
return ret;
}
std::string Web3Server::eth_call(Json::Value const& _json)
{
std::string ret = WebThreeStubServerBase::eth_call(_json);
emit newTransaction();
return ret;
}

13
mix/Web3Server.h

@ -24,6 +24,7 @@
#include <map>
#include <string>
#include <QObject>
#include <libweb3jsonrpc/WebThreeStubServerBase.h>
namespace dev
@ -32,11 +33,21 @@ namespace dev
namespace mix
{
class Web3Server: public dev::WebThreeStubServerBase, public dev::WebThreeStubDatabaseFace
class Web3Server: public QObject, public dev::WebThreeStubServerBase, public dev::WebThreeStubDatabaseFace
{
Q_OBJECT
public:
Web3Server(jsonrpc::AbstractServerConnector& _conn, std::vector<dev::KeyPair> const& _accounts, dev::eth::Interface* _client);
signals:
void newTransaction();
protected:
virtual Json::Value eth_changed(int const& _id) override;
virtual std::string eth_transact(Json::Value const& _json) override;
virtual std::string eth_call(Json::Value const& _json) override;
private:
dev::eth::Interface* client() override { return m_client; }
std::shared_ptr<dev::shh::Interface> face() override;

31
mix/qml/CallStack.qml

@ -0,0 +1,31 @@
import QtQuick 2.2
import QtQuick.Controls.Styles 1.1
import QtQuick.Controls 1.1
import QtQuick.Layouts 1.1
Item {
property alias model: callTable.model
signal frameActivated(int index)
ColumnLayout {
anchors.fill: parent
Text {
text: qsTr("Call Stack")
Layout.fillWidth: true
}
TableView {
id: callTable
Layout.fillWidth: true
Layout.fillHeight: true
headerDelegate: null
TableViewColumn {
role: "modelData"
title: qsTr("Address")
width: parent.width
}
onActivated: {
frameActivated(row);
}
}
}
}

27
mix/qml/ContractLibrary.qml

@ -0,0 +1,27 @@
import QtQuick 2.2
Item {
id: contractLibrary
property alias model: contractListModel;
Connections {
target: appContext
Component.onCompleted: {
//TODO: load a list, dependencies, ets, from external files
contractListModel.append({
name: "Config",
url: "qrc:///stdc/std.sol",
});
contractListModel.append({
name: "NameReg",
url: "qrc:///stdc/std.sol",
});
}
}
ListModel {
id: contractListModel
}
}

68
mix/qml/Debugger.qml

@ -13,13 +13,6 @@ Rectangle {
anchors.fill: parent;
color: "#ededed"
clip: true
Keys.onPressed:
{
if (event.key === Qt.Key_F10)
Debugger.moveSelection(1);
else if (event.key === Qt.Key_F9)
Debugger.moveSelection(-1);
}
onVisibleChanged:
{
@ -27,11 +20,11 @@ Rectangle {
forceActiveFocus();
}
function update(giveFocus)
function update(data, giveFocus)
{
if (statusPane.result.successful)
{
Debugger.init();
Debugger.init(data);
debugScrollArea.visible = true;
compilationErrorArea.visible = false;
machineStates.visible = true;
@ -50,9 +43,16 @@ Rectangle {
forceActiveFocus();
}
Connections {
target: clientModel
onDebugDataReady: {
update(_debugData, true);
}
}
Connections {
target: codeModel
onCompilationComplete: update(false)
onCompilationComplete: update(null, false);
}
Rectangle
@ -129,6 +129,12 @@ Rectangle {
anchors.fill: parent
Layout.fillWidth: true
Layout.fillHeight: true
TransactionLog {
Layout.fillWidth: true
height: 250
}
RowLayout {
// step button + slider
id: buttonRow
@ -153,6 +159,7 @@ Rectangle {
onClicked: Debugger.stepOutBack()
width: 28
height: 30
buttonShortcut: "Ctrl+Shift+F11"
buttonTooltip: qsTr("Step Out Back")
}
@ -164,6 +171,7 @@ Rectangle {
onClicked: Debugger.stepIntoBack()
width: 28
height: 30
buttonShortcut: "Ctrl+F11"
buttonTooltip: qsTr("Step Into Back")
}
@ -175,6 +183,7 @@ Rectangle {
onClicked: Debugger.stepOverBack()
width: 28
height: 30
buttonShortcut: "Ctrl+F10"
buttonTooltip: qsTr("Step Over Back")
}
@ -186,6 +195,7 @@ Rectangle {
onClicked: Debugger.stepOverForward()
width: 28
height: 30
buttonShortcut: "F10"
buttonTooltip: qsTr("Step Over Forward")
}
@ -197,6 +207,7 @@ Rectangle {
onClicked: Debugger.stepIntoForward()
width: 28
height: 30
buttonShortcut: "F11"
buttonTooltip: qsTr("Step Into Forward")
}
@ -208,6 +219,7 @@ Rectangle {
onClicked: Debugger.stepOutForward()
width: 28
height: 30
buttonShortcut: "Shift+F11"
buttonTooltip: qsTr("Step Out Forward")
}
}
@ -270,7 +282,8 @@ Rectangle {
id: statesList
delegate: renderDelegate
highlight: highlightBar
highlightFollowsCurrentItem: false
//highlightFollowsCurrentItem: false
model: ListModel {}
}
Component {
@ -281,9 +294,9 @@ Rectangle {
width: statesList.currentItem.width;
y: statesList.currentItem.y
color: "#4A90E2"
Behavior on y {
PropertyAnimation { properties: "y"; easing.type: Easing.InOutQuad; duration: 50}
}
//Behavior on y {
// PropertyAnimation { properties: "y"; easing.type: Easing.InOutQuad; duration: 50}
//}
}
}
@ -300,6 +313,7 @@ Rectangle {
width: 15
color: "#b2b3ae"
text: line.split(' ')[0]
font.family: "monospace"
font.pointSize: 9
id: id
wrapMode: Text.NoWrap
@ -307,6 +321,7 @@ Rectangle {
Text {
wrapMode: Text.NoWrap
color: parent.ListView.isCurrentItem ? "white" : "black"
font.family: "monospace"
text: line.replace(line.split(' ')[0], '')
anchors.left: id.right
font.pointSize: 9
@ -429,6 +444,24 @@ Rectangle {
orientation: Qt.Vertical
width: debugPanel.width - 2 * machineStates.sideMargin
Rectangle
{
id: callStackRect;
color: "transparent"
height: 120
width: parent.width
Layout.minimumHeight: 120
Layout.maximumHeight: 400
CallStack {
anchors.fill: parent
id: callStack
onFrameActivated: Debugger.displayFrame(index);
}
}
Rectangle
{
id: storageRect
@ -467,8 +500,10 @@ Rectangle {
font.family: "monospace"
anchors.leftMargin: 5
color: "#4a4a4a"
text: modelData.split(' ')[0].substring(0, 10);
text: modelData.split('\t')[0];
font.pointSize: 9
width: parent.width - 5
elide: Text.ElideRight
}
}
Rectangle
@ -488,7 +523,8 @@ Rectangle {
font.family: "monospace"
anchors.verticalCenter: parent.verticalCenter
color: "#4a4a4a"
text: modelData.split(' ')[1].substring(0, 10);
text: modelData.split('\t')[1];
elide: Text.ElideRight
font.pointSize: 9
}
}

31
mix/qml/Ether.qml

@ -10,7 +10,7 @@ import QtQuick.Controls 1.1
import QtQuick.Layouts 1.1
import QtQuick.Controls.Styles 1.1
Rectangle {
RowLayout {
id: etherEdition
property bool displayFormattedValue;
property bool edit;
@ -32,18 +32,9 @@ Rectangle {
units.currentIndex = unit;
}
RowLayout
{
anchors.fill: parent;
id: row
width: 200
height: parent.height
Rectangle
{
width : 200
color: edit ? "blue" : "white"
TextField
{
implicitWidth: 200
onTextChanged:
{
if (value !== undefined)
@ -52,19 +43,11 @@ Rectangle {
formattedValue.text = value.format();
}
}
width: parent.width
readOnly: !edit
visible: edit
id: etherValueEdit;
}
}
Rectangle
{
Layout.fillWidth: true
id: unitContainer
width: 20
anchors.verticalCenter: parent.verticalCenter
ComboBox
{
id: units
@ -99,17 +82,9 @@ Rectangle {
ListElement { text: "wei"; }
}
}
Rectangle
{
anchors.verticalCenter: parent.verticalCenter
anchors.left: units.right
visible: displayFormattedValue
width: 20
Text
{
visible: displayFormattedValue
id: formattedValue
}
}
}
}
}

15
mix/qml/ItemDelegateDataDump.qml

@ -49,21 +49,6 @@ Rectangle {
font.pointSize: 8
}
}
Rectangle
{
Layout.fillWidth: true
Layout.minimumWidth: 50
Layout.minimumHeight: parent.height
Text {
anchors.verticalCenter: parent.verticalCenter
anchors.horizontalCenter: parent.horizontalCenter
font.family: "monospace"
color: "#4a4a4a"
text: modelData[2]
font.pointSize: 8
}
}
}
Rectangle {

33
mix/qml/MainContent.qml

@ -35,39 +35,9 @@ Rectangle {
function startQuickDebugging()
{
var item = TransactionHelper.defaultTransaction();
item.executeConstructor = true;
if (codeModel.code.contract.constructor.parameters.length === 0)
{
ensureRightView();
startF5Debugging(item);
}
else
transactionDialog.open(0, item);
}
function startF5Debugging(transaction)
{
var ether = QEtherHelper.createEther("100000000000000000000000000", QEther.Wei);
var state = {
title: "",
balance: ether,
transactions: [transaction]
};
clientModel.debugState(state);
}
TransactionDialog {
id: transactionDialog
onAccepted: {
ensureRightView();
var item = transactionDialog.getItem();
item.executeConstructor = true;
startF5Debugging(item);
projectModel.stateListModel.debugDefaultState();
}
useTransactionDefaultValue: true
}
function toggleRightView() {
if (!rightView.visible)
@ -99,7 +69,6 @@ Rectangle {
codeWebSplitter.orientation = (codeWebSplitter.orientation === Qt.Vertical ? Qt.Horizontal : Qt.Vertical);
}
CodeEditorExtensionManager {
headerView: headerPaneTabs;
rightView: rightPaneTabs;

6
mix/qml/ProjectModel.qml

@ -17,6 +17,7 @@ Item {
signal documentAdded(var documentId)
signal projectSaving(var projectData)
signal projectSaved()
signal newProject(var projectData)
signal documentSaved(var documentId)
property bool isEmpty: (projectPath === "")
@ -27,6 +28,7 @@ Item {
property string projectTitle: ""
property string currentDocumentId: ""
property var listModel: projectListModel
property var stateListModel: projectStateListModel.model
//interface
function saveAll() { ProjectModelCode.saveAll(); }
@ -84,6 +86,10 @@ Item {
id: projectListModel
}
StateListModel {
id: projectStateListModel
}
Settings {
id: projectSettings
property string lastProjectPath;

13
mix/qml/StateDialog.qml

@ -17,11 +17,12 @@ Window {
property alias stateTitle: titleField.text
property alias stateBalance: balanceField.value
property alias isDefault: defaultCheckBox.checked
property int stateIndex
property var stateTransactions: []
signal accepted
function open(index, item) {
function open(index, item, setDefault) {
stateIndex = index;
stateTitle = item.title;
balanceField.value = item.balance;
@ -32,6 +33,7 @@ Window {
transactionsModel.append(item.transactions[t]);
stateTransactions.push(item.transactions[t]);
}
isDefault = setDefault;
visible = true;
titleField.focus = true;
}
@ -77,6 +79,14 @@ Window {
Layout.fillWidth: true
}
Label {
text: qsTr("Default")
}
CheckBox {
id: defaultCheckBox
Layout.fillWidth: true
}
Label {
text: qsTr("Transactions")
}
@ -152,6 +162,7 @@ Window {
}
ToolButton {
text: qsTr("Edit");
visible: !stdContract
Layout.fillHeight: true
onClicked: transactionsModel.editTransaction(index)
}

36
mix/qml/StateList.qml

@ -3,8 +3,6 @@ import QtQuick.Controls.Styles 1.1
import QtQuick.Controls 1.1
import QtQuick.Dialogs 1.1
import QtQuick.Layouts 1.1
import org.ethereum.qml.QEther 1.0
import "js/QEtherHelper.js" as QEtherHelper
Rectangle {
color: "#ededed"
@ -14,32 +12,13 @@ Rectangle {
anchors.left: parent.left
height: parent.height
width: parent.width
property var stateList: []
Connections {
target: projectModel
onProjectClosed: {
stateListModel.clear();
}
onProjectLoaded: {
if (!projectData.states)
projectData.states = [];
var items = projectData.states;
for(var i = 0; i < items.length; i++) {
stateListModel.append(items[i]);
stateList.push(items[i])
}
}
onProjectSaving: {
projectData.states = stateList;
}
}
ListView {
id: list
anchors.top: parent.top
height: parent.height
width: parent.width
model: stateListModel
model: projectModel.stateListModel
delegate: renderDelegate
}
@ -124,20 +103,17 @@ Rectangle {
ToolButton {
text: qsTr("Edit");
Layout.fillHeight: true
onClicked: stateListModel.editState(index);
onClicked: list.model.editState(index);
}
ToolButton {
text: qsTr("Delete");
Layout.fillHeight: true
onClicked: stateListModel.deleteState(index);
onClicked: list.model.deleteState(index);
}
ToolButton {
text: qsTr("Run");
Layout.fillHeight: true
onClicked:
{
stateListModel.runState(index)
}
onClicked: list.model.runState(index);
}
}
}
@ -148,7 +124,7 @@ Rectangle {
text: "&Add State"
shortcut: "Ctrl+T"
enabled: codeModel.hasContract && !clientModel.running;
onTriggered: stateListModel.addState();
onTriggered: list.model.addState();
}
}

196
mix/qml/StateListModel.qml

@ -0,0 +1,196 @@
import QtQuick 2.2
import QtQuick.Controls.Styles 1.1
import QtQuick.Controls 1.1
import QtQuick.Dialogs 1.1
import QtQuick.Layouts 1.1
import org.ethereum.qml.QEther 1.0
import "js/QEtherHelper.js" as QEtherHelper
Item {
property int defaultStateIndex: -1
property alias model: stateListModel
property var stateList: []
function fromPlainStateItem(s) {
return {
title: s.title,
balance: QEtherHelper.createEther(s.balance.value, s.balance.unit),
transactions: s.transactions.map(fromPlainTransactionItem)
};
}
function fromPlainTransactionItem(t) {
var r = {
functionId: t.functionId,
url: t.url,
value: QEtherHelper.createEther(t.value.value, t.value.unit),
gas: QEtherHelper.createEther(t.gas.value, t.gas.unit),
gasPrice: QEtherHelper.createEther(t.gasPrice.value, t.gasPrice.unit),
executeConstructor: t.executeConstructor,
stdContract: t.stdContract,
parameters: {}
};
for (var key in t.parameters) {
var intComponent = Qt.createComponent("qrc:/qml/BigIntValue.qml");
var param = intComponent.createObject();
param.setValue(t.parameters[key]);
r.parameters[key] = param;
}
return r;
}
function toPlainStateItem(s) {
return {
title: s.title,
balance: { value: s.balance.value, unit: s.balance.unit },
transactions: s.transactions.map(toPlainTransactionItem)
};
}
function toPlainTransactionItem(t) {
var r = {
functionId: t.functionId,
url: t.url,
value: { value: t.value.value, unit: t.value.unit },
gas: { value: t.gas.value, unit: t.gas.unit },
gasPrice: { value: t.gasPrice.value, unit: t.gasPrice.unit },
executeConstructor: t.executeConstructor,
stdContract: t.stdContract,
parameters: {}
};
for (var key in t.parameters)
r.parameters[key] = t.parameters[key];
return r;
}
Connections {
target: projectModel
onProjectClosed: {
stateListModel.clear();
stateList = [];
}
onProjectLoaded: {
if (!projectData.states)
projectData.states = [];
if (projectData.defaultStateIndex !== undefined)
defaultStateIndex = projectData.defaultStateIndex;
else
defaultStateIndex = -1;
var items = projectData.states;
for(var i = 0; i < items.length; i++) {
var item = fromPlainStateItem(items[i]);
stateListModel.append(item);
stateList.push(item);
}
}
onProjectSaving: {
projectData.states = []
for(var i = 0; i < stateListModel.count; i++) {
projectData.states.push(toPlainStateItem(stateList[i]));
}
projectData.defaultStateIndex = defaultStateIndex;
}
onNewProject: {
var state = toPlainStateItem(stateListModel.createDefaultState());
state.title = qsTr("Default");
projectData.states = [ state ];
projectData.defaultStateIndex = 0;
}
}
StateDialog {
id: stateDialog
onAccepted: {
var item = stateDialog.getItem();
if (stateDialog.stateIndex < stateListModel.count) {
if (stateDialog.isDefault)
defaultStateIndex = stateIndex;
stateList[stateDialog.stateIndex] = item;
stateListModel.set(stateDialog.stateIndex, item);
} else {
if (stateDialog.isDefault)
defaultStateIndex = 0;
stateList.push(item);
stateListModel.append(item);
}
stateListModel.save();
}
}
ContractLibrary {
id: contractLibrary;
}
ListModel {
id: stateListModel
function defaultTransactionItem() {
return {
value: QEtherHelper.createEther("100", QEther.Wei),
gas: QEtherHelper.createEther("125000", QEther.Wei),
gasPrice: QEtherHelper.createEther("10000000000000", QEther.Wei),
executeConstructor: false,
stdContract: false
};
}
function createDefaultState() {
var ether = QEtherHelper.createEther("100000000000000000000000000", QEther.Wei);
var item = {
title: "",
balance: ether,
transactions: []
};
//add all stdc contracts
for (var i = 0; i < contractLibrary.model.count; i++) {
var contractTransaction = defaultTransactionItem();
var contractItem = contractLibrary.model.get(i);
contractTransaction.url = contractItem.url;
contractTransaction.functionId = contractItem.name;
contractTransaction.stdContract = true;
item.transactions.push(contractTransaction);
};
//add constructor
var ctorTr = defaultTransactionItem();
ctorTr.executeConstructor = true;
ctorTr.functionId = qsTr("Constructor");
item.transactions.push(ctorTr);
return item;
}
function addState() {
var item = createDefaultState();
stateDialog.open(stateListModel.count, item, defaultStateIndex === -1);
}
function editState(index) {
stateDialog.open(index, stateList[index], defaultStateIndex === index);
}
function debugDefaultState() {
if (defaultStateIndex >= 0)
runState(defaultStateIndex);
}
function runState(index) {
var item = stateList[index];
clientModel.setupState(item);
}
function deleteState(index) {
stateListModel.remove(index);
stateList.splice(index, 1);
if (index === defaultStateIndex)
defaultStateIndex = -1;
save();
}
function save() {
projectModel.saveProject();
}
}
}

16
mix/qml/StatusPane.qml

@ -27,6 +27,22 @@ Rectangle {
debugRunActionIcon.enabled = statusPane.result.successful;
}
function infoMessage(text)
{
status.state = "";
status.text = text
logslink.visible = false;
}
Connections {
target:clientModel
onRunStarted: infoMessage(qsTr("Running transactions.."));
onRunFailed: infoMessage(qsTr("Error running transactions"));
onRunComplete: infoMessage(qsTr("Run complete"));
onNewBlock: infoMessage(qsTr("New block created"));
}
color: "transparent"
anchors.fill: parent
Rectangle {

2
mix/qml/StepActionImage.qml

@ -9,6 +9,7 @@ Rectangle {
property string disableStateImg
property string enabledStateImg
property string buttonTooltip
property string buttonShortcut
signal clicked
function enabled(state)
@ -33,6 +34,7 @@ Rectangle {
Action {
tooltip: buttonTooltip
id: buttonAction
shortcut: buttonShortcut
onTriggered: {
buttonActionContainer.clicked();
}

9
mix/qml/TransactionDialog.qml

@ -51,7 +51,7 @@ Window {
}
if (functionIndex == -1 && functionsModel.count > 0)
functionIndex = 0; //@todo suggest unused funtion
functionIndex = 0; //@todo suggest unused function
functionComboBox.currentIndex = functionIndex;
paramsModel.clear();
@ -258,19 +258,20 @@ Window {
Layout.maximumHeight: 600
TableViewColumn {
role: "name"
title: "Name"
title: qsTr("Name")
width: 120
}
TableViewColumn {
role: "type"
title: "Type"
title: qsTr("Type")
width: 120
}
TableViewColumn {
role: "value"
title: "Value"
title: qsTr("Value")
width: 240
}
rowDelegate: rowDelegate
itemDelegate: editableDelegate
}

84
mix/qml/TransactionLog.qml

@ -0,0 +1,84 @@
import QtQuick 2.2
import QtQuick.Controls.Styles 1.1
import QtQuick.Controls 1.1
import QtQuick.Dialogs 1.1
import QtQuick.Layouts 1.1
Item {
ColumnLayout {
anchors.fill: parent
CheckBox {
id: recording
text: qsTr("Record transactions");
checked: true
Layout.fillWidth: true
}
TableView {
Layout.fillWidth: true
Layout.fillHeight: true
model: logModel
TableViewColumn {
role: "block"
title: qsTr("Block")
width: 40
}
TableViewColumn {
role: "tindex"
title: qsTr("Index")
width: 40
}
TableViewColumn {
role: "contract"
title: qsTr("Contract")
width: 120
}
TableViewColumn {
role: "function"
title: qsTr("Function")
width: 120
}
TableViewColumn {
role: "value"
title: qsTr("Value")
width: 120
}
TableViewColumn {
role: "address"
title: qsTr("Address")
width: 120
}
TableViewColumn {
role: "returned"
title: qsTr("Returned")
width: 120
}
onActivated: {
var item = logModel.get(row);
clientModel.debugTransaction(item.block, item.tindex);
}
Keys.onPressed: {
if ((event.modifiers & Qt.ControlModifier) && event.key === Qt.Key_C && currentRow >=0 && currentRow < logModel.count) {
var item = logModel.get(currentRow);
appContext.toClipboard(item.returned);
}
}
}
}
ListModel {
id: logModel
}
Connections {
target: clientModel
onStateCleared: {
logModel.clear();
}
onNewTransaction: {
if (recording.checked)
logModel.append(_tr);
}
}
}

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

Loading…
Cancel
Save