Browse Source

Merge branch 'develop' of https://github.com/ethereum/cpp-ethereum into

mix_dbg

Conflicts:
	mix/QFunctionDefinition.cpp
cl-refactor
arkpar 10 years ago
parent
commit
1180ddbb5d
  1. 10
      alethzero/MainWin.cpp
  2. 3
      libdevcore/FixedHash.h
  3. 12
      libdevcrypto/Common.cpp
  4. 6
      libdevcrypto/Common.h
  5. 285
      libjsqrc/ethereumjs/dist/ethereum.js
  6. 14
      libjsqrc/ethereumjs/dist/ethereum.js.map
  7. 2
      libjsqrc/ethereumjs/dist/ethereum.min.js
  8. 1
      libjsqrc/ethereumjs/example/contract.html
  9. 67
      libjsqrc/ethereumjs/example/event.html
  10. 1
      libjsqrc/ethereumjs/example/natspec_contract.html
  11. 26
      libjsqrc/ethereumjs/lib/abi.js
  12. 178
      libjsqrc/ethereumjs/lib/contract.js
  13. 35
      libjsqrc/ethereumjs/lib/event.js
  14. 19
      libjsqrc/ethereumjs/lib/filter.js
  15. 6
      libjsqrc/ethereumjs/lib/providermanager.js
  16. 5
      libjsqrc/ethereumjs/lib/web3.js
  17. 49
      libjsqrc/ethereumjs/test/abi.filters.js
  18. 37
      libjsqrc/ethereumjs/test/abi.parsers.js
  19. 201
      libjsqrc/ethereumjs/test/eth.contract.js
  20. 1
      libjsqrc/ethereumjs/test/eth.methods.js
  21. 22
      libjsqrc/ethereumjs/test/event.js
  22. 3
      libjsqrc/ethereumjs/test/web3.methods.js
  23. 150
      libsolidity/AST.cpp
  24. 165
      libsolidity/AST.h
  25. 1
      libsolidity/ASTForward.h
  26. 12
      libsolidity/ASTPrinter.cpp
  27. 2
      libsolidity/ASTPrinter.h
  28. 4
      libsolidity/ASTVisitor.h
  29. 20
      libsolidity/AST_accept.h
  30. 4
      libsolidity/BaseTypes.h
  31. 20
      libsolidity/Compiler.cpp
  32. 56
      libsolidity/CompilerStack.cpp
  33. 4
      libsolidity/CompilerStack.h
  34. 109
      libsolidity/ExpressionCompiler.cpp
  35. 17
      libsolidity/ExpressionCompiler.h
  36. 45
      libsolidity/InterfaceHandler.cpp
  37. 8
      libsolidity/NameAndTypeResolver.cpp
  38. 1
      libsolidity/NameAndTypeResolver.h
  39. 65
      libsolidity/Parser.cpp
  40. 13
      libsolidity/Parser.h
  41. 2
      libsolidity/Token.h
  42. 69
      libsolidity/Types.cpp
  43. 28
      libsolidity/Types.h
  44. 24
      libweb3jsonrpc/WebThreeStubServerBase.cpp
  45. 24
      libwhisper/Common.cpp
  46. 20
      libwhisper/Common.h
  47. 3
      libwhisper/Interface.cpp
  48. 20
      libwhisper/Interface.h
  49. 66
      libwhisper/Message.cpp
  50. 28
      libwhisper/Message.h
  51. 11
      libwhisper/WhisperHost.cpp
  52. 8
      libwhisper/WhisperHost.h
  53. 2
      libwhisper/WhisperPeer.cpp
  54. 4
      libwhisper/WhisperPeer.h
  55. 5
      mix/QContractDefinition.cpp
  56. 37
      mix/QFunctionDefinition.cpp
  57. 2
      mix/QFunctionDefinition.h
  58. 2
      mix/QVariableDeclaration.h
  59. 9
      test/SolidityABIJSON.cpp
  60. 10
      test/SolidityCompiler.cpp
  61. 84
      test/SolidityEndToEndTest.cpp
  62. 107
      test/SolidityNameAndTypeResolution.cpp
  63. 35
      test/SolidityParser.cpp
  64. 1
      test/solidityExecutionFramework.h
  65. 16
      test/whisperTopic.cpp

10
alethzero/MainWin.cpp

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

3
libdevcore/FixedHash.h

@ -67,6 +67,9 @@ public:
/// Explicitly construct, copying from a byte array. /// 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)); } 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. /// Explicitly construct, copying from a bytes in memory with given pointer.
explicit FixedHash(byte const* _bs, ConstructFromPointerType) { memcpy(m_data.data(), _bs, N); } 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; 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) Public dev::recover(Signature const& _sig, h256 const& _message)
{ {
return s_secp256k1.recover(_sig, _message.ref()); 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. /// Decrypts cipher using Secret key.
bool decrypt(Secret const& _k, bytesConstRef _cipher, bytes& o_plaintext); 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. /// Recovers Public key from signed message hash.
Public recover(Signature const& _sig, h256 const& _hash); Public recover(Signature const& _sig, h256 const& _hash);

285
libjsqrc/ethereumjs/dist/ethereum.js

@ -66,6 +66,22 @@ var getMethodWithName = function (json, methodName) {
return json[index]; return json[index];
}; };
/// 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';
});
};
/// @param string string to be padded /// @param string string to be padded
/// @param number of characters that result string should have /// @param number of characters that result string should have
/// @param sign, by default 0 /// @param sign, by default 0
@ -212,6 +228,7 @@ var signedIsNegative = function (value) {
/// Formats input right-aligned input bytes to int /// Formats input right-aligned input bytes to int
/// @returns right-aligned input bytes formatted to int /// @returns right-aligned input bytes formatted to int
var formatOutputInt = function (value) { var formatOutputInt = function (value) {
value = value || "0";
// check if it's negative number // check if it's negative number
// it it is, return two's complement // it it is, return two's complement
if (signedIsNegative(value)) { if (signedIsNegative(value)) {
@ -223,6 +240,7 @@ var formatOutputInt = function (value) {
/// Formats big right-aligned input bytes to uint /// Formats big right-aligned input bytes to uint
/// @returns right-aligned input bytes formatted to uint /// @returns right-aligned input bytes formatted to uint
var formatOutputUInt = function (value) { var formatOutputUInt = function (value) {
value = value || "0";
return new BigNumber(value, 16); return new BigNumber(value, 16);
}; };
@ -350,7 +368,7 @@ var methodTypeName = function (method) {
/// @returns input parser object for given json abi /// @returns input parser object for given json abi
var inputParser = function (json) { var inputParser = function (json) {
var parser = {}; var parser = {};
json.forEach(function (method) { filterFunctions(json).forEach(function (method) {
var displayName = methodDisplayName(method.name); var displayName = methodDisplayName(method.name);
var typeName = methodTypeName(method.name); var typeName = methodTypeName(method.name);
@ -373,7 +391,7 @@ var inputParser = function (json) {
/// @returns output parser for given json abi /// @returns output parser for given json abi
var outputParser = function (json) { var outputParser = function (json) {
var parser = {}; var parser = {};
json.forEach(function (method) { filterFunctions(json).forEach(function (method) {
var displayName = methodDisplayName(method.name); var displayName = methodDisplayName(method.name);
var typeName = methodTypeName(method.name); var typeName = methodTypeName(method.name);
@ -404,11 +422,13 @@ module.exports = {
methodSignature: methodSignature, methodSignature: methodSignature,
methodDisplayName: methodDisplayName, methodDisplayName: methodDisplayName,
methodTypeName: methodTypeName, methodTypeName: methodTypeName,
getMethodWithName: getMethodWithName getMethodWithName: getMethodWithName,
filterFunctions: filterFunctions,
filterEvents: filterEvents
}; };
},{"./web3":7}],2:[function(require,module,exports){ },{"./web3":8}],2:[function(require,module,exports){
/* /*
This file is part of ethereum.js. This file is part of ethereum.js.
@ -431,71 +451,40 @@ module.exports = {
* @date 2014 * @date 2014
*/ */
var web3 = require('./web3'); // jshint ignore:line var web3 = require('./web3');
var abi = require('./abi'); var abi = require('./abi');
var eventImpl = require('./event');
/** var addFunctionRelatedPropertiesToContract = function (contract) {
* 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) { contract.call = function (options) {
// workaround for invalid assumption that method.name is the full anonymous prototype of the method. contract._isTransact = false;
// it's not. it's just the name. the rest of the code assumes it's actually the anonymous contract._options = options;
// prototype, so we make it so as a workaround. return contract;
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 result = {};
result.call = function (options) {
result._isTransact = false;
result._options = options;
return result;
}; };
result.transact = function (options) { contract.transact = function (options) {
result._isTransact = true; contract._isTransact = true;
result._options = options; contract._options = options;
return result; return contract;
}; };
result._options = {}; contract._options = {};
['gas', 'gasPrice', 'value', 'from'].forEach(function(p) { ['gas', 'gasPrice', 'value', 'from'].forEach(function(p) {
result[p] = function (v) { contract[p] = function (v) {
result._options[p] = v; contract._options[p] = v;
return result; 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
abi.filterFunctions(desc).forEach(function (method) {
var displayName = abi.methodDisplayName(method.name); var displayName = abi.methodDisplayName(method.name);
var typeName = abi.methodTypeName(method.name); var typeName = abi.methodTypeName(method.name);
@ -505,16 +494,16 @@ var contract = function (address, desc) {
var signature = abi.methodSignature(method.name); var signature = abi.methodSignature(method.name);
var parsed = inputParser[displayName][typeName].apply(null, params); var parsed = inputParser[displayName][typeName].apply(null, params);
var options = result._options || {}; var options = contract._options || {};
options.to = address; options.to = address;
options.data = signature + parsed; 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; var collapse = options.collapse !== false;
// reset // reset
result._options = {}; contract._options = {};
result._isTransact = null; contract._isTransact = null;
if (isTransact) { if (isTransact) {
// it's used byt natspec.js // it's used byt natspec.js
@ -541,21 +530,147 @@ var contract = function (address, desc) {
return ret; return ret;
}; };
if (result[displayName] === undefined) { if (contract[displayName] === undefined) {
result[displayName] = impl; 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 abi.filterEvents(desc).map(function (e) {
return abi.methodSignature(e.name);
});
}
}); });
};
var addEventsToContract = function (contract, desc, address) {
// create contract events
abi.filterEvents(desc).forEach(function (e) {
var impl = function () {
var params = Array.prototype.slice.call(arguments);
var signature = abi.methodSignature(e.name);
var event = eventImpl(address, signature);
var o = event.apply(null, params);
return web3.eth.watch(o);
};
impl.address = address;
Object.defineProperty(impl, 'topic', {
get: function() {
return [abi.methodSignature(e.name)];
}
});
// TODO: rename these methods, cause they are used not only for methods
var displayName = abi.methodDisplayName(e.name);
var typeName = abi.methodTypeName(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; return result;
}; };
module.exports = contract; module.exports = contract;
},{"./abi":1,"./web3":7}],3:[function(require,module,exports){ },{"./abi":1,"./event":3,"./web3":8}],3:[function(require,module,exports){
/*
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 implementationOfEvent = function (address, signature) {
return function (options) {
var o = options || {};
o.address = o.address || address;
o.topics = o.topics || [];
o.topics.push(signature);
return o;
};
};
module.exports = implementationOfEvent;
},{}],4:[function(require,module,exports){
/* /*
This file is part of ethereum.js. This file is part of ethereum.js.
@ -589,6 +704,23 @@ var Filter = function(options, impl) {
this.impl = impl; this.impl = impl;
this.callbacks = []; this.callbacks = [];
if (typeof options !== "string") {
// evaluate lazy properties
if (options.topics) {
console.warn('"topics" is deprecated, use "topic" instead');
}
options = {
to: options.to,
topic: options.topic,
earliest: options.earliest,
latest: options.latest,
max: options.max,
skip: options.skip,
address: options.address
};
}
this.id = impl.newFilter(options); this.id = impl.newFilter(options);
web3.provider.startPolling({call: impl.changed, args: [this.id]}, this.id, this.trigger.bind(this)); web3.provider.startPolling({call: impl.changed, args: [this.id]}, this.id, this.trigger.bind(this));
}; };
@ -606,7 +738,7 @@ Filter.prototype.changed = function(callback) {
/// trigger calling new message from people /// trigger calling new message from people
Filter.prototype.trigger = function(messages) { Filter.prototype.trigger = function(messages) {
for (var i = 0; i < this.callbacks.length; i++) { 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]); this.callbacks[i].call(this, messages[j]);
} }
} }
@ -630,7 +762,7 @@ Filter.prototype.logs = function () {
module.exports = Filter; module.exports = Filter;
},{"./web3":7}],4:[function(require,module,exports){ },{"./web3":8}],5:[function(require,module,exports){
/* /*
This file is part of ethereum.js. This file is part of ethereum.js.
@ -702,7 +834,7 @@ HttpSyncProvider.prototype.send = function (payload) {
module.exports = HttpSyncProvider; module.exports = HttpSyncProvider;
},{}],5:[function(require,module,exports){ },{}],6:[function(require,module,exports){
/* /*
This file is part of ethereum.js. This file is part of ethereum.js.
@ -781,6 +913,12 @@ ProviderManager.prototype.send = function(data) {
//TODO: handle error here? //TODO: handle error here?
var result = this.provider.send(data); var result = this.provider.send(data);
result = JSON.parse(result); result = JSON.parse(result);
if (result.error) {
console.log(result.error);
return null;
}
return result.result; return result.result;
}; };
@ -808,7 +946,7 @@ ProviderManager.prototype.stopPolling = function (pollId) {
module.exports = ProviderManager; module.exports = ProviderManager;
},{"./web3":7}],6:[function(require,module,exports){ },{"./web3":8}],7:[function(require,module,exports){
/* /*
This file is part of ethereum.js. This file is part of ethereum.js.
@ -842,7 +980,7 @@ QtSyncProvider.prototype.send = function (payload) {
module.exports = QtSyncProvider; module.exports = QtSyncProvider;
},{}],7:[function(require,module,exports){ },{}],8:[function(require,module,exports){
/* /*
This file is part of ethereum.js. This file is part of ethereum.js.
@ -943,7 +1081,6 @@ var ethProperties = function () {
{ name: 'listening', getter: 'eth_listening', setter: 'eth_setListening' }, { name: 'listening', getter: 'eth_listening', setter: 'eth_setListening' },
{ name: 'mining', getter: 'eth_mining', setter: 'eth_setMining' }, { name: 'mining', getter: 'eth_mining', setter: 'eth_setMining' },
{ name: 'gasPrice', getter: 'eth_gasPrice' }, { name: 'gasPrice', getter: 'eth_gasPrice' },
{ name: 'account', getter: 'eth_account' },
{ name: 'accounts', getter: 'eth_accounts' }, { name: 'accounts', getter: 'eth_accounts' },
{ name: 'peerCount', getter: 'eth_peerCount' }, { name: 'peerCount', getter: 'eth_peerCount' },
{ name: 'defaultBlock', getter: 'eth_defaultBlock', setter: 'eth_setDefaultBlock' }, { name: 'defaultBlock', getter: 'eth_defaultBlock', setter: 'eth_setDefaultBlock' },
@ -1078,7 +1215,9 @@ var web3 = {
/// @returns decimal representaton of hex value prefixed by 0x /// @returns decimal representaton of hex value prefixed by 0x
toDecimal: function (val) { 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 /// @returns hex representation (prefixed by 0x) of decimal value
@ -1183,7 +1322,7 @@ web3.abi = require('./lib/abi');
module.exports = web3; module.exports = web3;
},{"./lib/abi":1,"./lib/contract":2,"./lib/filter":3,"./lib/httpsync":4,"./lib/providermanager":5,"./lib/qtsync":6,"./lib/web3":7}]},{},["web3"]) },{"./lib/abi":1,"./lib/contract":2,"./lib/filter":4,"./lib/httpsync":5,"./lib/providermanager":6,"./lib/qtsync":7,"./lib/web3":8}]},{},["web3"])
//# sourceMappingURL=ethereum.js.map //# sourceMappingURL=ethereum.js.map

14
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 // contract description, this will be autogenerated somehow
var desc = [{ var desc = [{
"name": "multiply(uint256)", "name": "multiply(uint256)",
"type": "function",
"inputs": [ "inputs": [
{ {
"name": "a", "name": "a",

67
libjsqrc/ethereumjs/example/event.html

@ -0,0 +1,67 @@
<!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() {
web3.eth.watch(contract).changed(function (res) {
});
};
function test2() {
web3.eth.watch(contract.Event).changed(function (res) {
});
};
function test3() {
contract.Event().changed(function (res) {
});
};
// not valid
// function test4() {
// 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>
</body>
</html>

1
libjsqrc/ethereumjs/example/natspec_contract.html

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

26
libjsqrc/ethereumjs/lib/abi.js

@ -65,6 +65,22 @@ var getMethodWithName = function (json, methodName) {
return json[index]; return json[index];
}; };
/// 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';
});
};
/// @param string string to be padded /// @param string string to be padded
/// @param number of characters that result string should have /// @param number of characters that result string should have
/// @param sign, by default 0 /// @param sign, by default 0
@ -211,6 +227,7 @@ var signedIsNegative = function (value) {
/// Formats input right-aligned input bytes to int /// Formats input right-aligned input bytes to int
/// @returns right-aligned input bytes formatted to int /// @returns right-aligned input bytes formatted to int
var formatOutputInt = function (value) { var formatOutputInt = function (value) {
value = value || "0";
// check if it's negative number // check if it's negative number
// it it is, return two's complement // it it is, return two's complement
if (signedIsNegative(value)) { if (signedIsNegative(value)) {
@ -222,6 +239,7 @@ var formatOutputInt = function (value) {
/// Formats big right-aligned input bytes to uint /// Formats big right-aligned input bytes to uint
/// @returns right-aligned input bytes formatted to uint /// @returns right-aligned input bytes formatted to uint
var formatOutputUInt = function (value) { var formatOutputUInt = function (value) {
value = value || "0";
return new BigNumber(value, 16); return new BigNumber(value, 16);
}; };
@ -349,7 +367,7 @@ var methodTypeName = function (method) {
/// @returns input parser object for given json abi /// @returns input parser object for given json abi
var inputParser = function (json) { var inputParser = function (json) {
var parser = {}; var parser = {};
json.forEach(function (method) { filterFunctions(json).forEach(function (method) {
var displayName = methodDisplayName(method.name); var displayName = methodDisplayName(method.name);
var typeName = methodTypeName(method.name); var typeName = methodTypeName(method.name);
@ -372,7 +390,7 @@ var inputParser = function (json) {
/// @returns output parser for given json abi /// @returns output parser for given json abi
var outputParser = function (json) { var outputParser = function (json) {
var parser = {}; var parser = {};
json.forEach(function (method) { filterFunctions(json).forEach(function (method) {
var displayName = methodDisplayName(method.name); var displayName = methodDisplayName(method.name);
var typeName = methodTypeName(method.name); var typeName = methodTypeName(method.name);
@ -403,6 +421,8 @@ module.exports = {
methodSignature: methodSignature, methodSignature: methodSignature,
methodDisplayName: methodDisplayName, methodDisplayName: methodDisplayName,
methodTypeName: methodTypeName, methodTypeName: methodTypeName,
getMethodWithName: getMethodWithName getMethodWithName: getMethodWithName,
filterFunctions: filterFunctions,
filterEvents: filterEvents
}; };

178
libjsqrc/ethereumjs/lib/contract.js

@ -20,71 +20,40 @@
* @date 2014 * @date 2014
*/ */
var web3 = require('./web3'); // jshint ignore:line var web3 = require('./web3');
var abi = require('./abi'); var abi = require('./abi');
var eventImpl = require('./event');
/** var addFunctionRelatedPropertiesToContract = function (contract) {
* 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) { contract.call = function (options) {
// workaround for invalid assumption that method.name is the full anonymous prototype of the method. contract._isTransact = false;
// it's not. it's just the name. the rest of the code assumes it's actually the anonymous contract._options = options;
// prototype, so we make it so as a workaround. return contract;
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 result = {};
result.call = function (options) {
result._isTransact = false;
result._options = options;
return result;
}; };
result.transact = function (options) { contract.transact = function (options) {
result._isTransact = true; contract._isTransact = true;
result._options = options; contract._options = options;
return result; return contract;
}; };
result._options = {}; contract._options = {};
['gas', 'gasPrice', 'value', 'from'].forEach(function(p) { ['gas', 'gasPrice', 'value', 'from'].forEach(function(p) {
result[p] = function (v) { contract[p] = function (v) {
result._options[p] = v; contract._options[p] = v;
return result; return contract;
}; };
}); });
};
var addFunctionsToContract = function (contract, desc, address) {
var inputParser = abi.inputParser(desc);
var outputParser = abi.outputParser(desc);
desc.forEach(function (method) { // create contract functions
abi.filterFunctions(desc).forEach(function (method) {
var displayName = abi.methodDisplayName(method.name); var displayName = abi.methodDisplayName(method.name);
var typeName = abi.methodTypeName(method.name); var typeName = abi.methodTypeName(method.name);
@ -94,16 +63,16 @@ var contract = function (address, desc) {
var signature = abi.methodSignature(method.name); var signature = abi.methodSignature(method.name);
var parsed = inputParser[displayName][typeName].apply(null, params); var parsed = inputParser[displayName][typeName].apply(null, params);
var options = result._options || {}; var options = contract._options || {};
options.to = address; options.to = address;
options.data = signature + parsed; 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; var collapse = options.collapse !== false;
// reset // reset
result._options = {}; contract._options = {};
result._isTransact = null; contract._isTransact = null;
if (isTransact) { if (isTransact) {
// it's used byt natspec.js // it's used byt natspec.js
@ -130,14 +99,103 @@ var contract = function (address, desc) {
return ret; return ret;
}; };
if (result[displayName] === undefined) { if (contract[displayName] === undefined) {
result[displayName] = impl; 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 abi.filterEvents(desc).map(function (e) {
return abi.methodSignature(e.name);
});
}
}); });
};
var addEventsToContract = function (contract, desc, address) {
// create contract events
abi.filterEvents(desc).forEach(function (e) {
var impl = function () {
var params = Array.prototype.slice.call(arguments);
var signature = abi.methodSignature(e.name);
var event = eventImpl(address, signature);
var o = event.apply(null, params);
return web3.eth.watch(o);
};
impl.address = address;
Object.defineProperty(impl, 'topic', {
get: function() {
return [abi.methodSignature(e.name)];
}
});
// TODO: rename these methods, cause they are used not only for methods
var displayName = abi.methodDisplayName(e.name);
var typeName = abi.methodTypeName(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; return result;
}; };

35
libjsqrc/ethereumjs/lib/event.js

@ -0,0 +1,35 @@
/*
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 implementationOfEvent = function (address, signature) {
return function (options) {
var o = options || {};
o.address = o.address || address;
o.topics = o.topics || [];
o.topics.push(signature);
return o;
};
};
module.exports = implementationOfEvent;

19
libjsqrc/ethereumjs/lib/filter.js

@ -31,6 +31,23 @@ var Filter = function(options, impl) {
this.impl = impl; this.impl = impl;
this.callbacks = []; this.callbacks = [];
if (typeof options !== "string") {
// evaluate lazy properties
if (options.topics) {
console.warn('"topics" is deprecated, use "topic" instead');
}
options = {
to: options.to,
topic: options.topic,
earliest: options.earliest,
latest: options.latest,
max: options.max,
skip: options.skip,
address: options.address
};
}
this.id = impl.newFilter(options); this.id = impl.newFilter(options);
web3.provider.startPolling({call: impl.changed, args: [this.id]}, this.id, this.trigger.bind(this)); web3.provider.startPolling({call: impl.changed, args: [this.id]}, this.id, this.trigger.bind(this));
}; };
@ -48,7 +65,7 @@ Filter.prototype.changed = function(callback) {
/// trigger calling new message from people /// trigger calling new message from people
Filter.prototype.trigger = function(messages) { Filter.prototype.trigger = function(messages) {
for (var i = 0; i < this.callbacks.length; i++) { 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]); this.callbacks[i].call(this, messages[j]);
} }
} }

6
libjsqrc/ethereumjs/lib/providermanager.js

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

5
libjsqrc/ethereumjs/lib/web3.js

@ -98,7 +98,6 @@ var ethProperties = function () {
{ name: 'listening', getter: 'eth_listening', setter: 'eth_setListening' }, { name: 'listening', getter: 'eth_listening', setter: 'eth_setListening' },
{ name: 'mining', getter: 'eth_mining', setter: 'eth_setMining' }, { name: 'mining', getter: 'eth_mining', setter: 'eth_setMining' },
{ name: 'gasPrice', getter: 'eth_gasPrice' }, { name: 'gasPrice', getter: 'eth_gasPrice' },
{ name: 'account', getter: 'eth_account' },
{ name: 'accounts', getter: 'eth_accounts' }, { name: 'accounts', getter: 'eth_accounts' },
{ name: 'peerCount', getter: 'eth_peerCount' }, { name: 'peerCount', getter: 'eth_peerCount' },
{ name: 'defaultBlock', getter: 'eth_defaultBlock', setter: 'eth_setDefaultBlock' }, { name: 'defaultBlock', getter: 'eth_defaultBlock', setter: 'eth_setDefaultBlock' },
@ -233,7 +232,9 @@ var web3 = {
/// @returns decimal representaton of hex value prefixed by 0x /// @returns decimal representaton of hex value prefixed by 0x
toDecimal: function (val) { 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 /// @returns hex representation (prefixed by 0x) of decimal value

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

@ -0,0 +1,49 @@
var assert = require('assert');
var abi = require('../lib/abi.js');
describe('abi', 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 = abi.filterEvents(description);
var functions = abi.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');
});
});

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

@ -5,6 +5,7 @@ var clone = function (object) { return JSON.parse(JSON.stringify(object)); };
var description = [{ var description = [{
"name": "test", "name": "test",
"type": "function",
"inputs": [{ "inputs": [{
"name": "a", "name": "a",
"type": "uint256" "type": "uint256"
@ -339,10 +340,12 @@ describe('abi', function() {
// given // given
var d = [{ var d = [{
name: "test", name: "test",
type: "function",
inputs: [{ type: "int" }], inputs: [{ type: "int" }],
outputs: [{ type: "int" }] outputs: [{ type: "int" }]
},{ },{
name: "test2", name: "test2",
type: "function",
inputs: [{ type: "string" }], inputs: [{ type: "string" }],
outputs: [{ type: "string" }] outputs: [{ type: "string" }]
}]; }];
@ -775,10 +778,12 @@ describe('abi', function() {
// given // given
var d = [{ var d = [{
name: "test", name: "test",
type: "function",
inputs: [{ type: "int" }], inputs: [{ type: "int" }],
outputs: [{ type: "int" }] outputs: [{ type: "int" }]
},{ },{
name: "test2", name: "test2",
type: "function",
inputs: [{ type: "string" }], inputs: [{ type: "string" }],
outputs: [{ 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, 'listening');
u.propertyExists(web3.eth, 'mining'); u.propertyExists(web3.eth, 'mining');
u.propertyExists(web3.eth, 'gasPrice'); u.propertyExists(web3.eth, 'gasPrice');
u.propertyExists(web3.eth, 'account');
u.propertyExists(web3.eth, 'accounts'); u.propertyExists(web3.eth, 'accounts');
u.propertyExists(web3.eth, 'peerCount'); u.propertyExists(web3.eth, 'peerCount');
u.propertyExists(web3.eth, 'defaultBlock'); u.propertyExists(web3.eth, 'defaultBlock');

22
libjsqrc/ethereumjs/test/event.js

@ -0,0 +1,22 @@
var assert = require('assert');
var event = require('../lib/event.js');
describe('event', function () {
it('should create filter input object from given', function () {
// given
var address = '0x012345';
var signature = '0x987654';
// when
var impl = event(address, signature);
var result = impl();
// then
assert.equal(result.address, address);
assert.equal(result.topics.length, 1);
assert.equal(result.topics[0], signature);
});
});

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

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

150
libsolidity/AST.cpp

@ -56,7 +56,12 @@ void ContractDefinition::checkTypeRequirements()
FunctionDefinition const* constructor = getConstructor(); FunctionDefinition const* constructor = getConstructor();
if (constructor && !constructor->getReturnParameters().empty()) if (constructor && !constructor->getReturnParameters().empty())
BOOST_THROW_EXCEPTION(constructor->getReturnParameterList()->createTypeError( BOOST_THROW_EXCEPTION(constructor->getReturnParameterList()->createTypeError(
"Non-empty \"returns\" directive for constructor.")); "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()) for (ASTPointer<ModifierDefinition> const& modifier: getFunctionModifiers())
modifier->checkTypeRequirements(); modifier->checkTypeRequirements();
@ -66,24 +71,24 @@ void ContractDefinition::checkTypeRequirements()
// check for hash collisions in function signatures // check for hash collisions in function signatures
set<FixedHash<4>> hashes; 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)) if (hashes.count(hash))
BOOST_THROW_EXCEPTION(createTypeError( BOOST_THROW_EXCEPTION(createTypeError(
std::string("Function signature hash collision for ") + std::string("Function signature hash collision for ") +
std::get<1>(hashAndFunction)->getCanonicalSignature(std::get<2>(hashAndFunction)->getName()))); it.second->getCanonicalSignature()));
hashes.insert(hash); hashes.insert(hash);
} }
} }
map<FixedHash<4>, FunctionDescription> ContractDefinition::getInterfaceFunctions() const map<FixedHash<4>, FunctionTypePointer> ContractDefinition::getInterfaceFunctions() const
{ {
auto exportedFunctionList = getInterfaceFunctionList(); auto exportedFunctionList = getInterfaceFunctionList();
map<FixedHash<4>, FunctionDescription> exportedFunctions; map<FixedHash<4>, FunctionTypePointer> exportedFunctions;
for (auto const& it: exportedFunctionList) 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(), solAssert(exportedFunctionList.size() == exportedFunctions.size(),
"Hash collision at Function Definition Hash calculation"); "Hash collision at Function Definition Hash calculation");
@ -99,6 +104,15 @@ FunctionDefinition const* ContractDefinition::getConstructor() const
return nullptr; 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 void ContractDefinition::checkIllegalOverrides() const
{ {
// TODO unify this at a later point. for this we need to put the constness and the access specifier // TODO unify this at a later point. for this we need to put the constness and the access specifier
@ -138,20 +152,20 @@ void ContractDefinition::checkIllegalOverrides() const
} }
} }
vector<tuple<FixedHash<4>, std::shared_ptr<FunctionType const>, Declaration const*>> const& ContractDefinition::getInterfaceFunctionList() const vector<pair<FixedHash<4>, FunctionTypePointer>> const& ContractDefinition::getInterfaceFunctionList() const
{ {
if (!m_interfaceFunctionList) if (!m_interfaceFunctionList)
{ {
set<string> functionsSeen; 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 (ContractDefinition const* contract: getLinearizedBaseContracts())
{ {
for (ASTPointer<FunctionDefinition> const& f: contract->getDefinedFunctions()) 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()); functionsSeen.insert(f->getName());
FixedHash<4> hash(dev::sha3(f->getCanonicalSignature())); 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()) for (ASTPointer<VariableDeclaration> const& v: contract->getStateVariables())
@ -160,7 +174,7 @@ vector<tuple<FixedHash<4>, std::shared_ptr<FunctionType const>, Declaration cons
FunctionType ftype(*v); FunctionType ftype(*v);
functionsSeen.insert(v->getName()); functionsSeen.insert(v->getName());
FixedHash<4> hash(dev::sha3(ftype.getCanonicalSignature(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 +285,20 @@ void ModifierInvocation::checkTypeRequirements()
BOOST_THROW_EXCEPTION(createTypeError("Invalid type for argument in modifier invocation.")); 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() void Block::checkTypeRequirements()
{ {
for (shared_ptr<Statement> const& statement: m_statements) for (shared_ptr<Statement> const& statement: m_statements)
@ -519,103 +547,5 @@ void Literal::checkTypeRequirements()
BOOST_THROW_EXCEPTION(createTypeError("Invalid literal value.")); 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();
}
} }
} }

165
libsolidity/AST.h

@ -156,74 +156,43 @@ private:
Declaration const* m_scope; Declaration const* m_scope;
}; };
/** /**
* Generic Parameter description used by @see FunctionDescription to return * Abstract class that is added to each AST node that can store local variables.
* a descripton of its parameters.
*/ */
struct ParamDescription class VariableScope
{ {
ParamDescription(std::string const& _name, std::string const& _type): public:
m_description(_name, _type){} void addLocalVariable(VariableDeclaration const& _localVariable) { m_localVariables.push_back(&_localVariable); }
std::vector<VariableDeclaration const*> const& getLocalVariables() const { return m_localVariables; }
std::string const& getName() const;
std::string const& getType() const;
std::pair<std::string, std::string> m_description; private:
std::vector<VariableDeclaration const*> m_localVariables;
}; };
/** /**
* Generic function description able to describe both normal functions and * Abstract class that is added to each AST node that can receive documentation.
* functions that should be made as accessors to state variables
*/ */
struct FunctionDescription class Documented
{ {
FunctionDescription(std::shared_ptr<FunctionType const> _type, Declaration const* _decl): public:
m_description(_type, _decl){} explicit Documented(ASTPointer<ASTString> const& _documentation): m_documentation(_documentation) {}
/// constructor for a constructor's function definition. Used only inside mix.
FunctionDescription(Declaration const* _def):
m_description(nullptr, _def){}
FunctionDescription(): /// @return A shared pointer of an ASTString.
m_description(nullptr, nullptr){} /// Can contain a nullptr in which case indicates absence of documentation
ASTPointer<ASTString> const& getDocumentation() const { return m_documentation; }
/// @returns the natspec documentation of the function if existing. Accessor (for now) don't have natspec doc protected:
ASTPointer<ASTString> getDocumentation() const; ASTPointer<ASTString> m_documentation;
/// @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;
}; };
/// @}
/** /**
* Definition of a contract. This is the only AST nodes where child nodes are not visited in * 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 * document order. It first visits all struct declarations, then all variable declarations and
* finally all function declarations. * finally all function declarations.
*/ */
class ContractDefinition: public Declaration class ContractDefinition: public Declaration, public Documented
{ {
public: public:
ContractDefinition(Location const& _location, ContractDefinition(Location const& _location,
@ -233,14 +202,15 @@ public:
std::vector<ASTPointer<StructDefinition>> const& _definedStructs, std::vector<ASTPointer<StructDefinition>> const& _definedStructs,
std::vector<ASTPointer<VariableDeclaration>> const& _stateVariables, std::vector<ASTPointer<VariableDeclaration>> const& _stateVariables,
std::vector<ASTPointer<FunctionDefinition>> const& _definedFunctions, std::vector<ASTPointer<FunctionDefinition>> const& _definedFunctions,
std::vector<ASTPointer<ModifierDefinition>> const& _functionModifiers): std::vector<ASTPointer<ModifierDefinition>> const& _functionModifiers,
Declaration(_location, _name), std::vector<ASTPointer<EventDefinition>> const& _events):
Declaration(_location, _name), Documented(_documentation),
m_baseContracts(_baseContracts), m_baseContracts(_baseContracts),
m_definedStructs(_definedStructs), m_definedStructs(_definedStructs),
m_stateVariables(_stateVariables), m_stateVariables(_stateVariables),
m_definedFunctions(_definedFunctions), m_definedFunctions(_definedFunctions),
m_functionModifiers(_functionModifiers), m_functionModifiers(_functionModifiers),
m_documentation(_documentation) m_events(_events)
{} {}
virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTVisitor& _visitor) override;
@ -251,6 +221,7 @@ public:
std::vector<ASTPointer<VariableDeclaration>> const& getStateVariables() const { return m_stateVariables; } std::vector<ASTPointer<VariableDeclaration>> const& getStateVariables() const { return m_stateVariables; }
std::vector<ASTPointer<ModifierDefinition>> const& getFunctionModifiers() const { return m_functionModifiers; } std::vector<ASTPointer<ModifierDefinition>> const& getFunctionModifiers() const { return m_functionModifiers; }
std::vector<ASTPointer<FunctionDefinition>> const& getDefinedFunctions() const { return m_definedFunctions; } std::vector<ASTPointer<FunctionDefinition>> const& getDefinedFunctions() const { return m_definedFunctions; }
std::vector<ASTPointer<EventDefinition>> const& getEvents() const { return m_events; }
virtual TypePointer getType(ContractDefinition const* m_currentContract) const override; virtual TypePointer getType(ContractDefinition const* m_currentContract) const override;
@ -258,36 +229,34 @@ public:
/// and calls checkTypeRequirements on all its functions. /// and calls checkTypeRequirements on all its functions.
void checkTypeRequirements(); void checkTypeRequirements();
/// @return A shared pointer of an ASTString.
/// Can contain a nullptr in which case indicates absence of documentation
ASTPointer<ASTString> const& getDocumentation() const { return m_documentation; }
/// @returns a map of canonical function signatures to FunctionDefinitions /// @returns a map of canonical function signatures to FunctionDefinitions
/// as intended for use by the ABI. /// 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 /// List of all (direct and indirect) base contracts in order from derived to base, including
/// the contract itself. Available after name resolution /// the contract itself. Available after name resolution
std::vector<ContractDefinition const*> const& getLinearizedBaseContracts() const { return m_linearizedBaseContracts; } std::vector<ContractDefinition const*> const& getLinearizedBaseContracts() const { return m_linearizedBaseContracts; }
void setLinearizedBaseContracts(std::vector<ContractDefinition const*> const& _bases) { m_linearizedBaseContracts = _bases; } 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; FunctionDefinition const* getConstructor() const;
/// Returns the fallback function or nullptr if no constructor was specified.
FunctionDefinition const* getFallbackFunction() const;
private: private:
void checkIllegalOverrides() const; 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<InheritanceSpecifier>> m_baseContracts;
std::vector<ASTPointer<StructDefinition>> m_definedStructs; std::vector<ASTPointer<StructDefinition>> m_definedStructs;
std::vector<ASTPointer<VariableDeclaration>> m_stateVariables; std::vector<ASTPointer<VariableDeclaration>> m_stateVariables;
std::vector<ASTPointer<FunctionDefinition>> m_definedFunctions; std::vector<ASTPointer<FunctionDefinition>> m_definedFunctions;
std::vector<ASTPointer<ModifierDefinition>> m_functionModifiers; std::vector<ASTPointer<ModifierDefinition>> m_functionModifiers;
ASTPointer<ASTString> m_documentation; std::vector<ASTPointer<EventDefinition>> m_events;
std::vector<ContractDefinition const*> m_linearizedBaseContracts; 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;
}; };
class InheritanceSpecifier: public ASTNode class InheritanceSpecifier: public ASTNode
@ -355,20 +324,7 @@ private:
std::vector<ASTPointer<VariableDeclaration>> m_parameters; std::vector<ASTPointer<VariableDeclaration>> m_parameters;
}; };
/** class FunctionDefinition: public Declaration, public VariableScope, public Documented
* Abstract class that is added to each AST node that can store local variables.
*/
class VariableScope
{
public:
void addLocalVariable(VariableDeclaration const& _localVariable) { m_localVariables.push_back(&_localVariable); }
std::vector<VariableDeclaration const*> const& getLocalVariables() const { return m_localVariables; }
private:
std::vector<VariableDeclaration const*> m_localVariables;
};
class FunctionDefinition: public Declaration, public VariableScope
{ {
public: public:
FunctionDefinition(Location const& _location, ASTPointer<ASTString> const& _name, FunctionDefinition(Location const& _location, ASTPointer<ASTString> const& _name,
@ -380,13 +336,13 @@ public:
std::vector<ASTPointer<ModifierInvocation>> const& _modifiers, std::vector<ASTPointer<ModifierInvocation>> const& _modifiers,
ASTPointer<ParameterList> const& _returnParameters, ASTPointer<ParameterList> const& _returnParameters,
ASTPointer<Block> const& _body): ASTPointer<Block> const& _body):
Declaration(_location, _name), m_isPublic(_isPublic), m_isConstructor(_isConstructor), Declaration(_location, _name), Documented(_documentation),
m_isPublic(_isPublic), m_isConstructor(_isConstructor),
m_parameters(_parameters), m_parameters(_parameters),
m_isDeclaredConst(_isDeclaredConst), m_isDeclaredConst(_isDeclaredConst),
m_functionModifiers(_modifiers), m_functionModifiers(_modifiers),
m_returnParameters(_returnParameters), m_returnParameters(_returnParameters),
m_body(_body), m_body(_body)
m_documentation(_documentation)
{} {}
virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTVisitor& _visitor) override;
@ -401,9 +357,6 @@ public:
std::vector<ASTPointer<VariableDeclaration>> const& getReturnParameters() const { return m_returnParameters->getParameters(); } std::vector<ASTPointer<VariableDeclaration>> const& getReturnParameters() const { return m_returnParameters->getParameters(); }
ASTPointer<ParameterList> const& getReturnParameterList() const { return m_returnParameters; } ASTPointer<ParameterList> const& getReturnParameterList() const { return m_returnParameters; }
Block const& getBody() const { return *m_body; } Block const& getBody() const { return *m_body; }
/// @return A shared pointer of an ASTString.
/// Can contain a nullptr in which case indicates absence of documentation
ASTPointer<ASTString> const& getDocumentation() const { return m_documentation; }
virtual TypePointer getType(ContractDefinition const*) const override; virtual TypePointer getType(ContractDefinition const*) const override;
@ -423,7 +376,6 @@ private:
std::vector<ASTPointer<ModifierInvocation>> m_functionModifiers; std::vector<ASTPointer<ModifierInvocation>> m_functionModifiers;
ASTPointer<ParameterList> m_returnParameters; ASTPointer<ParameterList> m_returnParameters;
ASTPointer<Block> m_body; ASTPointer<Block> m_body;
ASTPointer<ASTString> m_documentation;
}; };
/** /**
@ -434,8 +386,10 @@ class VariableDeclaration: public Declaration
{ {
public: public:
VariableDeclaration(Location const& _location, ASTPointer<TypeName> const& _type, VariableDeclaration(Location const& _location, ASTPointer<TypeName> const& _type,
ASTPointer<ASTString> const& _name, bool _isPublic, bool _isStateVar = false): ASTPointer<ASTString> const& _name, bool _isPublic, bool _isStateVar = false,
Declaration(_location, _name), m_typeName(_type), m_isPublic(_isPublic), m_isStateVariable(_isStateVar) {} 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(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override; virtual void accept(ASTConstVisitor& _visitor) const override;
@ -450,12 +404,13 @@ public:
bool isLocalVariable() const { return !!dynamic_cast<FunctionDefinition const*>(getScope()); } bool isLocalVariable() const { return !!dynamic_cast<FunctionDefinition const*>(getScope()); }
bool isPublic() const { return m_isPublic; } bool isPublic() const { return m_isPublic; }
bool isStateVariable() const { return m_isStateVariable; } bool isStateVariable() const { return m_isStateVariable; }
bool isIndexed() const { return m_isIndexed; }
private: private:
ASTPointer<TypeName> m_typeName; ///< can be empty ("var") ASTPointer<TypeName> m_typeName; ///< can be empty ("var")
bool m_isPublic; ///< Whether there is an accessor for it or not 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_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 std::shared_ptr<Type const> m_type; ///< derived type, initially empty
}; };
@ -463,7 +418,7 @@ private:
/** /**
* Definition of a function modifier. * Definition of a function modifier.
*/ */
class ModifierDefinition: public Declaration, public VariableScope class ModifierDefinition: public Declaration, public VariableScope, public Documented
{ {
public: public:
ModifierDefinition(Location const& _location, ModifierDefinition(Location const& _location,
@ -471,7 +426,7 @@ public:
ASTPointer<ASTString> const& _documentation, ASTPointer<ASTString> const& _documentation,
ASTPointer<ParameterList> const& _parameters, ASTPointer<ParameterList> const& _parameters,
ASTPointer<Block> const& _body): ASTPointer<Block> const& _body):
Declaration(_location, _name), m_documentation(_documentation), Declaration(_location, _name), Documented(_documentation),
m_parameters(_parameters), m_body(_body) {} m_parameters(_parameters), m_body(_body) {}
virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTVisitor& _visitor) override;
@ -483,14 +438,9 @@ public:
virtual TypePointer getType(ContractDefinition const* = nullptr) const override; virtual TypePointer getType(ContractDefinition const* = nullptr) const override;
/// @return A shared pointer of an ASTString.
/// Can contain a nullptr in which case indicates absence of documentation
ASTPointer<ASTString> const& getDocumentation() const { return m_documentation; }
void checkTypeRequirements(); void checkTypeRequirements();
private: private:
ASTPointer<ASTString> m_documentation;
ASTPointer<ParameterList> m_parameters; ASTPointer<ParameterList> m_parameters;
ASTPointer<Block> m_body; ASTPointer<Block> m_body;
}; };
@ -518,6 +468,37 @@ private:
std::vector<ASTPointer<Expression>> m_arguments; std::vector<ASTPointer<Expression>> m_arguments;
}; };
/**
* Definition of a (loggable) event.
*/
class EventDefinition: public Declaration, 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 * 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. * 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 VariableDeclaration;
class ModifierDefinition; class ModifierDefinition;
class ModifierInvocation; class ModifierInvocation;
class EventDefinition;
class MagicVariableDeclaration; class MagicVariableDeclaration;
class TypeName; class TypeName;
class ElementaryTypeName; class ElementaryTypeName;

12
libsolidity/ASTPrinter.cpp

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

2
libsolidity/ASTPrinter.h

@ -51,6 +51,7 @@ public:
bool visit(VariableDeclaration const& _node) override; bool visit(VariableDeclaration const& _node) override;
bool visit(ModifierDefinition const& _node) override; bool visit(ModifierDefinition const& _node) override;
bool visit(ModifierInvocation const& _node) override; bool visit(ModifierInvocation const& _node) override;
bool visit(EventDefinition const& _node) override;
bool visit(TypeName const& _node) override; bool visit(TypeName const& _node) override;
bool visit(ElementaryTypeName const& _node) override; bool visit(ElementaryTypeName const& _node) override;
bool visit(UserDefinedTypeName const& _node) override; bool visit(UserDefinedTypeName const& _node) override;
@ -89,6 +90,7 @@ public:
void endVisit(VariableDeclaration const&) override; void endVisit(VariableDeclaration const&) override;
void endVisit(ModifierDefinition const&) override; void endVisit(ModifierDefinition const&) override;
void endVisit(ModifierInvocation const&) override; void endVisit(ModifierInvocation const&) override;
void endVisit(EventDefinition const&) override;
void endVisit(TypeName const&) override; void endVisit(TypeName const&) override;
void endVisit(ElementaryTypeName const&) override; void endVisit(ElementaryTypeName const&) override;
void endVisit(UserDefinedTypeName 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(VariableDeclaration&) { return true; }
virtual bool visit(ModifierDefinition&) { return true; } virtual bool visit(ModifierDefinition&) { return true; }
virtual bool visit(ModifierInvocation&) { return true; } virtual bool visit(ModifierInvocation&) { return true; }
virtual bool visit(EventDefinition&) { return true; }
virtual bool visit(TypeName&) { return true; } virtual bool visit(TypeName&) { return true; }
virtual bool visit(ElementaryTypeName&) { return true; } virtual bool visit(ElementaryTypeName&) { return true; }
virtual bool visit(UserDefinedTypeName&) { return true; } virtual bool visit(UserDefinedTypeName&) { return true; }
@ -92,6 +93,7 @@ public:
virtual void endVisit(VariableDeclaration&) { } virtual void endVisit(VariableDeclaration&) { }
virtual void endVisit(ModifierDefinition&) { } virtual void endVisit(ModifierDefinition&) { }
virtual void endVisit(ModifierInvocation&) { } virtual void endVisit(ModifierInvocation&) { }
virtual void endVisit(EventDefinition&) { }
virtual void endVisit(TypeName&) { } virtual void endVisit(TypeName&) { }
virtual void endVisit(ElementaryTypeName&) { } virtual void endVisit(ElementaryTypeName&) { }
virtual void endVisit(UserDefinedTypeName&) { } virtual void endVisit(UserDefinedTypeName&) { }
@ -136,6 +138,7 @@ public:
virtual bool visit(VariableDeclaration const&) { return true; } virtual bool visit(VariableDeclaration const&) { return true; }
virtual bool visit(ModifierDefinition const&) { return true; } virtual bool visit(ModifierDefinition const&) { return true; }
virtual bool visit(ModifierInvocation 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(TypeName const&) { return true; }
virtual bool visit(ElementaryTypeName const&) { return true; } virtual bool visit(ElementaryTypeName const&) { return true; }
virtual bool visit(UserDefinedTypeName const&) { return true; } virtual bool visit(UserDefinedTypeName const&) { return true; }
@ -176,6 +179,7 @@ public:
virtual void endVisit(VariableDeclaration const&) { } virtual void endVisit(VariableDeclaration const&) { }
virtual void endVisit(ModifierDefinition const&) { } virtual void endVisit(ModifierDefinition const&) { }
virtual void endVisit(ModifierInvocation const&) { } virtual void endVisit(ModifierInvocation const&) { }
virtual void endVisit(EventDefinition const&) { }
virtual void endVisit(TypeName const&) { } virtual void endVisit(TypeName const&) { }
virtual void endVisit(ElementaryTypeName const&) { } virtual void endVisit(ElementaryTypeName const&) { }
virtual void endVisit(UserDefinedTypeName 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_baseContracts, _visitor);
listAccept(m_definedStructs, _visitor); listAccept(m_definedStructs, _visitor);
listAccept(m_stateVariables, _visitor); listAccept(m_stateVariables, _visitor);
listAccept(m_definedFunctions, _visitor); listAccept(m_events, _visitor);
listAccept(m_functionModifiers, _visitor); listAccept(m_functionModifiers, _visitor);
listAccept(m_definedFunctions, _visitor);
} }
_visitor.endVisit(*this); _visitor.endVisit(*this);
} }
@ -77,8 +78,9 @@ void ContractDefinition::accept(ASTConstVisitor& _visitor) const
listAccept(m_baseContracts, _visitor); listAccept(m_baseContracts, _visitor);
listAccept(m_definedStructs, _visitor); listAccept(m_definedStructs, _visitor);
listAccept(m_stateVariables, _visitor); listAccept(m_stateVariables, _visitor);
listAccept(m_definedFunctions, _visitor); listAccept(m_events, _visitor);
listAccept(m_functionModifiers, _visitor); listAccept(m_functionModifiers, _visitor);
listAccept(m_definedFunctions, _visitor);
} }
_visitor.endVisit(*this); _visitor.endVisit(*this);
} }
@ -219,6 +221,20 @@ void ModifierInvocation::accept(ASTConstVisitor& _visitor) const
_visitor.endVisit(*this); _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) void TypeName::accept(ASTVisitor& _visitor)
{ {
_visitor.visit(*this); _visitor.visit(*this);

4
libsolidity/BaseTypes.h

@ -41,6 +41,8 @@ struct Location
start(_start), end(_end), sourceName(_sourceName) { } start(_start), end(_end), sourceName(_sourceName) { }
Location(): start(-1), end(-1) { } Location(): start(-1), end(-1) { }
bool isEmpty() const { return start == -1 && end == -1; }
int start; int start;
int end; int end;
std::shared_ptr<std::string const> sourceName; std::shared_ptr<std::string const> sourceName;
@ -49,6 +51,8 @@ struct Location
/// Stream output for Location (used e.g. in boost exceptions). /// Stream output for Location (used e.g. in boost exceptions).
inline std::ostream& operator<<(std::ostream& _out, Location const& _location) inline std::ostream& operator<<(std::ostream& _out, Location const& _location)
{ {
if (_location.isEmpty())
return _out << "NO_LOCATION_SPECIFIED";
return _out << *_location.sourceName << "[" << _location.start << "," << _location.end << ")"; return _out << *_location.sourceName << "[" << _location.start << "," << _location.end << ")";
} }

20
libsolidity/Compiler.cpp

@ -142,12 +142,12 @@ void Compiler::appendConstructorCall(FunctionDefinition const& _constructor)
void Compiler::appendFunctionSelector(ContractDefinition const& _contract) 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; map<FixedHash<4>, const eth::AssemblyItem> callDataUnpackerEntryPoints;
// retrieve the function signature hash from the calldata // 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); CompilerUtils(m_context).loadFromMemory(0, 4, false, true);
// stack now is: 1 0 <funhash> // stack now is: 1 0 <funhash>
for (auto const& it: interfaceFunctions) for (auto const& it: interfaceFunctions)
@ -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 << eth::dupInstruction(1) << u256(FixedHash<4>::Arith(it.first)) << eth::Instruction::EQ;
m_context.appendConditionalJumpTo(callDataUnpackerEntryPoints.at(it.first)); m_context.appendConditionalJumpTo(callDataUnpackerEntryPoints.at(it.first));
} }
m_context << eth::Instruction::STOP; // function not found 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) for (auto const& it: interfaceFunctions)
{ {
FunctionType const* functionType = it.second.getFunctionType(); FunctionTypePointer const& functionType = it.second;
m_context << callDataUnpackerEntryPoints.at(it.first); m_context << callDataUnpackerEntryPoints.at(it.first);
eth::AssemblyItem returnTag = m_context.pushNewTag(); eth::AssemblyItem returnTag = m_context.pushNewTag();
appendCalldataUnpacker(functionType->getParameterTypes()); appendCalldataUnpacker(functionType->getParameterTypes());
m_context.appendJumpTo(m_context.getFunctionEntryLabel(*it.second.getDeclaration())); m_context.appendJumpTo(m_context.getFunctionEntryLabel(it.second->getDeclaration()));
m_context << returnTag; m_context << returnTag;
appendReturnValuePacker(functionType->getReturnParameterTypes()); 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; bool existed = m_sources.count(_name) != 0;
reset(true); 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; return existed;
} }
void CompilerStack::setSource(string const& _sourceCode) void CompilerStack::setSource(string const& _sourceCode)
{ {
reset(); reset();
addSource("", _sourceCode); addSource("", expanded(_sourceCode));
} }
void CompilerStack::parse() void CompilerStack::parse()
@ -126,6 +126,58 @@ vector<string> CompilerStack::getContractNames() const
return contractNames; 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) void CompilerStack::compile(bool _optimize)
{ {
if (!m_parseSuccessful) if (!m_parseSuccessful)

4
libsolidity/CompilerStack.h

@ -142,6 +142,10 @@ private:
Contract(); 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 reset(bool _keepSources = false);
void resolveImports(); void resolveImports();

109
libsolidity/ExpressionCompiler.cpp

@ -23,6 +23,7 @@
#include <utility> #include <utility>
#include <numeric> #include <numeric>
#include <libdevcore/Common.h> #include <libdevcore/Common.h>
#include <libdevcrypto/SHA3.h>
#include <libsolidity/AST.h> #include <libsolidity/AST.h>
#include <libsolidity/ExpressionCompiler.h> #include <libsolidity/ExpressionCompiler.h>
#include <libsolidity/CompilerContext.h> #include <libsolidity/CompilerContext.h>
@ -66,7 +67,7 @@ bool ExpressionCompiler::visit(Assignment const& _assignment)
{ {
if (m_currentLValue.storesReferenceOnStack()) if (m_currentLValue.storesReferenceOnStack())
m_context << eth::Instruction::SWAP1 << eth::Instruction::DUP2; m_context << eth::Instruction::SWAP1 << eth::Instruction::DUP2;
m_currentLValue.retrieveValue(_assignment, true); m_currentLValue.retrieveValue(_assignment.getType(), _assignment.getLocation(), true);
appendOrdinaryBinaryOperatorCode(Token::AssignmentToBinaryOp(op), *_assignment.getType()); appendOrdinaryBinaryOperatorCode(Token::AssignmentToBinaryOp(op), *_assignment.getType());
if (m_currentLValue.storesReferenceOnStack()) if (m_currentLValue.storesReferenceOnStack())
m_context << eth::Instruction::SWAP1; m_context << eth::Instruction::SWAP1;
@ -107,7 +108,7 @@ bool ExpressionCompiler::visit(UnaryOperation const& _unaryOperation)
case Token::INC: // ++ (pre- or postfix) case Token::INC: // ++ (pre- or postfix)
case Token::DEC: // -- (pre- or postfix) case Token::DEC: // -- (pre- or postfix)
solAssert(m_currentLValue.isValid(), "LValue not retrieved."); solAssert(m_currentLValue.isValid(), "LValue not retrieved.");
m_currentLValue.retrieveValue(_unaryOperation); m_currentLValue.retrieveValue(_unaryOperation.getType(), _unaryOperation.getLocation());
if (!_unaryOperation.isPrefixOperation()) if (!_unaryOperation.isPrefixOperation())
{ {
if (m_currentLValue.storesReferenceOnStack()) if (m_currentLValue.storesReferenceOnStack())
@ -304,10 +305,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
m_context << eth::Instruction::SUICIDE; m_context << eth::Instruction::SUICIDE;
break; break;
case Location::SHA3: case Location::SHA3:
arguments.front()->accept(*this); appendExpressionCopyToMemory(*function.getParameterTypes().front(), *arguments.front());
appendTypeConversion(*arguments.front()->getType(), *function.getParameterTypes().front(), true);
// @todo move this once we actually use memory
CompilerUtils(m_context).storeInMemory(0);
m_context << u256(32) << u256(0) << eth::Instruction::SHA3; m_context << u256(32) << u256(0) << eth::Instruction::SHA3;
break; break;
case Location::LOG0: case Location::LOG0:
@ -317,14 +315,41 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
case Location::LOG4: case Location::LOG4:
{ {
unsigned logNumber = int(function.getLocation()) - int(Location::LOG0); 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); arguments[arg]->accept(*this);
appendTypeConversion(*arguments[arg]->getType(), *function.getParameterTypes()[arg], true); appendTypeConversion(*arguments[arg]->getType(), *function.getParameterTypes()[arg], true);
} }
// @todo move this once we actually use memory unsigned length = appendExpressionCopyToMemory(*function.getParameterTypes().front(),
CompilerUtils(m_context).storeInMemory(0); *arguments.front());
m_context << u256(32) << u256(0) << eth::logInstruction(logNumber); 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; break;
} }
case Location::BLOCKHASH: case Location::BLOCKHASH:
@ -459,14 +484,13 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess)
bool ExpressionCompiler::visit(IndexAccess const& _indexAccess) bool ExpressionCompiler::visit(IndexAccess const& _indexAccess)
{ {
_indexAccess.getBaseExpression().accept(*this); _indexAccess.getBaseExpression().accept(*this);
_indexAccess.getIndexExpression().accept(*this);
appendTypeConversion(*_indexAccess.getIndexExpression().getType(), TypePointer const& keyType = dynamic_cast<MappingType const&>(*_indexAccess.getBaseExpression().getType()).getKeyType();
*dynamic_cast<MappingType const&>(*_indexAccess.getBaseExpression().getType()).getKeyType(), unsigned length = appendExpressionCopyToMemory(*keyType, _indexAccess.getIndexExpression());
true); solAssert(length == 32, "Mapping key has to take 32 bytes in memory (for now).");
// @todo move this once we actually use memory // @todo move this once we actually use memory
CompilerUtils(m_context).storeInMemory(0); length += CompilerUtils(m_context).storeInMemory(length);
CompilerUtils(m_context).storeInMemory(32); m_context << u256(length) << u256(0) << eth::Instruction::SHA3;
m_context << u256(64) << u256(0) << eth::Instruction::SHA3;
m_currentLValue = LValue(m_context, LValue::STORAGE, *_indexAccess.getType()); m_currentLValue = LValue(m_context, LValue::STORAGE, *_indexAccess.getType());
m_currentLValue.retrieveValueIfLValueNotRequested(_indexAccess); m_currentLValue.retrieveValueIfLValueNotRequested(_indexAccess);
@ -495,6 +519,10 @@ void ExpressionCompiler::endVisit(Identifier const& _identifier)
{ {
// no-op // no-op
} }
else if (dynamic_cast<EventDefinition const*>(declaration))
{
// no-op
}
else else
{ {
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Identifier type not expected in expression context.")); BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Identifier type not expected in expression context."));
@ -791,27 +819,30 @@ unsigned ExpressionCompiler::appendArgumentCopyToMemory(TypePointers const& _typ
{ {
unsigned length = 0; unsigned length = 0;
for (unsigned i = 0; i < _arguments.size(); ++i) for (unsigned i = 0; i < _arguments.size(); ++i)
{ length += appendExpressionCopyToMemory(*_types[i], *_arguments[i], _memoryOffset + length);
_arguments[i]->accept(*this);
appendTypeConversion(*_arguments[i]->getType(), *_types[i], true);
unsigned const c_numBytes = _types[i]->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;
bool const c_padToWords = true;
length += CompilerUtils(m_context).storeInMemory(_memoryOffset + length, c_numBytes,
c_leftAligned, c_padToWords);
}
return length; return length;
} }
unsigned ExpressionCompiler::appendExpressionCopyToMemory(Type const& _expectedType,
Expression const& _expression, unsigned _memoryOffset)
{
_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(_expression.getLocation())
<< errinfo_comment("Type " + _expectedType.toString() + " not yet supported."));
bool const c_leftAligned = _expectedType.getCategory() == Type::Category::STRING;
bool const c_padToWords = true;
return CompilerUtils(m_context).storeInMemory(_memoryOffset, c_numBytes, c_leftAligned, c_padToWords);
}
void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const& _varDecl) void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const& _varDecl)
{ {
m_currentLValue.fromStateVariable(_varDecl, _varDecl.getType()); m_currentLValue.fromStateVariable(_varDecl, _varDecl.getType());
solAssert(m_currentLValue.isInStorage(), ""); solAssert(m_currentLValue.isInStorage(), "");
m_currentLValue.retrieveValueFromStorage(_varDecl.getType(), true); m_currentLValue.retrieveValue(_varDecl.getType(), Location(), true);
} }
ExpressionCompiler::LValue::LValue(CompilerContext& _compilerContext, LValueType _type, Type const& _dataType, ExpressionCompiler::LValue::LValue(CompilerContext& _compilerContext, LValueType _type, Type const& _dataType,
@ -826,7 +857,7 @@ ExpressionCompiler::LValue::LValue(CompilerContext& _compilerContext, LValueType
m_size = unsigned(_dataType.getSizeOnStack()); m_size = unsigned(_dataType.getSizeOnStack());
} }
void ExpressionCompiler::LValue::retrieveValue(Expression const& _expression, bool _remove) const void ExpressionCompiler::LValue::retrieveValue(TypePointer const& _type, Location const& _location, bool _remove) const
{ {
switch (m_type) switch (m_type)
{ {
@ -834,23 +865,23 @@ void ExpressionCompiler::LValue::retrieveValue(Expression const& _expression, bo
{ {
unsigned stackPos = m_context->baseToCurrentStackOffset(unsigned(m_baseStackOffset)); unsigned stackPos = m_context->baseToCurrentStackOffset(unsigned(m_baseStackOffset));
if (stackPos >= 15) //@todo correct this by fetching earlier or moving to memory if (stackPos >= 15) //@todo correct this by fetching earlier or moving to memory
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(_expression.getLocation()) BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(_location)
<< errinfo_comment("Stack too deep.")); << errinfo_comment("Stack too deep."));
for (unsigned i = 0; i < m_size; ++i) for (unsigned i = 0; i < m_size; ++i)
*m_context << eth::dupInstruction(stackPos + 1); *m_context << eth::dupInstruction(stackPos + 1);
break; break;
} }
case STORAGE: case STORAGE:
retrieveValueFromStorage(_expression.getType(), _remove); retrieveValueFromStorage(_type, _remove);
break; break;
case MEMORY: case MEMORY:
if (!_expression.getType()->isValueType()) if (!_type->isValueType())
break; // no distinction between value and reference for non-value types break; // no distinction between value and reference for non-value types
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_sourceLocation(_expression.getLocation()) BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_sourceLocation(_location)
<< errinfo_comment("Location type not yet implemented.")); << errinfo_comment("Location type not yet implemented."));
break; break;
default: default:
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_sourceLocation(_expression.getLocation()) BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_sourceLocation(_location)
<< errinfo_comment("Unsupported location type.")); << errinfo_comment("Unsupported location type."));
break; break;
} }
@ -889,7 +920,7 @@ void ExpressionCompiler::LValue::storeValue(Expression const& _expression, bool
for (unsigned i = 0; i < m_size; ++i) for (unsigned i = 0; i < m_size; ++i)
*m_context << eth::swapInstruction(stackDiff) << eth::Instruction::POP; *m_context << eth::swapInstruction(stackDiff) << eth::Instruction::POP;
if (!_move) if (!_move)
retrieveValue(_expression); retrieveValue(_expression.getType(), _expression.getLocation());
break; break;
} }
case LValue::STORAGE: case LValue::STORAGE:
@ -976,7 +1007,7 @@ void ExpressionCompiler::LValue::retrieveValueIfLValueNotRequested(Expression co
{ {
if (!_expression.lvalueRequested()) if (!_expression.lvalueRequested())
{ {
retrieveValue(_expression, true); retrieveValue(_expression.getType(), _expression.getLocation(), true);
reset(); reset();
} }
} }

17
libsolidity/ExpressionCompiler.h

@ -94,8 +94,13 @@ private:
bool bare = false); bool bare = false);
/// Appends code that copies the given arguments to memory (with optional offset). /// Appends code that copies the given arguments to memory (with optional offset).
/// @returns the number of bytes copied to memory /// @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); 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 /// Appends code for a State Variable accessor function
void appendStateVariableAccessor(VariableDeclaration const& _varDecl); void appendStateVariableAccessor(VariableDeclaration const& _varDecl);
@ -130,10 +135,9 @@ private:
/// Copies the value of the current lvalue to the top of the stack and, if @a _remove is true, /// Copies the value of the current lvalue to the top of the stack and, if @a _remove is true,
/// also removes the reference from the stack (note that is does not reset the type to @a NONE). /// also removes the reference from the stack (note that is does not reset the type to @a NONE).
/// @a _expression is the current expression, used for error reporting. /// @a _type is the type of the current expression and @ _location its location, used for error reporting.
void retrieveValue(Expression const& _expression, bool _remove = false) const; /// @a _location can be a nullptr for expressions that don't have an actual ASTNode equivalent
/// Convenience function to retrieve Value from Storage. Specific version of @ref retrieveValue void retrieveValue(TypePointer const& _type, Location const& _location, bool _remove = false) const;
void retrieveValueFromStorage(TypePointer const& _type, bool _remove = false) const;
/// Stores a value (from the stack directly beneath the reference, which is assumed to /// Stores a value (from the stack directly beneath the reference, which is assumed to
/// be on the top of the stack, if any) in the lvalue and removes the reference. /// be on the top of the stack, if any) in the lvalue and removes the reference.
/// Also removes the stored value from the stack if @a _move is /// Also removes the stored value from the stack if @a _move is
@ -147,6 +151,9 @@ private:
void retrieveValueIfLValueNotRequested(Expression const& _expression); void retrieveValueIfLValueNotRequested(Expression const& _expression);
private: private:
/// Convenience function to retrieve Value from Storage. Specific version of @ref retrieveValue
void retrieveValueFromStorage(TypePointer const& _type, bool _remove = false) const;
CompilerContext* m_context; CompilerContext* m_context;
LValueType m_type = NONE; LValueType m_type = NONE;
/// If m_type is STACK, this is base stack offset (@see /// If m_type is STACK, this is base stack offset (@see

45
libsolidity/InterfaceHandler.cpp

@ -45,23 +45,26 @@ std::unique_ptr<std::string> InterfaceHandler::getABIInterface(ContractDefinitio
Json::Value inputs(Json::arrayValue); Json::Value inputs(Json::arrayValue);
Json::Value outputs(Json::arrayValue); Json::Value outputs(Json::arrayValue);
auto populateParameters = [](vector<ParamDescription> const& _params) auto populateParameters = [](vector<string> const& _paramNames,
vector<string> const& _paramTypes)
{ {
Json::Value params(Json::arrayValue); Json::Value params(Json::arrayValue);
for (auto const& param: _params) solAssert(_paramNames.size() == _paramTypes.size(), "Names and types vector size does not match");
for (unsigned i = 0; i < _paramNames.size(); ++i)
{ {
Json::Value input; Json::Value input;
input["name"] = param.getName(); input["name"] = _paramNames[i];
input["type"] = param.getType(); input["type"] = _paramTypes[i];
params.append(input); params.append(input);
} }
return params; return params;
}; };
method["name"] = it.second->getDeclaration().getName();
method["name"] = it.second.getName(); method["constant"] = it.second->isConstant();
method["constant"] = it.second.isConstant(); method["inputs"] = populateParameters(it.second->getParameterNames(),
method["inputs"] = populateParameters(it.second.getParameters()); it.second->getParameterTypeNames());
method["outputs"] = populateParameters(it.second.getReturnParameters()); method["outputs"] = populateParameters(it.second->getReturnParameterNames(),
it.second->getReturnParameterTypeNames());
methods.append(method); methods.append(method);
} }
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(methods)));
@ -72,16 +75,20 @@ unique_ptr<string> InterfaceHandler::getABISolidityInterface(ContractDefinition
string ret = "contract " + _contractDef.getName() + "{"; string ret = "contract " + _contractDef.getName() + "{";
for (auto const& it: _contractDef.getInterfaceFunctions()) for (auto const& it: _contractDef.getInterfaceFunctions())
{ {
auto populateParameters = [](vector<ParamDescription> const& _params) auto populateParameters = [](vector<string> const& _paramNames,
vector<string> const& _paramTypes)
{ {
string r = ""; string r = "";
for (auto const& param: _params) solAssert(_paramNames.size() == _paramTypes.size(), "Names and types vector size does not match");
r += (r.size() ? "," : "(") + param.getType() + " " + param.getName(); for (unsigned i = 0; i < _paramNames.size(); ++i)
r += (r.size() ? "," : "(") + _paramTypes[i] + " " + _paramNames[i];
return r.size() ? r + ")" : "()"; return r.size() ? r + ")" : "()";
}; };
ret += "function " + it.second.getName() + populateParameters(it.second.getParameters()) + (it.second.isConstant() ? "constant " : ""); ret += "function " + it.second->getDeclaration().getName() +
if (it.second.getReturnParameters().size()) populateParameters(it.second->getParameterNames(), it.second->getParameterTypeNames()) +
ret += "returns" + populateParameters(it.second.getReturnParameters()); (it.second->isConstant() ? "constant " : "");
if (it.second->getReturnParameterTypes().size())
ret += "returns" + populateParameters(it.second->getReturnParameterNames(), it.second->getReturnParameterTypeNames());
else if (ret.back() == ' ') else if (ret.back() == ' ')
ret.pop_back(); ret.pop_back();
ret += "{}"; ret += "{}";
@ -97,7 +104,7 @@ std::unique_ptr<std::string> InterfaceHandler::getUserDocumentation(ContractDefi
for (auto const& it: _contractDef.getInterfaceFunctions()) for (auto const& it: _contractDef.getInterfaceFunctions())
{ {
Json::Value user; Json::Value user;
auto strPtr = it.second.getDocumentation(); auto strPtr = it.second->getDocumentation();
if (strPtr) if (strPtr)
{ {
resetUser(); resetUser();
@ -105,7 +112,7 @@ std::unique_ptr<std::string> InterfaceHandler::getUserDocumentation(ContractDefi
if (!m_notice.empty()) if (!m_notice.empty())
{// since @notice is the only user tag if missing function should not appear {// since @notice is the only user tag if missing function should not appear
user["notice"] = Json::Value(m_notice); user["notice"] = Json::Value(m_notice);
methods[it.second.getSignature()] = user; methods[it.second->getCanonicalSignature()] = user;
} }
} }
} }
@ -138,7 +145,7 @@ std::unique_ptr<std::string> InterfaceHandler::getDevDocumentation(ContractDefin
for (auto const& it: _contractDef.getInterfaceFunctions()) for (auto const& it: _contractDef.getInterfaceFunctions())
{ {
Json::Value method; Json::Value method;
auto strPtr = it.second.getDocumentation(); auto strPtr = it.second->getDocumentation();
if (strPtr) if (strPtr)
{ {
resetDev(); resetDev();
@ -161,7 +168,7 @@ std::unique_ptr<std::string> InterfaceHandler::getDevDocumentation(ContractDefin
method["return"] = m_return; method["return"] = m_return;
if (!method.empty()) // add the function, only if we have any documentation to add 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; doc["methods"] = methods;

8
libsolidity/NameAndTypeResolver.cpp

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

1
libsolidity/NameAndTypeResolver.h

@ -104,6 +104,7 @@ private:
void endVisit(ModifierDefinition& _modifier); void endVisit(ModifierDefinition& _modifier);
void endVisit(VariableDefinition& _variableDefinition); void endVisit(VariableDefinition& _variableDefinition);
bool visit(VariableDeclaration& _declaration); bool visit(VariableDeclaration& _declaration);
bool visit(EventDefinition& _event);
void enterNewSubScope(Declaration const& _declaration); void enterNewSubScope(Declaration const& _declaration);
void closeCurrentScope(); void closeCurrentScope();

65
libsolidity/Parser.cpp

@ -122,6 +122,7 @@ ASTPointer<ContractDefinition> Parser::parseContractDefinition()
vector<ASTPointer<VariableDeclaration>> stateVariables; vector<ASTPointer<VariableDeclaration>> stateVariables;
vector<ASTPointer<FunctionDefinition>> functions; vector<ASTPointer<FunctionDefinition>> functions;
vector<ASTPointer<ModifierDefinition>> modifiers; vector<ASTPointer<ModifierDefinition>> modifiers;
vector<ASTPointer<EventDefinition>> events;
if (m_scanner->getCurrentToken() == Token::IS) if (m_scanner->getCurrentToken() == Token::IS)
do do
{ {
@ -149,19 +150,23 @@ ASTPointer<ContractDefinition> Parser::parseContractDefinition()
else if (currentToken == Token::IDENTIFIER || currentToken == Token::MAPPING || else if (currentToken == Token::IDENTIFIER || currentToken == Token::MAPPING ||
Token::isElementaryTypeName(currentToken)) Token::isElementaryTypeName(currentToken))
{ {
bool const allowVar = false; VarDeclParserOptions options;
stateVariables.push_back(parseVariableDeclaration(allowVar, visibilityIsPublic, true)); options.isPublic = visibilityIsPublic;
options.isStateVariable = true;
stateVariables.push_back(parseVariableDeclaration(options));
expectToken(Token::SEMICOLON); expectToken(Token::SEMICOLON);
} }
else if (currentToken == Token::MODIFIER) else if (currentToken == Token::MODIFIER)
modifiers.push_back(parseModifierDefinition()); modifiers.push_back(parseModifierDefinition());
else if (currentToken == Token::EVENT)
events.push_back(parseEventDefinition());
else else
BOOST_THROW_EXCEPTION(createParserError("Function, variable, struct or modifier declaration expected.")); BOOST_THROW_EXCEPTION(createParserError("Function, variable, struct or modifier declaration expected."));
} }
nodeFactory.markEndPosition(); nodeFactory.markEndPosition();
expectToken(Token::RBRACE); expectToken(Token::RBRACE);
return nodeFactory.createNode<ContractDefinition>(name, docString, baseContracts, structs, return nodeFactory.createNode<ContractDefinition>(name, docString, baseContracts, structs,
stateVariables, functions, modifiers); stateVariables, functions, modifiers, events);
} }
ASTPointer<InheritanceSpecifier> Parser::parseInheritanceSpecifier() ASTPointer<InheritanceSpecifier> Parser::parseInheritanceSpecifier()
@ -189,7 +194,11 @@ ASTPointer<FunctionDefinition> Parser::parseFunctionDefinition(bool _isPublic, A
docstring = make_shared<ASTString>(m_scanner->getCurrentCommentLiteral()); docstring = make_shared<ASTString>(m_scanner->getCurrentCommentLiteral());
expectToken(Token::FUNCTION); 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()); ASTPointer<ParameterList> parameters(parseParameterList());
bool isDeclaredConst = false; bool isDeclaredConst = false;
vector<ASTPointer<ModifierInvocation>> modifiers; vector<ASTPointer<ModifierInvocation>> modifiers;
@ -236,8 +245,7 @@ ASTPointer<StructDefinition> Parser::parseStructDefinition()
expectToken(Token::LBRACE); expectToken(Token::LBRACE);
while (m_scanner->getCurrentToken() != Token::RBRACE) while (m_scanner->getCurrentToken() != Token::RBRACE)
{ {
bool const allowVar = false; members.push_back(parseVariableDeclaration());
members.push_back(parseVariableDeclaration(allowVar));
expectToken(Token::SEMICOLON); expectToken(Token::SEMICOLON);
} }
nodeFactory.markEndPosition(); nodeFactory.markEndPosition();
@ -245,12 +253,20 @@ ASTPointer<StructDefinition> Parser::parseStructDefinition()
return nodeFactory.createNode<StructDefinition>(name, members); 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); 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(); 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() ASTPointer<ModifierDefinition> Parser::parseModifierDefinition()
@ -280,6 +296,23 @@ ASTPointer<ModifierDefinition> Parser::parseModifierDefinition()
return nodeFactory.createNode<ModifierDefinition>(name, docstring, parameters, 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);
nodeFactory.markEndPosition();
expectToken(Token::SEMICOLON);
return nodeFactory.createNode<EventDefinition>(name, docstring, parameters);
}
ASTPointer<ModifierInvocation> Parser::parseModifierInvocation() ASTPointer<ModifierInvocation> Parser::parseModifierInvocation()
{ {
ASTNodeFactory nodeFactory(*this); ASTNodeFactory nodeFactory(*this);
@ -352,19 +385,20 @@ ASTPointer<Mapping> Parser::parseMapping()
return nodeFactory.createNode<Mapping>(keyType, valueType); return nodeFactory.createNode<Mapping>(keyType, valueType);
} }
ASTPointer<ParameterList> Parser::parseParameterList(bool _allowEmpty) ASTPointer<ParameterList> Parser::parseParameterList(bool _allowEmpty, bool _allowIndexed)
{ {
ASTNodeFactory nodeFactory(*this); ASTNodeFactory nodeFactory(*this);
vector<ASTPointer<VariableDeclaration>> parameters; vector<ASTPointer<VariableDeclaration>> parameters;
VarDeclParserOptions options;
options.allowIndexed = _allowIndexed;
expectToken(Token::LPAREN); expectToken(Token::LPAREN);
if (!_allowEmpty || m_scanner->getCurrentToken() != Token::RPAREN) if (!_allowEmpty || m_scanner->getCurrentToken() != Token::RPAREN)
{ {
bool const allowVar = false; parameters.push_back(parseVariableDeclaration(options));
parameters.push_back(parseVariableDeclaration(allowVar));
while (m_scanner->getCurrentToken() != Token::RPAREN) while (m_scanner->getCurrentToken() != Token::RPAREN)
{ {
expectToken(Token::COMMA); expectToken(Token::COMMA);
parameters.push_back(parseVariableDeclaration(allowVar)); parameters.push_back(parseVariableDeclaration(options));
} }
} }
nodeFactory.markEndPosition(); nodeFactory.markEndPosition();
@ -506,8 +540,9 @@ ASTPointer<Statement> Parser::parseVarDefOrExprStmt()
ASTPointer<VariableDefinition> Parser::parseVariableDefinition() ASTPointer<VariableDefinition> Parser::parseVariableDefinition()
{ {
ASTNodeFactory nodeFactory(*this); ASTNodeFactory nodeFactory(*this);
bool const allowVar = true; VarDeclParserOptions options;
ASTPointer<VariableDeclaration> variable = parseVariableDeclaration(allowVar); options.allowVar = true;
ASTPointer<VariableDeclaration> variable = parseVariableDeclaration(options);
ASTPointer<Expression> value; ASTPointer<Expression> value;
if (m_scanner->getCurrentToken() == Token::ASSIGN) if (m_scanner->getCurrentToken() == Token::ASSIGN)
{ {

13
libsolidity/Parser.h

@ -45,6 +45,14 @@ private:
/// End position of the current token /// End position of the current token
int getEndPosition() const; 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 ///@name Parsing functions for the AST nodes
ASTPointer<ImportDirective> parseImportDirective(); ASTPointer<ImportDirective> parseImportDirective();
@ -52,13 +60,14 @@ private:
ASTPointer<InheritanceSpecifier> parseInheritanceSpecifier(); ASTPointer<InheritanceSpecifier> parseInheritanceSpecifier();
ASTPointer<FunctionDefinition> parseFunctionDefinition(bool _isPublic, ASTString const* _contractName); ASTPointer<FunctionDefinition> parseFunctionDefinition(bool _isPublic, ASTString const* _contractName);
ASTPointer<StructDefinition> parseStructDefinition(); 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<ModifierDefinition> parseModifierDefinition();
ASTPointer<EventDefinition> parseEventDefinition();
ASTPointer<ModifierInvocation> parseModifierInvocation(); ASTPointer<ModifierInvocation> parseModifierInvocation();
ASTPointer<Identifier> parseIdentifier(); ASTPointer<Identifier> parseIdentifier();
ASTPointer<TypeName> parseTypeName(bool _allowVar); ASTPointer<TypeName> parseTypeName(bool _allowVar);
ASTPointer<Mapping> parseMapping(); ASTPointer<Mapping> parseMapping();
ASTPointer<ParameterList> parseParameterList(bool _allowEmpty = true); ASTPointer<ParameterList> parseParameterList(bool _allowEmpty = true, bool _allowIndexed = false);
ASTPointer<Block> parseBlock(); ASTPointer<Block> parseBlock();
ASTPointer<Statement> parseStatement(); ASTPointer<Statement> parseStatement();
ASTPointer<IfStatement> parseIfStatement(); ASTPointer<IfStatement> parseIfStatement();

2
libsolidity/Token.h

@ -153,7 +153,9 @@ namespace solidity
K(DEFAULT, "default", 0) \ K(DEFAULT, "default", 0) \
K(DO, "do", 0) \ K(DO, "do", 0) \
K(ELSE, "else", 0) \ K(ELSE, "else", 0) \
K(EVENT, "event", 0) \
K(IS, "is", 0) \ K(IS, "is", 0) \
K(INDEXED, "indexed", 0) \
K(FOR, "for", 0) \ K(FOR, "for", 0) \
K(FUNCTION, "function", 0) \ K(FUNCTION, "function", 0) \
K(IF, "if", 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 (ContractDefinition const* base: m_contract.getLinearizedBaseContracts())
for (ASTPointer<FunctionDefinition> const& function: base->getDefinedFunctions()) 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))); members.insert(make_pair(function->getName(), make_shared<FunctionType>(*function, true)));
} }
else else
for (auto const& it: m_contract.getInterfaceFunctions()) 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)); m_members.reset(new MemberList(members));
} }
return *m_members; return *m_members;
@ -522,7 +522,7 @@ u256 ContractType::getFunctionIdentifier(string const& _functionName) const
{ {
auto interfaceFunctions = m_contract.getInterfaceFunctions(); auto interfaceFunctions = m_contract.getInterfaceFunctions();
for (auto const& it: 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 FixedHash<4>::Arith(it.first);
return Invalid256; return Invalid256;
@ -589,12 +589,15 @@ u256 StructType::getStorageOffsetOfMember(string const& _name) const
} }
FunctionType::FunctionType(FunctionDefinition const& _function, bool _isInternal): 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; TypePointers params;
vector<string> paramNames; vector<string> paramNames;
TypePointers retParams; TypePointers retParams;
vector<string> retParamNames; vector<string> retParamNames;
params.reserve(_function.getParameters().size()); params.reserve(_function.getParameters().size());
paramNames.reserve(_function.getParameters().size()); paramNames.reserve(_function.getParameters().size());
for (ASTPointer<VariableDeclaration> const& var: _function.getParameters()) for (ASTPointer<VariableDeclaration> const& var: _function.getParameters())
@ -616,7 +619,7 @@ FunctionType::FunctionType(FunctionDefinition const& _function, bool _isInternal
} }
FunctionType::FunctionType(VariableDeclaration const& _varDecl): FunctionType::FunctionType(VariableDeclaration const& _varDecl):
m_location(Location::EXTERNAL) m_location(Location::EXTERNAL), m_isConstant(true), m_declaration(&_varDecl)
{ {
TypePointers params({}); TypePointers params({});
vector<string> paramNames({}); vector<string> paramNames({});
@ -630,6 +633,22 @@ FunctionType::FunctionType(VariableDeclaration const& _varDecl):
swap(retParamNames, m_returnParameterNames); 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 bool FunctionType::operator==(Type const& _other) const
{ {
if (_other.getCategory() != getCategory()) if (_other.getCategory() != getCategory())
@ -638,6 +657,9 @@ bool FunctionType::operator==(Type const& _other) const
if (m_location != other.m_location) if (m_location != other.m_location)
return false; return false;
if (m_isConstant != other.isConstant())
return false;
if (m_parameterTypes.size() != other.m_parameterTypes.size() || if (m_parameterTypes.size() != other.m_parameterTypes.size() ||
m_returnParameterTypes.size() != other.m_returnParameterTypes.size()) m_returnParameterTypes.size() != other.m_returnParameterTypes.size())
return false; return false;
@ -711,7 +733,13 @@ MemberList const& FunctionType::getMembers() const
string FunctionType::getCanonicalSignature(std::string const& _name) 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) for (auto it = m_parameterTypes.cbegin(); it != m_parameterTypes.cend(); ++it)
ret += (*it)->toString() + (it + 1 == m_parameterTypes.cend() ? "" : ","); 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); 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 bool MappingType::operator==(Type const& _other) const
{ {
if (_other.getCategory() != getCategory()) 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 // 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. // functions. Note that this does not add inherited functions on purpose.
for (ASTPointer<FunctionDefinition> const& f: contract.getDefinedFunctions()) for (ASTPointer<FunctionDefinition> const& f: contract.getDefinedFunctions())
if (!f->isConstructor()) if (!f->isConstructor() && !f->getName().empty())
members[f->getName()] = make_shared<FunctionType>(*f); members[f->getName()] = make_shared<FunctionType>(*f);
} }
m_members.reset(new MemberList(members)); m_members.reset(new MemberList(members));

28
libsolidity/Types.h

@ -41,6 +41,7 @@ namespace solidity
class Type; // forward class Type; // forward
class FunctionType; // forward class FunctionType; // forward
using TypePointer = std::shared_ptr<Type const>; using TypePointer = std::shared_ptr<Type const>;
using FunctionTypePointer = std::shared_ptr<FunctionType const>;
using TypePointers = std::vector<TypePointer>; 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 /// 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. /// 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 /// @returns the identifier of the function with the given name or Invalid256 if such a name does
/// not exist. /// not exist.
@ -307,7 +308,7 @@ private:
/// members. /// members.
bool m_super; bool m_super;
/// Type of the constructor, @see getConstructorType. Lazily initialized. /// 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. /// List of member types, will be lazy-initialized because of recursive references.
mutable std::unique_ptr<MemberList> m_members; mutable std::unique_ptr<MemberList> m_members;
}; };
@ -349,16 +350,18 @@ public:
/// INTERNAL: jump tag, EXTERNAL: contract address + function identifier, /// INTERNAL: jump tag, EXTERNAL: contract address + function identifier,
/// BARE: contract address (non-abi contract call) /// BARE: contract address (non-abi contract call)
/// OTHERS: special virtual function, nothing on the stack /// 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, enum class Location { INTERNAL, EXTERNAL, CREATION, SEND,
SHA3, SUICIDE, SHA3, SUICIDE,
ECRECOVER, SHA256, RIPEMD160, ECRECOVER, SHA256, RIPEMD160,
LOG0, LOG1, LOG2, LOG3, LOG4, LOG0, LOG1, LOG2, LOG3, LOG4, EVENT,
SET_GAS, SET_VALUE, BLOCKHASH, SET_GAS, SET_VALUE, BLOCKHASH,
BARE }; BARE };
virtual Category getCategory() const override { return Category::FUNCTION; } virtual Category getCategory() const override { return Category::FUNCTION; }
explicit FunctionType(FunctionDefinition const& _function, bool _isInternal = true); explicit FunctionType(FunctionDefinition const& _function, bool _isInternal = true);
explicit FunctionType(VariableDeclaration const& _varDecl); explicit FunctionType(VariableDeclaration const& _varDecl);
explicit FunctionType(EventDefinition const& _event);
FunctionType(strings const& _parameterTypes, strings const& _returnParameterTypes, FunctionType(strings const& _parameterTypes, strings const& _returnParameterTypes,
Location _location = Location::INTERNAL): Location _location = Location::INTERNAL):
FunctionType(parseElementaryTypeVector(_parameterTypes), parseElementaryTypeVector(_returnParameterTypes), FunctionType(parseElementaryTypeVector(_parameterTypes), parseElementaryTypeVector(_returnParameterTypes),
@ -371,8 +374,10 @@ public:
TypePointers const& getParameterTypes() const { return m_parameterTypes; } TypePointers const& getParameterTypes() const { return m_parameterTypes; }
std::vector<std::string> const& getParameterNames() const { return m_parameterNames; } std::vector<std::string> const& getParameterNames() const { return m_parameterNames; }
std::vector<std::string> const getParameterTypeNames() const;
TypePointers const& getReturnParameterTypes() const { return m_returnParameterTypes; } TypePointers const& getReturnParameterTypes() const { return m_returnParameterTypes; }
std::vector<std::string> const& getReturnParameterNames() const { return m_returnParameterNames; } 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 bool operator==(Type const& _other) const override;
virtual std::string toString() const override; virtual std::string toString() const override;
@ -383,7 +388,20 @@ public:
virtual MemberList const& getMembers() const override; virtual MemberList const& getMembers() const override;
Location const& getLocation() const { return m_location; } 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 gasSet() const { return m_gasSet; }
bool valueSet() const { return m_valueSet; } bool valueSet() const { return m_valueSet; }
@ -402,7 +420,9 @@ private:
Location const m_location; Location const m_location;
bool const m_gasSet = false; ///< true iff the gas value to be used is on the stack 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 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; 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["data"] = jsFromBinary(_e.data);
res["address"] = toJS(_e.address); res["address"] = toJS(_e.address);
for (auto const& t: _e.topics) for (auto const& t: _e.topics)
res["topics"].append(toJS(t)); res["topic"].append(toJS(t));
res["number"] = _e.number; res["number"] = _e.number;
return res; return res;
} }
@ -123,10 +123,10 @@ static dev::eth::LogFilter toLogFilter(Json::Value const& _json) // commented to
else if (_json["address"].isString()) else if (_json["address"].isString())
filter.address(jsToAddress(_json["address"].asString())); filter.address(jsToAddress(_json["address"].asString()));
} }
if (!_json["topics"].empty() && _json["topics"].isArray()) if (!_json["topic"].empty() && _json["topic"].isArray())
{ {
unsigned i = 0; unsigned i = 0;
for (auto t: _json["topics"]) for (auto t: _json["topic"])
{ {
if (t.isArray()) if (t.isArray())
for (auto tt: t) 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); 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; Public to;
if (_json["to"].isString()) if (_json["to"].isString())
@ -190,7 +190,7 @@ static pair<shh::TopicMask, Public> toWatch(Json::Value const& _json)
if (i.isString()) if (i.isString())
bt.shift(jsToBytes(i.asString())); 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) 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["sent"] = (int)_e.sent();
res["ttl"] = (int)_e.ttl(); res["ttl"] = (int)_e.ttl();
res["workProved"] = (int)_e.workProved(); res["workProved"] = (int)_e.workProved();
for (auto const& t: _e.topics()) for (auto const& t: _e.topic())
res["topics"].append(toJS(t)); res["topic"].append(toJS(t));
res["payload"] = toJS(_m.payload()); res["payload"] = toJS(_m.payload());
res["from"] = toJS(_m.from()); res["from"] = toJS(_m.from());
res["to"] = toJS(_m.to()); res["to"] = toJS(_m.to());
@ -576,12 +576,12 @@ Json::Value WebThreeStubServerBase::shh_changed(int const& _id)
if (pub) if (pub)
{ {
cwarn << "Silently decrypting message from identity" << pub.abridged() << ": User validation hook goes here."; cwarn << "Silently decrypting message from identity" << pub.abridged() << ": User validation hook goes here.";
m = e.open(m_ids[pub]); m = e.open(face()->fullTopic(_id), m_ids[pub]);
if (!m)
continue;
} }
else else
m = e.open(); m = e.open(face()->fullTopic(_id));
if (!m)
continue;
ret.append(toJson(h, e, m)); ret.append(toJson(h, e, m));
} }

24
libwhisper/Common.cpp

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

20
libwhisper/Common.h

@ -59,9 +59,14 @@ enum WhisperPacket
PacketCount 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 class BuildTopic
{ {
@ -74,8 +79,10 @@ public:
BuildTopic& shiftRaw(h256 const& _part) { m_parts.push_back(_part); return *this; } BuildTopic& shiftRaw(h256 const& _part) { m_parts.push_back(_part); return *this; }
operator Topic() const { return toTopic(); } operator CollapsedTopic() const { return toTopic(); }
Topic toTopic() const; operator FullTopic() const { return toFullTopic(); }
CollapsedTopic toTopic() const;
FullTopic toFullTopic() const { return m_parts; }
protected: protected:
BuildTopic& shiftBytes(bytes const& _b); BuildTopic& shiftBytes(bytes const& _b);
@ -83,13 +90,14 @@ protected:
h256s m_parts; h256s m_parts;
}; };
using TopicMask = std::vector<std::pair<TopicPart, TopicPart>>; using TopicMask = std::vector<std::pair<CollapsedTopicPart, CollapsedTopicPart>>;
using TopicMasks = std::vector<TopicMask>; using TopicMasks = std::vector<TopicMask>;
class TopicFilter class TopicFilter
{ {
public: public:
TopicFilter() {} 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(TopicMask const& _m): m_topicMasks(1, _m) {}
TopicFilter(TopicMasks const& _m): m_topicMasks(_m) {} TopicFilter(TopicMasks const& _m): m_topicMasks(_m) {}
TopicFilter(RLP const& _r)//: m_topicMasks(_r.toVector<std::vector<>>()) 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; } template <class T> BuildTopicMask& operator()(T const& _t) { shift(_t); return *this; }
operator TopicMask() const { return toTopicMask(); } operator TopicMask() const { return toTopicMask(); }
operator FullTopic() const { return toFullTopic(); }
TopicMask toTopicMask() const; TopicMask toTopicMask() const;
FullTopic toFullTopic() const { return m_parts; }
}; };
} }

3
libwhisper/Interface.cpp

@ -34,7 +34,6 @@ using namespace dev::shh;
#endif #endif
#define clogS(X) dev::LogOutputStream<X, true>(false) << "| " << std::setw(2) << session()->socketId() << "] " #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 struct InstalledFilter
{ {
InstalledFilter(TopicFilter const& _f): filter(_f) {} InstalledFilter(FullTopic const& _f): full(_f), filter(_f) {}
FullTopic full;
TopicFilter filter; TopicFilter filter;
unsigned refCount = 1; unsigned refCount = 1;
}; };
@ -65,12 +66,12 @@ struct ClientWatch
class Interface class Interface
{ {
public: public:
virtual ~Interface() {} virtual ~Interface();
virtual void inject(Envelope const& _m, WhisperPeer* _from = nullptr) = 0; virtual void inject(Envelope const& _m, WhisperPeer* _from = nullptr) = 0;
unsigned installWatch(TopicMask const& _mask); virtual FullTopic const& fullTopic(unsigned _id) const = 0;
virtual unsigned installWatch(TopicFilter const& _filter) = 0; virtual unsigned installWatch(FullTopic const& _mask) = 0;
virtual unsigned installWatchOnId(h256 _filterId) = 0; virtual unsigned installWatchOnId(h256 _filterId) = 0;
virtual void uninstallWatch(unsigned _watchId) = 0; virtual void uninstallWatch(unsigned _watchId) = 0;
virtual h256s peekWatch(unsigned _watchId) const = 0; virtual h256s peekWatch(unsigned _watchId) const = 0;
@ -79,10 +80,10 @@ public:
virtual Envelope envelope(h256 _m) const = 0; 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(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, Topic _topic, unsigned _ttl = 50, unsigned _workToProve = 50) { inject(Message(_payload).sealTo(_to, _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, Topic _topic, unsigned _ttl = 50, unsigned _workToProve = 50) { inject(Message(_payload).seal(_from, _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, Topic _topic, unsigned _ttl = 50, unsigned _workToProve = 50) { inject(Message(_payload).sealTo(_from, _to, _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; }; 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: public:
Watch() {} Watch() {}
Watch(Interface& _c, TopicMask const& _f): m_c(&_c), m_id(_c.installWatch(_f)) {} Watch(Interface& _c, FullTopic 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() { if (m_c) m_c->uninstallWatch(m_id); } ~Watch() { if (m_c) m_c->uninstallWatch(m_id); }
h256s check() { return m_c ? m_c->checkWatch(m_id) : h256s(); } h256s check() { return m_c ? m_c->checkWatch(m_id) : h256s(); }

66
libwhisper/Message.cpp

@ -26,7 +26,7 @@ using namespace dev;
using namespace dev::p2p; using namespace dev::p2p;
using namespace dev::shh; using namespace dev::shh;
Message::Message(Envelope const& _e, Secret const& _s) Message::Message(Envelope const& _e, FullTopic const& _fk, Secret const& _s)
{ {
try try
{ {
@ -34,8 +34,39 @@ Message::Message(Envelope const& _e, Secret const& _s)
if (_s) if (_s)
if (!decrypt(_s, &(_e.data()), b)) if (!decrypt(_s, &(_e.data()), b))
return; return;
if (populate(_s ? b : _e.data())) else{}
m_to = KeyPair(_s).pub(); 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 catch (...) // Invalid secret? TODO: replace ... with InvalidSecret
{ {
@ -63,9 +94,10 @@ bool Message::populate(bytes const& _data)
return true; 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()); bytes input(1 + m_payload.size());
input[0] = 0; input[0] = 0;
@ -83,7 +115,25 @@ Envelope Message::seal(Secret _from, Topic const& _topic, unsigned _ttl, unsigne
if (m_to) if (m_to)
encrypt(m_to, &input, ret.m_data); encrypt(m_to, &input, ret.m_data);
else 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); ret.proveWork(_workToProve);
return ret; return ret;
@ -98,9 +148,9 @@ Envelope::Envelope(RLP const& _m)
m_nonce = _m[4].toInt<u256>(); 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 unsigned Envelope::workProved() const

28
libwhisper/Message.h

@ -39,6 +39,16 @@ namespace shh
class Message; 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 enum IncludeNonce
{ {
WithoutNonce = 0, WithoutNonce = 0,
@ -61,22 +71,22 @@ public:
unsigned sent() const { return m_expiry - m_ttl; } unsigned sent() const { return m_expiry - m_ttl; }
unsigned expiry() const { return m_expiry; } unsigned expiry() const { return m_expiry; }
unsigned ttl() const { return m_ttl; } 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; } 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; unsigned workProved() const;
void proveWork(unsigned _ms); void proveWork(unsigned _ms);
private: 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_expiry = 0;
unsigned m_ttl = 0; unsigned m_ttl = 0;
u256 m_nonce; u256 m_nonce;
Topic m_topic; CollapsedTopic m_topic;
bytes m_data; bytes m_data;
}; };
@ -91,7 +101,7 @@ class Message
{ {
public: public:
Message() {} 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(bytes const& _payload): m_payload(_payload) {}
Message(bytesConstRef _payload): m_payload(_payload.toBytes()) {} Message(bytesConstRef _payload): m_payload(_payload.toBytes()) {}
Message(bytes&& _payload) { std::swap(_payload, m_payload); } Message(bytes&& _payload) { std::swap(_payload, m_payload); }
@ -108,11 +118,11 @@ public:
operator bool() const { return !!m_payload.size() || m_from || m_to; } operator bool() const { return !!m_payload.size() || m_from || m_to; }
/// Turn this message into a ditributable Envelope. /// 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. // 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 seal(FullTopic 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(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, Topic const& _topic, unsigned _workToProve = 50, unsigned _ttl = 50) { m_to = _to; return seal(_from, _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: private:
bool populate(bytes const& _data); bool populate(bytes const& _data);

11
libwhisper/WhisperHost.cpp

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

8
libwhisper/WhisperHost.h

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

4
libwhisper/WhisperPeer.h

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

5
mix/QContractDefinition.cpp

@ -34,10 +34,7 @@ using namespace dev::mix;
QContractDefinition::QContractDefinition(dev::solidity::ContractDefinition const* _contract): QBasicNodeDefinition(_contract) QContractDefinition::QContractDefinition(dev::solidity::ContractDefinition const* _contract): QBasicNodeDefinition(_contract)
{ {
if (_contract->getConstructor() != nullptr) if (_contract->getConstructor() != nullptr)
{ m_constructor = new QFunctionDefinition(ContractType(*_contract).getConstructorType());
FunctionDescription desc(_contract->getConstructor());
m_constructor = new QFunctionDefinition(desc);
}
else else
m_constructor = new QFunctionDefinition(); m_constructor = new QFunctionDefinition();

37
mix/QFunctionDefinition.cpp

@ -28,32 +28,15 @@
using namespace dev::solidity; using namespace dev::solidity;
using namespace dev::mix; using namespace dev::mix;
QFunctionDefinition::QFunctionDefinition(dev::solidity::FunctionDescription const& _f): QBasicNodeDefinition(_f.getDeclaration()), m_hash() QFunctionDefinition::QFunctionDefinition(dev::solidity::FunctionTypePointer const& _f): QBasicNodeDefinition(&_f->getDeclaration()), m_hash(dev::sha3(_f->getCanonicalSignature()))
{ {
auto paramNames = _f->getParameterNames();
FunctionDefinition const* funcDef; auto paramTypes = _f->getParameterTypeNames();
VariableDeclaration const* varDecl; auto returnNames = _f->getReturnParameterNames();
auto returnTypes = _f->getReturnParameterTypeNames();
if ((funcDef = _f.getFunctionDefinition())) for (unsigned i = 0; i < paramNames.size(); ++i)
{ m_parameters.append(new QVariableDeclaration(paramNames[i], paramTypes[i]));
m_hash = FixedHash<4>(dev::sha3(funcDef->getCanonicalSignature()));
std::vector<std::shared_ptr<VariableDeclaration>> parameters = funcDef->getParameterList().getParameters(); for (unsigned i = 0; i < returnNames.size(); ++i)
for (unsigned i = 0; i < parameters.size(); i++) m_returnParameters.append(new QVariableDeclaration(returnNames[i], returnTypes[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]));
}
} }

2
mix/QFunctionDefinition.h

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

2
mix/QVariableDeclaration.h

@ -37,7 +37,7 @@ class QVariableDeclaration: public QBasicNodeDefinition
public: public:
QVariableDeclaration() {} QVariableDeclaration() {}
QVariableDeclaration(solidity::VariableDeclaration const* _v): QBasicNodeDefinition(_v), m_type(QString::fromStdString(_v->getType()->toString())) {} 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; } QString type() const { return m_type; }
private: private:
QString m_type; QString m_type;

9
test/SolidityABIJSON.cpp

@ -273,6 +273,15 @@ BOOST_AUTO_TEST_CASE(const_function)
checkInterface(sourceCode, interface); checkInterface(sourceCode, interface);
} }
BOOST_AUTO_TEST_CASE(exclude_fallback_function)
{
char const* sourceCode = "contract test { function() {} }";
char const* interface = "[]";
checkInterface(sourceCode, interface);
}
BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END()
} }

10
test/SolidityCompiler.cpp

@ -96,7 +96,7 @@ BOOST_AUTO_TEST_CASE(smoke_test)
"}\n"; "}\n";
bytes code = compileContract(sourceCode); bytes code = compileContract(sourceCode);
unsigned boilerplateSize = 73; unsigned boilerplateSize = 69;
bytes expectation({byte(Instruction::JUMPDEST), bytes expectation({byte(Instruction::JUMPDEST),
byte(Instruction::PUSH1), 0x0, // initialize local variable x byte(Instruction::PUSH1), 0x0, // initialize local variable x
byte(Instruction::PUSH1), 0x2, byte(Instruction::PUSH1), 0x2,
@ -114,8 +114,8 @@ BOOST_AUTO_TEST_CASE(ifStatement)
" function f() { bool x; if (x) 77; else if (!x) 78; else 79; }" " function f() { bool x; if (x) 77; else if (!x) 78; else 79; }"
"}\n"; "}\n";
bytes code = compileContract(sourceCode); bytes code = compileContract(sourceCode);
unsigned shift = 60; unsigned shift = 56;
unsigned boilerplateSize = 73; unsigned boilerplateSize = 69;
bytes expectation({byte(Instruction::JUMPDEST), bytes expectation({byte(Instruction::JUMPDEST),
byte(Instruction::PUSH1), 0x0, byte(Instruction::PUSH1), 0x0,
byte(Instruction::DUP1), byte(Instruction::DUP1),
@ -155,8 +155,8 @@ BOOST_AUTO_TEST_CASE(loops)
" function f() { while(true){1;break;2;continue;3;return;4;} }" " function f() { while(true){1;break;2;continue;3;return;4;} }"
"}\n"; "}\n";
bytes code = compileContract(sourceCode); bytes code = compileContract(sourceCode);
unsigned shift = 60; unsigned shift = 56;
unsigned boilerplateSize = 73; unsigned boilerplateSize = 69;
bytes expectation({byte(Instruction::JUMPDEST), bytes expectation({byte(Instruction::JUMPDEST),
byte(Instruction::JUMPDEST), byte(Instruction::JUMPDEST),
byte(Instruction::PUSH1), 0x1, byte(Instruction::PUSH1), 0x1,

84
test/SolidityEndToEndTest.cpp

@ -916,7 +916,7 @@ BOOST_AUTO_TEST_CASE(multiple_elementary_accessors)
BOOST_CHECK(callContractFunction("name()") == encodeArgs("Celina")); BOOST_CHECK(callContractFunction("name()") == encodeArgs("Celina"));
BOOST_CHECK(callContractFunction("a_hash()") == encodeArgs(dev::sha3(toBigEndian(u256(123))))); BOOST_CHECK(callContractFunction("a_hash()") == encodeArgs(dev::sha3(toBigEndian(u256(123)))));
BOOST_CHECK(callContractFunction("an_address()") == encodeArgs(toBigEndian(u160(0x1337)))); BOOST_CHECK(callContractFunction("an_address()") == encodeArgs(toBigEndian(u160(0x1337))));
BOOST_CHECK(!(callContractFunction("super_secret_data()") == encodeArgs(42))); BOOST_CHECK(callContractFunction("super_secret_data()") == bytes());
} }
BOOST_AUTO_TEST_CASE(balance) BOOST_AUTO_TEST_CASE(balance)
@ -1955,6 +1955,88 @@ BOOST_AUTO_TEST_CASE(super_in_constructor)
BOOST_CHECK(callContractFunction("f()") == encodeArgs(1 | 2 | 4 | 8)); BOOST_CHECK(callContractFunction("f()") == encodeArgs(1 | 2 | 4 | 8));
} }
BOOST_AUTO_TEST_CASE(fallback_function)
{
char const* sourceCode = R"(
contract A {
uint data;
function() returns (uint r) { data = 1; return 2; }
function getData() returns (uint r) { return data; }
}
)";
compileAndRun(sourceCode);
BOOST_CHECK(callContractFunction("getData()") == encodeArgs(0));
BOOST_CHECK(callContractFunction("") == encodeArgs(2));
BOOST_CHECK(callContractFunction("getData()") == encodeArgs(1));
}
BOOST_AUTO_TEST_CASE(inherited_fallback_function)
{
char const* sourceCode = R"(
contract A {
uint data;
function() returns (uint r) { data = 1; return 2; }
function getData() returns (uint r) { return data; }
}
contract B is A {}
)";
compileAndRun(sourceCode, 0, "B");
BOOST_CHECK(callContractFunction("getData()") == encodeArgs(0));
BOOST_CHECK(callContractFunction("") == encodeArgs(2));
BOOST_CHECK(callContractFunction("getData()") == encodeArgs(1));
}
BOOST_AUTO_TEST_CASE(event)
{
char const* sourceCode = R"(
contract ClientReceipt {
event Deposit(address indexed _from, hash indexed _id, uint _value);
function deposit(hash _id, bool _manually) {
if (_manually) {
hash s = 0x50cb9fe53daa9737b786ab3646f04d0150dc50ef4e75f59509d83667ad5adb20;
log3(msg.value, s, hash32(msg.sender), _id);
} else
Deposit(hash32(msg.sender), _id, msg.value);
}
}
)";
compileAndRun(sourceCode);
u256 value(18);
u256 id(0x1234);
for (bool manually: {true, false})
{
callContractFunctionWithValue("deposit(hash256,bool)", value, id, manually);
BOOST_REQUIRE_EQUAL(m_logs.size(), 1);
BOOST_CHECK_EQUAL(m_logs[0].address, m_contractAddress);
BOOST_CHECK_EQUAL(h256(m_logs[0].data), h256(u256(value)));
BOOST_REQUIRE_EQUAL(m_logs[0].topics.size(), 3);
BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::sha3(string("Deposit(address,hash256,uint256)")));
BOOST_CHECK_EQUAL(m_logs[0].topics[1], h256(m_sender));
BOOST_CHECK_EQUAL(m_logs[0].topics[2], h256(id));
}
}
BOOST_AUTO_TEST_CASE(event_lots_of_data)
{
char const* sourceCode = R"(
contract ClientReceipt {
event Deposit(address _from, hash _id, uint _value, bool _flag);
function deposit(hash _id) {
Deposit(msg.sender, hash32(_id), msg.value, true);
}
}
)";
compileAndRun(sourceCode);
u256 value(18);
u256 id(0x1234);
callContractFunctionWithValue("deposit(hash256)", value, id);
BOOST_REQUIRE_EQUAL(m_logs.size(), 1);
BOOST_CHECK_EQUAL(m_logs[0].address, m_contractAddress);
BOOST_CHECK(m_logs[0].data == encodeArgs(m_sender, id, value, true));
BOOST_REQUIRE_EQUAL(m_logs[0].topics.size(), 1);
BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::sha3(string("Deposit(address,hash256,uint256,bool)")));
}
BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END()
} }

107
test/SolidityNameAndTypeResolution.cpp

@ -93,7 +93,7 @@ static ContractDefinition const* retrieveContract(ASTPointer<SourceUnit> _source
return NULL; return NULL;
} }
static FunctionDescription const& retrieveFunctionBySignature(ContractDefinition const* _contract, static FunctionTypePointer const& retrieveFunctionBySignature(ContractDefinition const* _contract,
std::string const& _signature) std::string const& _signature)
{ {
FixedHash<4> hash(dev::sha3(_signature)); FixedHash<4> hash(dev::sha3(_signature));
@ -643,11 +643,11 @@ BOOST_AUTO_TEST_CASE(state_variable_accessors)
ContractDefinition const* contract; ContractDefinition const* contract;
BOOST_CHECK_NO_THROW(source = parseTextAndResolveNamesWithChecks(text)); BOOST_CHECK_NO_THROW(source = parseTextAndResolveNamesWithChecks(text));
BOOST_REQUIRE((contract = retrieveContract(source, 0)) != nullptr); BOOST_REQUIRE((contract = retrieveContract(source, 0)) != nullptr);
FunctionDescription function = retrieveFunctionBySignature(contract, "foo()"); FunctionTypePointer function = retrieveFunctionBySignature(contract, "foo()");
BOOST_CHECK_MESSAGE(function.getDeclaration() != nullptr, "Could not find the accessor function"); BOOST_REQUIRE(function->hasDeclaration());
auto returnParams = function.getReturnParameters(); auto returnParams = function->getReturnParameterTypeNames();
BOOST_CHECK_EQUAL(returnParams.at(0).getType(), "uint256"); BOOST_CHECK_EQUAL(returnParams.at(0), "uint256");
BOOST_CHECK(function.isConstant()); BOOST_CHECK(function->isConstant());
} }
BOOST_AUTO_TEST_CASE(function_clash_with_state_variable_accessor) BOOST_AUTO_TEST_CASE(function_clash_with_state_variable_accessor)
@ -676,10 +676,101 @@ BOOST_AUTO_TEST_CASE(private_state_variable)
ContractDefinition const* contract; ContractDefinition const* contract;
BOOST_CHECK_NO_THROW(source = parseTextAndResolveNamesWithChecks(text)); BOOST_CHECK_NO_THROW(source = parseTextAndResolveNamesWithChecks(text));
BOOST_CHECK((contract = retrieveContract(source, 0)) != nullptr); BOOST_CHECK((contract = retrieveContract(source, 0)) != nullptr);
FunctionDescription function = retrieveFunctionBySignature(contract, "foo()"); FunctionTypePointer function = retrieveFunctionBySignature(contract, "foo()");
BOOST_CHECK_MESSAGE(function.getDeclaration() == nullptr, "Accessor function of a private variable should not exist"); BOOST_CHECK_MESSAGE(function == nullptr, "Accessor function of a private variable should not exist");
} }
BOOST_AUTO_TEST_CASE(fallback_function)
{
char const* text = R"(
contract C {
uint x;
function() { x = 2; }
}
)";
BOOST_CHECK_NO_THROW(parseTextAndResolveNames(text));
}
BOOST_AUTO_TEST_CASE(fallback_function_with_arguments)
{
char const* text = R"(
contract C {
uint x;
function(uint a) { x = 2; }
}
)";
BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError);
}
BOOST_AUTO_TEST_CASE(fallback_function_twice)
{
char const* text = R"(
contract C {
uint x;
function() { x = 2; }
function() { x = 3; }
}
)";
BOOST_CHECK_THROW(parseTextAndResolveNames(text), DeclarationError);
}
BOOST_AUTO_TEST_CASE(fallback_function_inheritance)
{
char const* text = R"(
contract A {
uint x;
function() { x = 1; }
}
contract C is A {
function() { x = 2; }
}
)";
BOOST_CHECK_NO_THROW(parseTextAndResolveNames(text));
}
BOOST_AUTO_TEST_CASE(event)
{
char const* text = R"(
contract c {
event e(uint indexed a, string3 indexed s, bool indexed b);
function f() { e(2, "abc", true); }
})";
BOOST_CHECK_NO_THROW(parseTextAndResolveNames(text));
}
BOOST_AUTO_TEST_CASE(event_too_many_indexed)
{
char const* text = R"(
contract c {
event e(uint indexed a, string3 indexed b, bool indexed c, uint indexed d);
function f() { e(2, "abc", true); }
})";
BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError);
}
BOOST_AUTO_TEST_CASE(event_call)
{
char const* text = R"(
contract c {
event e(uint a, string3 indexed s, bool indexed b);
function f() { e(2, "abc", true); }
})";
BOOST_CHECK_NO_THROW(parseTextAndResolveNames(text));
}
BOOST_AUTO_TEST_CASE(event_inheritance)
{
char const* text = R"(
contract base {
event e(uint a, string3 indexed s, bool indexed b);
}
contract c is base {
function f() { e(2, "abc", true); }
})";
BOOST_CHECK_NO_THROW(parseTextAndResolveNames(text));
}
BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END()
} }

35
test/SolidityParser.cpp

@ -586,6 +586,41 @@ BOOST_AUTO_TEST_CASE(modifier_invocation)
BOOST_CHECK_NO_THROW(parseText(text)); BOOST_CHECK_NO_THROW(parseText(text));
} }
BOOST_AUTO_TEST_CASE(fallback_function)
{
char const* text = "contract c {\n"
" function() { }\n"
"}\n";
BOOST_CHECK_NO_THROW(parseText(text));
}
BOOST_AUTO_TEST_CASE(event)
{
char const* text = R"(
contract c {
event e();
})";
BOOST_CHECK_NO_THROW(parseText(text));
}
BOOST_AUTO_TEST_CASE(event_arguments)
{
char const* text = R"(
contract c {
event e(uint a, string32 s);
})";
BOOST_CHECK_NO_THROW(parseText(text));
}
BOOST_AUTO_TEST_CASE(event_arguments_indexed)
{
char const* text = R"(
contract c {
event e(uint a, string32 indexed s, bool indexed b);
})";
BOOST_CHECK_NO_THROW(parseText(text));
}
BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END()
} }

1
test/solidityExecutionFramework.h

@ -67,7 +67,6 @@ public:
bytes const& callContractFunctionWithValue(std::string _sig, u256 const& _value, bytes const& callContractFunctionWithValue(std::string _sig, u256 const& _value,
Args const&... _arguments) Args const&... _arguments)
{ {
FixedHash<4> hash(dev::sha3(_sig)); FixedHash<4> hash(dev::sha3(_sig));
sendMessage(hash.asBytes() + encodeArgs(_arguments...), false, _value); sendMessage(hash.asBytes() + encodeArgs(_arguments...), false, _value);
return m_output; return m_output;

16
test/whisperTopic.cpp

@ -46,16 +46,16 @@ BOOST_AUTO_TEST_CASE(topic)
auto wh = ph.registerCapability(new WhisperHost()); auto wh = ph.registerCapability(new WhisperHost());
ph.start(); ph.start();
started = true;
/// Only interested in odd packets /// Only interested in odd packets
auto w = wh->installWatch(BuildTopicMask("odd")); auto w = wh->installWatch(BuildTopicMask("odd"));
for (int i = 0, last = 0; i < 200 && last < 81; ++i) started = true;
for (int iterout = 0, last = 0; iterout < 200 && last < 81; ++iterout)
{ {
for (auto i: wh->checkWatch(w)) for (auto i: wh->checkWatch(w))
{ {
Message msg = wh->envelope(i).open(); Message msg = wh->envelope(i).open(wh->fullTopic(w));
last = RLP(msg.payload()).toInt<unsigned>(); last = RLP(msg.payload()).toInt<unsigned>();
cnote << "New message from:" << msg.from().abridged() << RLP(msg.payload()).toInt<unsigned>(); cnote << "New message from:" << msg.from().abridged() << RLP(msg.payload()).toInt<unsigned>();
result += last; result += last;
@ -116,7 +116,7 @@ BOOST_AUTO_TEST_CASE(forwarding)
{ {
for (auto i: wh->checkWatch(w)) for (auto i: wh->checkWatch(w))
{ {
Message msg = wh->envelope(i).open(); Message msg = wh->envelope(i).open(wh->fullTopic(w));
unsigned last = RLP(msg.payload()).toInt<unsigned>(); unsigned last = RLP(msg.payload()).toInt<unsigned>();
cnote << "New message from:" << msg.from().abridged() << RLP(msg.payload()).toInt<unsigned>(); cnote << "New message from:" << msg.from().abridged() << RLP(msg.payload()).toInt<unsigned>();
result = last; result = last;
@ -152,7 +152,7 @@ BOOST_AUTO_TEST_CASE(forwarding)
{ {
for (auto i: wh->checkWatch(w)) for (auto i: wh->checkWatch(w))
{ {
Message msg = wh->envelope(i).open(); Message msg = wh->envelope(i).open(wh->fullTopic(w));
cnote << "New message from:" << msg.from().abridged() << RLP(msg.payload()).toInt<unsigned>(); cnote << "New message from:" << msg.from().abridged() << RLP(msg.payload()).toInt<unsigned>();
} }
this_thread::sleep_for(chrono::milliseconds(50)); this_thread::sleep_for(chrono::milliseconds(50));
@ -215,7 +215,7 @@ BOOST_AUTO_TEST_CASE(asyncforwarding)
{ {
for (auto i: wh->checkWatch(w)) for (auto i: wh->checkWatch(w))
{ {
Message msg = wh->envelope(i).open(); Message msg = wh->envelope(i).open(wh->fullTopic(w));
cnote << "New message from:" << msg.from().abridged() << RLP(msg.payload()).toInt<unsigned>(); cnote << "New message from:" << msg.from().abridged() << RLP(msg.payload()).toInt<unsigned>();
} }
this_thread::sleep_for(chrono::milliseconds(50)); this_thread::sleep_for(chrono::milliseconds(50));
@ -255,7 +255,7 @@ BOOST_AUTO_TEST_CASE(asyncforwarding)
{ {
for (auto i: wh->checkWatch(w)) for (auto i: wh->checkWatch(w))
{ {
Message msg = wh->envelope(i).open(); Message msg = wh->envelope(i).open(wh->fullTopic(w));
unsigned last = RLP(msg.payload()).toInt<unsigned>(); unsigned last = RLP(msg.payload()).toInt<unsigned>();
cnote << "New message from:" << msg.from().abridged() << RLP(msg.payload()).toInt<unsigned>(); cnote << "New message from:" << msg.from().abridged() << RLP(msg.payload()).toInt<unsigned>();
result = last; result = last;

Loading…
Cancel
Save