From 86db09cf66c19fd4d5073b5fc9518510b31368d0 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Wed, 28 Jan 2015 17:34:57 -0800 Subject: [PATCH 01/21] Revert to using require for Solidity - it works. --- libsolidity/CompilerStack.cpp | 57 ++++++++++++++++++++++++++++++++--- libsolidity/CompilerStack.h | 4 +++ 2 files changed, 57 insertions(+), 4 deletions(-) diff --git a/libsolidity/CompilerStack.cpp b/libsolidity/CompilerStack.cpp index 0b8218bb3..28687e909 100644 --- a/libsolidity/CompilerStack.cpp +++ b/libsolidity/CompilerStack.cpp @@ -41,7 +41,7 @@ namespace solidity { const map StandardSources = map{ - {"coin", R"(import "CoinReg";import "Config";import "configUser";contract coin is configUser{function coin(string3 name, uint denom) {CoinReg(Config(configAddr()).lookup(3)).register(name, denom);}})"}, +/* {"coin", R"(import "CoinReg";import "Config";import "configUser";contract coin is configUser{function coin(string3 name, uint denom) {CoinReg(Config(configAddr()).lookup(3)).register(name, denom);}})"}, {"Coin", R"(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", R"(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(){}})"}, {"configUser", R"(contract configUser{function configAddr()constant returns(address a){ return 0xc6d9d2cd449a754c494264e1809c50e34d64562b;}})"}, @@ -51,7 +51,7 @@ const map StandardSources = map{ {"NameReg", R"(contract NameReg{function register(string32 name){}function addressOf(string32 name)constant returns(address addr){}function unregister(){}function nameOf(address addr)constant returns(string32 name){}})"}, {"owned", R"(contract owned{function owned(){owner = msg.sender;}modifier onlyowner(){if(msg.sender==owner)_}address owner;})"}, {"service", R"(import "Config";import "configUser";contract service is configUser{function service(uint _n){Config(configAddr()).register(_n, this);}})"}, - {"std", R"(import "owned";import "mortal";import "Config";import "configUser";import "NameReg";import "named";)"} + {"std", R"(import "owned";import "mortal";import "Config";import "configUser";import "NameReg";import "named";)"}*/ }; CompilerStack::CompilerStack(bool _addStandardSources): @@ -65,14 +65,14 @@ bool CompilerStack::addSource(string const& _name, string const& _content) { bool existed = m_sources.count(_name) != 0; reset(true); - m_sources[_name].scanner = make_shared(CharStream(_content), _name); + m_sources[_name].scanner = make_shared(CharStream(expanded(_content)), _name); return existed; } void CompilerStack::setSource(string const& _sourceCode) { reset(); - addSource("", _sourceCode); + addSource("", expanded(_sourceCode)); } void CompilerStack::parse() @@ -125,6 +125,55 @@ vector CompilerStack::getContractNames() const return contractNames; } +////// BEGIN: TEMPORARY ONLY +/// remove once import works properly and we have genesis contracts + +string CompilerStack::expanded(string const& _sourceCode) +{ + const map c_standardSources = map{ + { "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 got; + function localExpanded; + localExpanded = [&](string const& s) -> string + { + string ret = s; + for (size_t p = 0; p != string::npos;) + if ((p = ret.find("#require ")) != string::npos) + { + string n = ret.substr(p + 9, ret.find_first_of('\n', p + 9) - p - 9); + ret.replace(p, n.size() + 9, ""); + vector rs; + boost::split(rs, n, boost::is_any_of(" \t,"), boost::token_compress_on); + for (auto const& r: rs) + if (!got.count(r)) + { + if (c_standardSources.count(r)) + sub.append("\n" + localExpanded(c_standardSources.at(r)) + "\n"); + got.insert(r); + } + } + // TODO: remove once we have genesis contracts. + else if ((p = ret.find("Config()")) != string::npos) + ret.replace(p, 8, "Config(0xc6d9d2cd449a754c494264e1809c50e34d64562b)"); + return ret; + }; + return sub + localExpanded(_sourceCode); +} + +////// END: TEMPORARY ONLY + void CompilerStack::compile(bool _optimize) { if (!m_parseSuccessful) diff --git a/libsolidity/CompilerStack.h b/libsolidity/CompilerStack.h index cae0f4e27..66b05d421 100644 --- a/libsolidity/CompilerStack.h +++ b/libsolidity/CompilerStack.h @@ -142,6 +142,10 @@ private: Contract(); }; + /// Expand source code with preprocessor-like includes. + /// @todo Replace with better framework. + std::string expanded(std::string const& _sourceCode); + void reset(bool _keepSources = false); void resolveImports(); From 74cacce2da23afc33972919b9187e4a574d5de2f Mon Sep 17 00:00:00 2001 From: Christian Date: Thu, 29 Jan 2015 12:17:37 +0100 Subject: [PATCH 02/21] Common class that contains AST node documentations. --- libsolidity/AST.h | 80 ++++++++++++++++++++++++++--------------------- 1 file changed, 44 insertions(+), 36 deletions(-) diff --git a/libsolidity/AST.h b/libsolidity/AST.h index f397c13ad..f3b18d392 100755 --- a/libsolidity/AST.h +++ b/libsolidity/AST.h @@ -157,6 +157,10 @@ private: }; + +/// Traits and Helpers (@todo: move to their own header) +/// @{ + /** * Generic Parameter description used by @see FunctionDescription to return * a descripton of its parameters. @@ -217,13 +221,44 @@ struct FunctionDescription std::pair, Declaration const*> m_description; }; +/** + * 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 const& getLocalVariables() const { return m_localVariables; } + +private: + std::vector m_localVariables; +}; + +/** + * Abstract class that is added to each AST node that can receive documentation. + */ +class Documented +{ +public: + explicit Documented(ASTPointer const& _documentation): m_documentation(_documentation) {} + + /// @return A shared pointer of an ASTString. + /// Can contain a nullptr in which case indicates absence of documentation + ASTPointer const& getDocumentation() const { return m_documentation; } + +protected: + ASTPointer m_documentation; +}; + +/// @} + /** * 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 * finally all function declarations. */ -class ContractDefinition: public Declaration +class ContractDefinition: public Declaration, public Documented { public: ContractDefinition(Location const& _location, @@ -234,13 +269,12 @@ public: std::vector> const& _stateVariables, std::vector> const& _definedFunctions, std::vector> const& _functionModifiers): - Declaration(_location, _name), + Declaration(_location, _name), Documented(_documentation), m_baseContracts(_baseContracts), m_definedStructs(_definedStructs), m_stateVariables(_stateVariables), m_definedFunctions(_definedFunctions), - m_functionModifiers(_functionModifiers), - m_documentation(_documentation) + m_functionModifiers(_functionModifiers) {} virtual void accept(ASTVisitor& _visitor) override; @@ -258,10 +292,6 @@ public: /// and calls checkTypeRequirements on all its functions. void checkTypeRequirements(); - /// @return A shared pointer of an ASTString. - /// Can contain a nullptr in which case indicates absence of documentation - ASTPointer const& getDocumentation() const { return m_documentation; } - /// @returns a map of canonical function signatures to FunctionDefinitions /// as intended for use by the ABI. std::map, FunctionDescription> getInterfaceFunctions() const; @@ -284,7 +314,6 @@ private: std::vector> m_stateVariables; std::vector> m_definedFunctions; std::vector> m_functionModifiers; - ASTPointer m_documentation; std::vector m_linearizedBaseContracts; mutable std::unique_ptr, std::shared_ptr, Declaration const*>>> m_interfaceFunctionList; @@ -355,20 +384,7 @@ private: std::vector> m_parameters; }; -/** - * 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 const& getLocalVariables() const { return m_localVariables; } - -private: - std::vector m_localVariables; -}; - -class FunctionDefinition: public Declaration, public VariableScope +class FunctionDefinition: public Declaration, public VariableScope, public Documented { public: FunctionDefinition(Location const& _location, ASTPointer const& _name, @@ -380,13 +396,13 @@ public: std::vector> const& _modifiers, ASTPointer const& _returnParameters, ASTPointer 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_isDeclaredConst(_isDeclaredConst), m_functionModifiers(_modifiers), m_returnParameters(_returnParameters), - m_body(_body), - m_documentation(_documentation) + m_body(_body) {} virtual void accept(ASTVisitor& _visitor) override; @@ -401,9 +417,6 @@ public: std::vector> const& getReturnParameters() const { return m_returnParameters->getParameters(); } ASTPointer const& getReturnParameterList() const { return m_returnParameters; } 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 const& getDocumentation() const { return m_documentation; } virtual TypePointer getType(ContractDefinition const*) const override; @@ -423,7 +436,6 @@ private: std::vector> m_functionModifiers; ASTPointer m_returnParameters; ASTPointer m_body; - ASTPointer m_documentation; }; /** @@ -463,7 +475,7 @@ private: /** * Definition of a function modifier. */ -class ModifierDefinition: public Declaration, public VariableScope +class ModifierDefinition: public Declaration, public VariableScope, public Documented { public: ModifierDefinition(Location const& _location, @@ -471,7 +483,7 @@ public: ASTPointer const& _documentation, ASTPointer const& _parameters, ASTPointer const& _body): - Declaration(_location, _name), m_documentation(_documentation), + Declaration(_location, _name), Documented(_documentation), m_parameters(_parameters), m_body(_body) {} virtual void accept(ASTVisitor& _visitor) override; @@ -483,14 +495,10 @@ public: 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 const& getDocumentation() const { return m_documentation; } void checkTypeRequirements(); private: - ASTPointer m_documentation; ASTPointer m_parameters; ASTPointer m_body; }; From 51fb3b6f75b7376990575a292c6962eda7934895 Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Thu, 29 Jan 2015 10:44:29 +0100 Subject: [PATCH 03/21] No longer exposing retrieveValueFromStorage() as a public function - plus small fix in EndToEndTests --- libsolidity/BaseTypes.h | 4 ++++ libsolidity/ExpressionCompiler.cpp | 22 +++++++++++----------- libsolidity/ExpressionCompiler.h | 10 ++++++---- test/SolidityEndToEndTest.cpp | 2 +- test/SolidityNameAndTypeResolution.cpp | 2 +- 5 files changed, 23 insertions(+), 17 deletions(-) diff --git a/libsolidity/BaseTypes.h b/libsolidity/BaseTypes.h index a8fd77c86..057289ef3 100644 --- a/libsolidity/BaseTypes.h +++ b/libsolidity/BaseTypes.h @@ -41,6 +41,8 @@ struct Location start(_start), end(_end), sourceName(_sourceName) { } Location(): start(-1), end(-1) { } + bool isEmpty() const { return start == -1 && end == -1; } + int start; int end; std::shared_ptr sourceName; @@ -49,6 +51,8 @@ struct Location /// Stream output for Location (used e.g. in boost exceptions). 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 << ")"; } diff --git a/libsolidity/ExpressionCompiler.cpp b/libsolidity/ExpressionCompiler.cpp index bcd90acfc..5d44c86f3 100644 --- a/libsolidity/ExpressionCompiler.cpp +++ b/libsolidity/ExpressionCompiler.cpp @@ -66,7 +66,7 @@ bool ExpressionCompiler::visit(Assignment const& _assignment) { if (m_currentLValue.storesReferenceOnStack()) 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()); if (m_currentLValue.storesReferenceOnStack()) m_context << eth::Instruction::SWAP1; @@ -107,7 +107,7 @@ bool ExpressionCompiler::visit(UnaryOperation const& _unaryOperation) case Token::INC: // ++ (pre- or postfix) case Token::DEC: // -- (pre- or postfix) solAssert(m_currentLValue.isValid(), "LValue not retrieved."); - m_currentLValue.retrieveValue(_unaryOperation); + m_currentLValue.retrieveValue(_unaryOperation.getType(), _unaryOperation.getLocation()); if (!_unaryOperation.isPrefixOperation()) { if (m_currentLValue.storesReferenceOnStack()) @@ -811,7 +811,7 @@ void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const& { m_currentLValue.fromStateVariable(_varDecl, _varDecl.getType()); 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, @@ -826,7 +826,7 @@ ExpressionCompiler::LValue::LValue(CompilerContext& _compilerContext, LValueType 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) { @@ -834,23 +834,23 @@ void ExpressionCompiler::LValue::retrieveValue(Expression const& _expression, bo { unsigned stackPos = m_context->baseToCurrentStackOffset(unsigned(m_baseStackOffset)); 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.")); for (unsigned i = 0; i < m_size; ++i) *m_context << eth::dupInstruction(stackPos + 1); break; } case STORAGE: - retrieveValueFromStorage(_expression.getType(), _remove); + retrieveValueFromStorage(_type, _remove); break; case MEMORY: - if (!_expression.getType()->isValueType()) + if (!_type->isValueType()) 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.")); break; default: - BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_sourceLocation(_expression.getLocation()) + BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_sourceLocation(_location) << errinfo_comment("Unsupported location type.")); break; } @@ -889,7 +889,7 @@ void ExpressionCompiler::LValue::storeValue(Expression const& _expression, bool for (unsigned i = 0; i < m_size; ++i) *m_context << eth::swapInstruction(stackDiff) << eth::Instruction::POP; if (!_move) - retrieveValue(_expression); + retrieveValue(_expression.getType(), _expression.getLocation()); break; } case LValue::STORAGE: @@ -976,7 +976,7 @@ void ExpressionCompiler::LValue::retrieveValueIfLValueNotRequested(Expression co { if (!_expression.lvalueRequested()) { - retrieveValue(_expression, true); + retrieveValue(_expression.getType(), _expression.getLocation(), true); reset(); } } diff --git a/libsolidity/ExpressionCompiler.h b/libsolidity/ExpressionCompiler.h index b4a64594d..748cc6c6f 100644 --- a/libsolidity/ExpressionCompiler.h +++ b/libsolidity/ExpressionCompiler.h @@ -130,10 +130,9 @@ private: /// 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). - /// @a _expression is the current expression, used for error reporting. - void retrieveValue(Expression const& _expression, bool _remove = false) const; - /// Convenience function to retrieve Value from Storage. Specific version of @ref retrieveValue - void retrieveValueFromStorage(TypePointer const& _type, bool _remove = false) const; + /// @a _type is the type of the current expression and @ _location its location, used for error reporting. + /// @a _location can be a nullptr for expressions that don't have an actual ASTNode equivalent + void retrieveValue(TypePointer const& _type, Location const& _location, bool _remove = false) const; /// 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. /// Also removes the stored value from the stack if @a _move is @@ -147,6 +146,9 @@ private: void retrieveValueIfLValueNotRequested(Expression const& _expression); 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; LValueType m_type = NONE; /// If m_type is STACK, this is base stack offset (@see diff --git a/test/SolidityEndToEndTest.cpp b/test/SolidityEndToEndTest.cpp index 1450095af..5f1a0993c 100644 --- a/test/SolidityEndToEndTest.cpp +++ b/test/SolidityEndToEndTest.cpp @@ -916,7 +916,7 @@ BOOST_AUTO_TEST_CASE(multiple_elementary_accessors) BOOST_CHECK(callContractFunction("name()") == encodeArgs("Celina")); BOOST_CHECK(callContractFunction("a_hash()") == encodeArgs(dev::sha3(toBigEndian(u256(123))))); 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) diff --git a/test/SolidityNameAndTypeResolution.cpp b/test/SolidityNameAndTypeResolution.cpp index 979836ecc..5ae854bc0 100644 --- a/test/SolidityNameAndTypeResolution.cpp +++ b/test/SolidityNameAndTypeResolution.cpp @@ -644,7 +644,7 @@ BOOST_AUTO_TEST_CASE(state_variable_accessors) BOOST_CHECK_NO_THROW(source = parseTextAndResolveNamesWithChecks(text)); BOOST_REQUIRE((contract = retrieveContract(source, 0)) != nullptr); FunctionDescription function = retrieveFunctionBySignature(contract, "foo()"); - BOOST_CHECK_MESSAGE(function.getDeclaration() != nullptr, "Could not find the accessor function"); + BOOST_REQUIRE(function.getDeclaration() != nullptr); auto returnParams = function.getReturnParameters(); BOOST_CHECK_EQUAL(returnParams.at(0).getType(), "uint256"); BOOST_CHECK(function.isConstant()); From 86b70485a615c65919513426d407d7ab3c498a6f Mon Sep 17 00:00:00 2001 From: Marek Kotewicz Date: Thu, 29 Jan 2015 15:26:42 +0100 Subject: [PATCH 04/21] Squashed 'libjsqrc/ethereumjs/' changes from 6d59047..94e0e5a 94e0e5a Merge branch 'cpp' into cpp2 8613382 moved comment df17c33 event example c8ee08c contract.js simplified 842b8cf event.js e1c0862 Fix for API. 61e8ae2 events init 2544d2c tests for abi.filters ea7c2fc abi function type 63d9c07 fixed incoming messages 1345a8c log error on console, if api returns an error 83fad0f removed fromFixed, toFixed && offset from tests c2cb2be removed web3.eth.account, fixed #37 09f6335 fixed #23 42a25f2 evaluating solidity method input params git-subtree-dir: libjsqrc/ethereumjs git-subtree-split: 94e0e5ab7d8ec9adcd03fedc3abe5cf6444a5123 --- dist/ethereum.js | 285 +++++++++++++++++++++++++--------- dist/ethereum.js.map | 14 +- dist/ethereum.min.js | 2 +- example/balance.html | 2 +- example/contract.html | 1 + example/event.html | 67 ++++++++ example/natspec_contract.html | 1 + lib/abi.js | 26 +++- lib/contract.js | 183 ++++++++++++++-------- lib/event.js | 35 +++++ lib/filter.js | 15 +- lib/providermanager.js | 6 + lib/web3.js | 5 +- test/abi.filters.js | 49 ++++++ test/abi.parsers.js | 37 +++++ test/eth.contract.js | 201 ++++++++++++++++++++++++ test/eth.methods.js | 1 - test/event.js | 22 +++ test/web3.methods.js | 3 - 19 files changed, 801 insertions(+), 154 deletions(-) create mode 100644 example/event.html create mode 100644 lib/event.js create mode 100644 test/abi.filters.js create mode 100644 test/eth.contract.js create mode 100644 test/event.js diff --git a/dist/ethereum.js b/dist/ethereum.js index 7bcf86c81..fc7bf09ca 100644 --- a/dist/ethereum.js +++ b/dist/ethereum.js @@ -66,6 +66,22 @@ var getMethodWithName = function (json, methodName) { 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 number of characters that result string should have /// @param sign, by default 0 @@ -212,6 +228,7 @@ var signedIsNegative = function (value) { /// Formats input right-aligned input bytes to int /// @returns right-aligned input bytes formatted to int var formatOutputInt = function (value) { + value = value || "0"; // check if it's negative number // it it is, return two's complement if (signedIsNegative(value)) { @@ -223,6 +240,7 @@ var formatOutputInt = function (value) { /// Formats big right-aligned input bytes to uint /// @returns right-aligned input bytes formatted to uint var formatOutputUInt = function (value) { + value = value || "0"; return new BigNumber(value, 16); }; @@ -350,7 +368,7 @@ var methodTypeName = function (method) { /// @returns input parser object for given json abi var inputParser = function (json) { var parser = {}; - json.forEach(function (method) { + filterFunctions(json).forEach(function (method) { var displayName = methodDisplayName(method.name); var typeName = methodTypeName(method.name); @@ -373,7 +391,7 @@ var inputParser = function (json) { /// @returns output parser for given json abi var outputParser = function (json) { var parser = {}; - json.forEach(function (method) { + filterFunctions(json).forEach(function (method) { var displayName = methodDisplayName(method.name); var typeName = methodTypeName(method.name); @@ -404,11 +422,13 @@ module.exports = { methodSignature: methodSignature, methodDisplayName: methodDisplayName, 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. @@ -431,71 +451,40 @@ module.exports = { * @date 2014 */ -var web3 = require('./web3'); // jshint ignore:line +var web3 = require('./web3'); var abi = require('./abi'); +var eventImpl = require('./event'); -/** - * This method should be called when we want to call / transact some solidity method from javascript - * it returns an object which has same methods available as solidity contract description - * usage example: - * - * var abi = [{ - * name: 'myMethod', - * inputs: [{ name: 'a', type: 'string' }], - * outputs: [{name: 'd', type: 'string' }] - * }]; // contract abi - * - * var myContract = web3.eth.contract('0x0123123121', abi); // creation of contract object - * - * myContract.myMethod('this is test string param for call'); // myMethod call (implicit, default) - * myContract.call().myMethod('this is test string param for call'); // myMethod call (explicit) - * myContract.transact().myMethod('this is test string param for transact'); // myMethod transact - * - * @param address - address of the contract, which should be called - * @param desc - abi json description of the contract, which is being created - * @returns contract object - */ - -var contract = function (address, desc) { - - desc.forEach(function (method) { - // workaround for invalid assumption that method.name is the full anonymous prototype of the method. - // it's not. it's just the name. the rest of the code assumes it's actually the anonymous - // prototype, so we make it so as a workaround. - if (method.name.indexOf('(') === -1) { - var displayName = method.name; - var typeName = method.inputs.map(function(i){return i.type; }).join(); - method.name = displayName + '(' + typeName + ')'; - } - }); - - var inputParser = abi.inputParser(desc); - var outputParser = abi.outputParser(desc); - - var result = {}; - - result.call = function (options) { - result._isTransact = false; - result._options = options; - return result; +var addFunctionRelatedPropertiesToContract = function (contract) { + + contract.call = function (options) { + contract._isTransact = false; + contract._options = options; + return contract; }; - result.transact = function (options) { - result._isTransact = true; - result._options = options; - return result; + contract.transact = function (options) { + contract._isTransact = true; + contract._options = options; + return contract; }; - result._options = {}; + contract._options = {}; ['gas', 'gasPrice', 'value', 'from'].forEach(function(p) { - result[p] = function (v) { - result._options[p] = v; - return result; + contract[p] = function (v) { + contract._options[p] = v; + return contract; }; }); +}; - desc.forEach(function (method) { +var addFunctionsToContract = function (contract, desc, address) { + var inputParser = abi.inputParser(desc); + var outputParser = abi.outputParser(desc); + + // create contract functions + abi.filterFunctions(desc).forEach(function (method) { var displayName = abi.methodDisplayName(method.name); var typeName = abi.methodTypeName(method.name); @@ -505,22 +494,24 @@ var contract = function (address, desc) { var signature = abi.methodSignature(method.name); var parsed = inputParser[displayName][typeName].apply(null, params); - var options = result._options || {}; + var options = contract._options || {}; options.to = address; options.data = signature + parsed; - var isTransact = result._isTransact === true || (result._isTransact !== false && !method.constant); + var isTransact = contract._isTransact === true || (contract._isTransact !== false && !method.constant); var collapse = options.collapse !== false; // reset - result._options = {}; - result._isTransact = null; + contract._options = {}; + contract._isTransact = null; if (isTransact) { // it's used byt natspec.js // TODO: figure out better way to solve this web3._currentContractAbi = desc; web3._currentContractAddress = address; + web3._currentContractMethodName = method.name; + web3._currentContractMethodParams = params; // transactions do not have any output, cause we do not know, when they will be processed web3.eth.transact(options); @@ -539,13 +530,102 @@ var contract = function (address, desc) { return ret; }; - if (result[displayName] === undefined) { - result[displayName] = impl; + if (contract[displayName] === undefined) { + contract[displayName] = impl; + } + + contract[displayName][typeName] = impl; + }); +}; + +var addEventRelatedPropertiesToContract = function (contract, desc, address) { + contract.address = address; + + Object.defineProperty(contract, 'topics', { + 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, 'topics', { + 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; } - result[displayName][typeName] = 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; }; @@ -553,7 +633,44 @@ var contract = function (address, desc) { 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 . +*/ +/** @file event.js + * @authors: + * Marek Kotewicz + * @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. @@ -587,6 +704,19 @@ var Filter = function(options, impl) { this.impl = impl; this.callbacks = []; + if (typeof options !== "string") { + // evaluate lazy properties + options = { + to: options.to, + topics: options.topics, + earliest: options.earliest, + latest: options.latest, + max: options.max, + skip: options.skip, + address: options.address + }; + } + this.id = impl.newFilter(options); web3.provider.startPolling({call: impl.changed, args: [this.id]}, this.id, this.trigger.bind(this)); }; @@ -604,7 +734,7 @@ Filter.prototype.changed = function(callback) { /// trigger calling new message from people Filter.prototype.trigger = function(messages) { for (var i = 0; i < this.callbacks.length; i++) { - for (var j = 0; j < messages; j++) { + for (var j = 0; j < messages.length; j++) { this.callbacks[i].call(this, messages[j]); } } @@ -628,7 +758,7 @@ Filter.prototype.logs = function () { module.exports = Filter; -},{"./web3":7}],4:[function(require,module,exports){ +},{"./web3":8}],5:[function(require,module,exports){ /* This file is part of ethereum.js. @@ -700,7 +830,7 @@ HttpSyncProvider.prototype.send = function (payload) { module.exports = HttpSyncProvider; -},{}],5:[function(require,module,exports){ +},{}],6:[function(require,module,exports){ /* This file is part of ethereum.js. @@ -779,6 +909,12 @@ ProviderManager.prototype.send = function(data) { //TODO: handle error here? var result = this.provider.send(data); result = JSON.parse(result); + + if (result.error) { + console.log(result.error); + return null; + } + return result.result; }; @@ -806,7 +942,7 @@ ProviderManager.prototype.stopPolling = function (pollId) { module.exports = ProviderManager; -},{"./web3":7}],6:[function(require,module,exports){ +},{"./web3":8}],7:[function(require,module,exports){ /* This file is part of ethereum.js. @@ -840,7 +976,7 @@ QtSyncProvider.prototype.send = function (payload) { module.exports = QtSyncProvider; -},{}],7:[function(require,module,exports){ +},{}],8:[function(require,module,exports){ /* This file is part of ethereum.js. @@ -941,7 +1077,6 @@ var ethProperties = function () { { name: 'listening', getter: 'eth_listening', setter: 'eth_setListening' }, { name: 'mining', getter: 'eth_mining', setter: 'eth_setMining' }, { name: 'gasPrice', getter: 'eth_gasPrice' }, - { name: 'account', getter: 'eth_account' }, { name: 'accounts', getter: 'eth_accounts' }, { name: 'peerCount', getter: 'eth_peerCount' }, { name: 'defaultBlock', getter: 'eth_defaultBlock', setter: 'eth_setDefaultBlock' }, @@ -1076,7 +1211,9 @@ var web3 = { /// @returns decimal representaton of hex value prefixed by 0x toDecimal: function (val) { - return (new BigNumber(val.substring(2), 16).toString(10)); + // remove 0x and place 0, if it's required + val = val.length > 2 ? val.substring(2) : "0"; + return (new BigNumber(val, 16).toString(10)); }, /// @returns hex representation (prefixed by 0x) of decimal value @@ -1181,7 +1318,7 @@ web3.abi = require('./lib/abi'); 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 \ No newline at end of file diff --git a/dist/ethereum.js.map b/dist/ethereum.js.map index 829d0a899..8f6bd1a1f 100644 --- a/dist/ethereum.js.map +++ b/dist/ethereum.js.map @@ -4,6 +4,7 @@ "node_modules/browserify/node_modules/browser-pack/_prelude.js", "lib/abi.js", "lib/contract.js", + "lib/event.js", "lib/filter.js", "lib/httpsync.js", "lib/providermanager.js", @@ -12,18 +13,19 @@ "index.js" ], "names": [], - "mappings": "AAAA;ACAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACxZA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AC/IA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACzEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACtEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACxGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AChCA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACtUA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA", + "mappings": "AAAA;ACAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AC5aA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AC3MA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACnCA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACtFA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACtEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AC9GA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AChCA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACvUA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA", "file": "generated.js", "sourceRoot": "", "sourcesContent": [ "(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require==\"function\"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error(\"Cannot find module '\"+o+\"'\");throw f.code=\"MODULE_NOT_FOUND\",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require==\"function\"&&require;for(var o=0;o.\n*/\n/** @file abi.js\n * @authors:\n * Marek Kotewicz \n * Gav Wood \n * @date 2014\n */\n\n// TODO: is these line is supposed to be here? \nif (\"build\" !== 'build') {/*\n var BigNumber = require('bignumber.js'); // jshint ignore:line\n*/}\n\nvar web3 = require('./web3'); // jshint ignore:line\n\nBigNumber.config({ ROUNDING_MODE: BigNumber.ROUND_DOWN });\n\nvar ETH_PADDING = 32;\n\n/// method signature length in bytes\nvar ETH_METHOD_SIGNATURE_LENGTH = 4;\n\n/// Finds first index of array element matching pattern\n/// @param array\n/// @param callback pattern\n/// @returns index of element\nvar findIndex = function (array, callback) {\n var end = false;\n var i = 0;\n for (; i < array.length && !end; i++) {\n end = callback(array[i]);\n }\n return end ? i - 1 : -1;\n};\n\n/// @returns a function that is used as a pattern for 'findIndex'\nvar findMethodIndex = function (json, methodName) {\n return findIndex(json, function (method) {\n return method.name === methodName;\n });\n};\n\n/// @returns method with given method name\nvar getMethodWithName = function (json, methodName) {\n var index = findMethodIndex(json, methodName);\n if (index === -1) {\n console.error('method ' + methodName + ' not found in the abi');\n return undefined;\n }\n return json[index];\n};\n\n/// @param string string to be padded\n/// @param number of characters that result string should have\n/// @param sign, by default 0\n/// @returns right aligned string\nvar padLeft = function (string, chars, sign) {\n return new Array(chars - string.length + 1).join(sign ? sign : \"0\") + string;\n};\n\n/// @param expected type prefix (string)\n/// @returns function which checks if type has matching prefix. if yes, returns true, otherwise false\nvar prefixedType = function (prefix) {\n return function (type) {\n return type.indexOf(prefix) === 0;\n };\n};\n\n/// @param expected type name (string)\n/// @returns function which checks if type is matching expected one. if yes, returns true, otherwise false\nvar namedType = function (name) {\n return function (type) {\n return name === type;\n };\n};\n\nvar arrayType = function (type) {\n return type.slice(-2) === '[]';\n};\n\n/// Formats input value to byte representation of int\n/// If value is negative, return it's two's complement\n/// If the value is floating point, round it down\n/// @returns right-aligned byte representation of int\nvar formatInputInt = function (value) {\n var padding = ETH_PADDING * 2;\n if (value instanceof BigNumber || typeof value === 'number') {\n if (typeof value === 'number')\n value = new BigNumber(value);\n value = value.round();\n\n if (value.lessThan(0)) \n value = new BigNumber(\"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff\", 16).plus(value).plus(1);\n value = value.toString(16);\n }\n else if (value.indexOf('0x') === 0)\n value = value.substr(2);\n else if (typeof value === 'string')\n value = formatInputInt(new BigNumber(value));\n else\n value = (+value).toString(16);\n return padLeft(value, padding);\n};\n\n/// Formats input value to byte representation of string\n/// @returns left-algined byte representation of string\nvar formatInputString = function (value) {\n return web3.fromAscii(value, ETH_PADDING).substr(2);\n};\n\n/// Formats input value to byte representation of bool\n/// @returns right-aligned byte representation bool\nvar formatInputBool = function (value) {\n return '000000000000000000000000000000000000000000000000000000000000000' + (value ? '1' : '0');\n};\n\n/// Formats input value to byte representation of real\n/// Values are multiplied by 2^m and encoded as integers\n/// @returns byte representation of real\nvar formatInputReal = function (value) {\n return formatInputInt(new BigNumber(value).times(new BigNumber(2).pow(128))); \n};\n\nvar dynamicTypeBytes = function (type, value) {\n // TODO: decide what to do with array of strings\n if (arrayType(type) || type === 'string') // only string itself that is dynamic; stringX is static length.\n return formatInputInt(value.length); \n return \"\";\n};\n\n/// Setups input formatters for solidity types\n/// @returns an array of input formatters \nvar setupInputTypes = function () {\n \n return [\n { type: prefixedType('uint'), format: formatInputInt },\n { type: prefixedType('int'), format: formatInputInt },\n { type: prefixedType('hash'), format: formatInputInt },\n { type: prefixedType('string'), format: formatInputString }, \n { type: prefixedType('real'), format: formatInputReal },\n { type: prefixedType('ureal'), format: formatInputReal },\n { type: namedType('address'), format: formatInputInt },\n { type: namedType('bool'), format: formatInputBool }\n ];\n};\n\nvar inputTypes = setupInputTypes();\n\n/// Formats input params to bytes\n/// @param contract json abi\n/// @param name of the method that we want to use\n/// @param array of params that will be formatted to bytes\n/// @returns bytes representation of input params\nvar toAbiInput = function (json, methodName, params) {\n var bytes = \"\";\n\n var method = getMethodWithName(json, methodName);\n var padding = ETH_PADDING * 2;\n\n /// first we iterate in search for dynamic \n method.inputs.forEach(function (input, index) {\n bytes += dynamicTypeBytes(input.type, params[index]);\n });\n\n method.inputs.forEach(function (input, i) {\n var typeMatch = false;\n for (var j = 0; j < inputTypes.length && !typeMatch; j++) {\n typeMatch = inputTypes[j].type(method.inputs[i].type, params[i]);\n }\n if (!typeMatch) {\n console.error('input parser does not support type: ' + method.inputs[i].type);\n }\n\n var formatter = inputTypes[j - 1].format;\n var toAppend = \"\";\n\n if (arrayType(method.inputs[i].type))\n toAppend = params[i].reduce(function (acc, curr) {\n return acc + formatter(curr);\n }, \"\");\n else\n toAppend = formatter(params[i]);\n\n bytes += toAppend; \n });\n return bytes;\n};\n\n/// Check if input value is negative\n/// @param value is hex format\n/// @returns true if it is negative, otherwise false\nvar signedIsNegative = function (value) {\n return (new BigNumber(value.substr(0, 1), 16).toString(2).substr(0, 1)) === '1';\n};\n\n/// Formats input right-aligned input bytes to int\n/// @returns right-aligned input bytes formatted to int\nvar formatOutputInt = function (value) {\n // check if it's negative number\n // it it is, return two's complement\n if (signedIsNegative(value)) {\n return new BigNumber(value, 16).minus(new BigNumber('ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', 16)).minus(1);\n }\n return new BigNumber(value, 16);\n};\n\n/// Formats big right-aligned input bytes to uint\n/// @returns right-aligned input bytes formatted to uint\nvar formatOutputUInt = function (value) {\n return new BigNumber(value, 16);\n};\n\n/// @returns input bytes formatted to real\nvar formatOutputReal = function (value) {\n return formatOutputInt(value).dividedBy(new BigNumber(2).pow(128)); \n};\n\n/// @returns input bytes formatted to ureal\nvar formatOutputUReal = function (value) {\n return formatOutputUInt(value).dividedBy(new BigNumber(2).pow(128)); \n};\n\n/// @returns right-aligned input bytes formatted to hex\nvar formatOutputHash = function (value) {\n return \"0x\" + value;\n};\n\n/// @returns right-aligned input bytes formatted to bool\nvar formatOutputBool = function (value) {\n return value === '0000000000000000000000000000000000000000000000000000000000000001' ? true : false;\n};\n\n/// @returns left-aligned input bytes formatted to ascii string\nvar formatOutputString = function (value) {\n return web3.toAscii(value);\n};\n\n/// @returns right-aligned input bytes formatted to address\nvar formatOutputAddress = function (value) {\n return \"0x\" + value.slice(value.length - 40, value.length);\n};\n\nvar dynamicBytesLength = function (type) {\n if (arrayType(type) || type === 'string') // only string itself that is dynamic; stringX is static length.\n return ETH_PADDING * 2;\n return 0;\n};\n\n/// Setups output formaters for solidity types\n/// @returns an array of output formatters\nvar setupOutputTypes = function () {\n\n return [\n { type: prefixedType('uint'), format: formatOutputUInt },\n { type: prefixedType('int'), format: formatOutputInt },\n { type: prefixedType('hash'), format: formatOutputHash },\n { type: prefixedType('string'), format: formatOutputString },\n { type: prefixedType('real'), format: formatOutputReal },\n { type: prefixedType('ureal'), format: formatOutputUReal },\n { type: namedType('address'), format: formatOutputAddress },\n { type: namedType('bool'), format: formatOutputBool }\n ];\n};\n\nvar outputTypes = setupOutputTypes();\n\n/// Formats output bytes back to param list\n/// @param contract json abi\n/// @param name of the method that we want to use\n/// @param bytes representtion of output \n/// @returns array of output params \nvar fromAbiOutput = function (json, methodName, output) {\n \n output = output.slice(2);\n var result = [];\n var method = getMethodWithName(json, methodName);\n var padding = ETH_PADDING * 2;\n\n var dynamicPartLength = method.outputs.reduce(function (acc, curr) {\n return acc + dynamicBytesLength(curr.type);\n }, 0);\n \n var dynamicPart = output.slice(0, dynamicPartLength);\n output = output.slice(dynamicPartLength);\n\n method.outputs.forEach(function (out, i) {\n var typeMatch = false;\n for (var j = 0; j < outputTypes.length && !typeMatch; j++) {\n typeMatch = outputTypes[j].type(method.outputs[i].type);\n }\n\n if (!typeMatch) {\n console.error('output parser does not support type: ' + method.outputs[i].type);\n }\n\n var formatter = outputTypes[j - 1].format;\n if (arrayType(method.outputs[i].type)) {\n var size = formatOutputUInt(dynamicPart.slice(0, padding));\n dynamicPart = dynamicPart.slice(padding);\n var array = [];\n for (var k = 0; k < size; k++) {\n array.push(formatter(output.slice(0, padding))); \n output = output.slice(padding);\n }\n result.push(array);\n }\n else if (prefixedType('string')(method.outputs[i].type)) {\n dynamicPart = dynamicPart.slice(padding); \n result.push(formatter(output.slice(0, padding)));\n output = output.slice(padding);\n } else {\n result.push(formatter(output.slice(0, padding)));\n output = output.slice(padding);\n }\n });\n\n return result;\n};\n\n/// @returns display name for method eg. multiply(uint256) -> multiply\nvar methodDisplayName = function (method) {\n var length = method.indexOf('('); \n return length !== -1 ? method.substr(0, length) : method;\n};\n\n/// @returns overloaded part of method's name\nvar methodTypeName = function (method) {\n /// TODO: make it not vulnerable\n var length = method.indexOf('(');\n return length !== -1 ? method.substr(length + 1, method.length - 1 - (length + 1)) : \"\";\n};\n\n/// @param json abi for contract\n/// @returns input parser object for given json abi\nvar inputParser = function (json) {\n var parser = {};\n json.forEach(function (method) {\n var displayName = methodDisplayName(method.name); \n var typeName = methodTypeName(method.name);\n\n var impl = function () {\n var params = Array.prototype.slice.call(arguments);\n return toAbiInput(json, method.name, params);\n };\n \n if (parser[displayName] === undefined) {\n parser[displayName] = impl;\n }\n\n parser[displayName][typeName] = impl;\n });\n\n return parser;\n};\n\n/// @param json abi for contract\n/// @returns output parser for given json abi\nvar outputParser = function (json) {\n var parser = {};\n json.forEach(function (method) {\n\n var displayName = methodDisplayName(method.name); \n var typeName = methodTypeName(method.name);\n\n var impl = function (output) {\n return fromAbiOutput(json, method.name, output);\n };\n\n if (parser[displayName] === undefined) {\n parser[displayName] = impl;\n }\n\n parser[displayName][typeName] = impl;\n });\n\n return parser;\n};\n\n/// @param method name for which we want to get method signature\n/// @returns (promise) contract method signature for method with given name\nvar methodSignature = function (name) {\n return web3.sha3(web3.fromAscii(name)).slice(0, 2 + ETH_METHOD_SIGNATURE_LENGTH * 2);\n};\n\nmodule.exports = {\n inputParser: inputParser,\n outputParser: outputParser,\n methodSignature: methodSignature,\n methodDisplayName: methodDisplayName,\n methodTypeName: methodTypeName,\n getMethodWithName: getMethodWithName\n};\n\n", - "/*\n This file is part of ethereum.js.\n\n ethereum.js is free software: you can redistribute it and/or modify\n it under the terms of the GNU Lesser General Public License as published by\n the Free Software Foundation, either version 3 of the License, or\n (at your option) any later version.\n\n ethereum.js is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n GNU Lesser General Public License for more details.\n\n You should have received a copy of the GNU Lesser General Public License\n along with ethereum.js. If not, see .\n*/\n/** @file contract.js\n * @authors:\n * Marek Kotewicz \n * @date 2014\n */\n\nvar web3 = require('./web3'); // jshint ignore:line\nvar abi = require('./abi');\n\n/**\n * This method should be called when we want to call / transact some solidity method from javascript\n * it returns an object which has same methods available as solidity contract description\n * usage example: \n *\n * var abi = [{\n * name: 'myMethod',\n * inputs: [{ name: 'a', type: 'string' }],\n * outputs: [{name: 'd', type: 'string' }]\n * }]; // contract abi\n *\n * var myContract = web3.eth.contract('0x0123123121', abi); // creation of contract object\n *\n * myContract.myMethod('this is test string param for call'); // myMethod call (implicit, default)\n * myContract.call().myMethod('this is test string param for call'); // myMethod call (explicit)\n * myContract.transact().myMethod('this is test string param for transact'); // myMethod transact\n *\n * @param address - address of the contract, which should be called\n * @param desc - abi json description of the contract, which is being created\n * @returns contract object\n */\n\nvar contract = function (address, desc) {\n\n desc.forEach(function (method) {\n // workaround for invalid assumption that method.name is the full anonymous prototype of the method.\n // it's not. it's just the name. the rest of the code assumes it's actually the anonymous\n // prototype, so we make it so as a workaround.\n if (method.name.indexOf('(') === -1) {\n var displayName = method.name;\n var typeName = method.inputs.map(function(i){return i.type; }).join();\n method.name = displayName + '(' + typeName + ')';\n }\n });\n\n var inputParser = abi.inputParser(desc);\n var outputParser = abi.outputParser(desc);\n\n var result = {};\n\n result.call = function (options) {\n result._isTransact = false;\n result._options = options;\n return result;\n };\n\n result.transact = function (options) {\n result._isTransact = true;\n result._options = options;\n return result;\n };\n\n result._options = {};\n ['gas', 'gasPrice', 'value', 'from'].forEach(function(p) {\n result[p] = function (v) {\n result._options[p] = v;\n return result;\n };\n });\n\n\n desc.forEach(function (method) {\n\n var displayName = abi.methodDisplayName(method.name);\n var typeName = abi.methodTypeName(method.name);\n\n var impl = function () {\n var params = Array.prototype.slice.call(arguments);\n var signature = abi.methodSignature(method.name);\n var parsed = inputParser[displayName][typeName].apply(null, params);\n\n var options = result._options || {};\n options.to = address;\n options.data = signature + parsed;\n \n var isTransact = result._isTransact === true || (result._isTransact !== false && !method.constant);\n var collapse = options.collapse !== false;\n \n // reset\n result._options = {};\n result._isTransact = null;\n\n if (isTransact) {\n // it's used byt natspec.js\n // TODO: figure out better way to solve this\n web3._currentContractAbi = desc;\n web3._currentContractAddress = address;\n\n // transactions do not have any output, cause we do not know, when they will be processed\n web3.eth.transact(options);\n return;\n }\n \n var output = web3.eth.call(options);\n var ret = outputParser[displayName][typeName](output);\n if (collapse)\n {\n if (ret.length === 1)\n ret = ret[0];\n else if (ret.length === 0)\n ret = null;\n }\n return ret;\n };\n\n if (result[displayName] === undefined) {\n result[displayName] = impl;\n }\n\n result[displayName][typeName] = impl;\n\n });\n\n return result;\n};\n\nmodule.exports = contract;\n\n", - "/*\n This file is part of ethereum.js.\n\n ethereum.js is free software: you can redistribute it and/or modify\n it under the terms of the GNU Lesser General Public License as published by\n the Free Software Foundation, either version 3 of the License, or\n (at your option) any later version.\n\n ethereum.js is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n GNU Lesser General Public License for more details.\n\n You should have received a copy of the GNU Lesser General Public License\n along with ethereum.js. If not, see .\n*/\n/** @file filter.js\n * @authors:\n * Jeffrey Wilcke \n * Marek Kotewicz \n * Marian Oancea \n * Gav Wood \n * @date 2014\n */\n\nvar web3 = require('./web3'); // jshint ignore:line\n\n/// should be used when we want to watch something\n/// it's using inner polling mechanism and is notified about changes\nvar Filter = function(options, impl) {\n this.impl = impl;\n this.callbacks = [];\n\n this.id = impl.newFilter(options);\n web3.provider.startPolling({call: impl.changed, args: [this.id]}, this.id, this.trigger.bind(this));\n};\n\n/// alias for changed*\nFilter.prototype.arrived = function(callback) {\n this.changed(callback);\n};\n\n/// gets called when there is new eth/shh message\nFilter.prototype.changed = function(callback) {\n this.callbacks.push(callback);\n};\n\n/// trigger calling new message from people\nFilter.prototype.trigger = function(messages) {\n for (var i = 0; i < this.callbacks.length; i++) {\n for (var j = 0; j < messages; j++) {\n this.callbacks[i].call(this, messages[j]);\n }\n }\n};\n\n/// should be called to uninstall current filter\nFilter.prototype.uninstall = function() {\n this.impl.uninstallFilter(this.id);\n web3.provider.stopPolling(this.id);\n};\n\n/// should be called to manually trigger getting latest messages from the client\nFilter.prototype.messages = function() {\n return this.impl.getMessages(this.id);\n};\n\n/// alias for messages\nFilter.prototype.logs = function () {\n return this.messages();\n};\n\nmodule.exports = Filter;\n", + "/*\n This file is part of ethereum.js.\n\n ethereum.js is free software: you can redistribute it and/or modify\n it under the terms of the GNU Lesser General Public License as published by\n the Free Software Foundation, either version 3 of the License, or\n (at your option) any later version.\n\n ethereum.js is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n GNU Lesser General Public License for more details.\n\n You should have received a copy of the GNU Lesser General Public License\n along with ethereum.js. If not, see .\n*/\n/** @file abi.js\n * @authors:\n * Marek Kotewicz \n * Gav Wood \n * @date 2014\n */\n\n// TODO: is these line is supposed to be here? \nif (\"build\" !== 'build') {/*\n var BigNumber = require('bignumber.js'); // jshint ignore:line\n*/}\n\nvar web3 = require('./web3'); // jshint ignore:line\n\nBigNumber.config({ ROUNDING_MODE: BigNumber.ROUND_DOWN });\n\nvar ETH_PADDING = 32;\n\n/// method signature length in bytes\nvar ETH_METHOD_SIGNATURE_LENGTH = 4;\n\n/// Finds first index of array element matching pattern\n/// @param array\n/// @param callback pattern\n/// @returns index of element\nvar findIndex = function (array, callback) {\n var end = false;\n var i = 0;\n for (; i < array.length && !end; i++) {\n end = callback(array[i]);\n }\n return end ? i - 1 : -1;\n};\n\n/// @returns a function that is used as a pattern for 'findIndex'\nvar findMethodIndex = function (json, methodName) {\n return findIndex(json, function (method) {\n return method.name === methodName;\n });\n};\n\n/// @returns method with given method name\nvar getMethodWithName = function (json, methodName) {\n var index = findMethodIndex(json, methodName);\n if (index === -1) {\n console.error('method ' + methodName + ' not found in the abi');\n return undefined;\n }\n return json[index];\n};\n\n/// Filters all function from input abi\n/// @returns abi array with filtered objects of type 'function'\nvar filterFunctions = function (json) {\n return json.filter(function (current) {\n return current.type === 'function'; \n }); \n};\n\n/// Filters all events form input abi\n/// @returns abi array with filtered objects of type 'event'\nvar filterEvents = function (json) {\n return json.filter(function (current) {\n return current.type === 'event';\n });\n};\n\n/// @param string string to be padded\n/// @param number of characters that result string should have\n/// @param sign, by default 0\n/// @returns right aligned string\nvar padLeft = function (string, chars, sign) {\n return new Array(chars - string.length + 1).join(sign ? sign : \"0\") + string;\n};\n\n/// @param expected type prefix (string)\n/// @returns function which checks if type has matching prefix. if yes, returns true, otherwise false\nvar prefixedType = function (prefix) {\n return function (type) {\n return type.indexOf(prefix) === 0;\n };\n};\n\n/// @param expected type name (string)\n/// @returns function which checks if type is matching expected one. if yes, returns true, otherwise false\nvar namedType = function (name) {\n return function (type) {\n return name === type;\n };\n};\n\nvar arrayType = function (type) {\n return type.slice(-2) === '[]';\n};\n\n/// Formats input value to byte representation of int\n/// If value is negative, return it's two's complement\n/// If the value is floating point, round it down\n/// @returns right-aligned byte representation of int\nvar formatInputInt = function (value) {\n var padding = ETH_PADDING * 2;\n if (value instanceof BigNumber || typeof value === 'number') {\n if (typeof value === 'number')\n value = new BigNumber(value);\n value = value.round();\n\n if (value.lessThan(0)) \n value = new BigNumber(\"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff\", 16).plus(value).plus(1);\n value = value.toString(16);\n }\n else if (value.indexOf('0x') === 0)\n value = value.substr(2);\n else if (typeof value === 'string')\n value = formatInputInt(new BigNumber(value));\n else\n value = (+value).toString(16);\n return padLeft(value, padding);\n};\n\n/// Formats input value to byte representation of string\n/// @returns left-algined byte representation of string\nvar formatInputString = function (value) {\n return web3.fromAscii(value, ETH_PADDING).substr(2);\n};\n\n/// Formats input value to byte representation of bool\n/// @returns right-aligned byte representation bool\nvar formatInputBool = function (value) {\n return '000000000000000000000000000000000000000000000000000000000000000' + (value ? '1' : '0');\n};\n\n/// Formats input value to byte representation of real\n/// Values are multiplied by 2^m and encoded as integers\n/// @returns byte representation of real\nvar formatInputReal = function (value) {\n return formatInputInt(new BigNumber(value).times(new BigNumber(2).pow(128))); \n};\n\nvar dynamicTypeBytes = function (type, value) {\n // TODO: decide what to do with array of strings\n if (arrayType(type) || type === 'string') // only string itself that is dynamic; stringX is static length.\n return formatInputInt(value.length); \n return \"\";\n};\n\n/// Setups input formatters for solidity types\n/// @returns an array of input formatters \nvar setupInputTypes = function () {\n \n return [\n { type: prefixedType('uint'), format: formatInputInt },\n { type: prefixedType('int'), format: formatInputInt },\n { type: prefixedType('hash'), format: formatInputInt },\n { type: prefixedType('string'), format: formatInputString }, \n { type: prefixedType('real'), format: formatInputReal },\n { type: prefixedType('ureal'), format: formatInputReal },\n { type: namedType('address'), format: formatInputInt },\n { type: namedType('bool'), format: formatInputBool }\n ];\n};\n\nvar inputTypes = setupInputTypes();\n\n/// Formats input params to bytes\n/// @param contract json abi\n/// @param name of the method that we want to use\n/// @param array of params that will be formatted to bytes\n/// @returns bytes representation of input params\nvar toAbiInput = function (json, methodName, params) {\n var bytes = \"\";\n\n var method = getMethodWithName(json, methodName);\n var padding = ETH_PADDING * 2;\n\n /// first we iterate in search for dynamic \n method.inputs.forEach(function (input, index) {\n bytes += dynamicTypeBytes(input.type, params[index]);\n });\n\n method.inputs.forEach(function (input, i) {\n var typeMatch = false;\n for (var j = 0; j < inputTypes.length && !typeMatch; j++) {\n typeMatch = inputTypes[j].type(method.inputs[i].type, params[i]);\n }\n if (!typeMatch) {\n console.error('input parser does not support type: ' + method.inputs[i].type);\n }\n\n var formatter = inputTypes[j - 1].format;\n var toAppend = \"\";\n\n if (arrayType(method.inputs[i].type))\n toAppend = params[i].reduce(function (acc, curr) {\n return acc + formatter(curr);\n }, \"\");\n else\n toAppend = formatter(params[i]);\n\n bytes += toAppend; \n });\n return bytes;\n};\n\n/// Check if input value is negative\n/// @param value is hex format\n/// @returns true if it is negative, otherwise false\nvar signedIsNegative = function (value) {\n return (new BigNumber(value.substr(0, 1), 16).toString(2).substr(0, 1)) === '1';\n};\n\n/// Formats input right-aligned input bytes to int\n/// @returns right-aligned input bytes formatted to int\nvar formatOutputInt = function (value) {\n value = value || \"0\";\n // check if it's negative number\n // it it is, return two's complement\n if (signedIsNegative(value)) {\n return new BigNumber(value, 16).minus(new BigNumber('ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', 16)).minus(1);\n }\n return new BigNumber(value, 16);\n};\n\n/// Formats big right-aligned input bytes to uint\n/// @returns right-aligned input bytes formatted to uint\nvar formatOutputUInt = function (value) {\n value = value || \"0\";\n return new BigNumber(value, 16);\n};\n\n/// @returns input bytes formatted to real\nvar formatOutputReal = function (value) {\n return formatOutputInt(value).dividedBy(new BigNumber(2).pow(128)); \n};\n\n/// @returns input bytes formatted to ureal\nvar formatOutputUReal = function (value) {\n return formatOutputUInt(value).dividedBy(new BigNumber(2).pow(128)); \n};\n\n/// @returns right-aligned input bytes formatted to hex\nvar formatOutputHash = function (value) {\n return \"0x\" + value;\n};\n\n/// @returns right-aligned input bytes formatted to bool\nvar formatOutputBool = function (value) {\n return value === '0000000000000000000000000000000000000000000000000000000000000001' ? true : false;\n};\n\n/// @returns left-aligned input bytes formatted to ascii string\nvar formatOutputString = function (value) {\n return web3.toAscii(value);\n};\n\n/// @returns right-aligned input bytes formatted to address\nvar formatOutputAddress = function (value) {\n return \"0x\" + value.slice(value.length - 40, value.length);\n};\n\nvar dynamicBytesLength = function (type) {\n if (arrayType(type) || type === 'string') // only string itself that is dynamic; stringX is static length.\n return ETH_PADDING * 2;\n return 0;\n};\n\n/// Setups output formaters for solidity types\n/// @returns an array of output formatters\nvar setupOutputTypes = function () {\n\n return [\n { type: prefixedType('uint'), format: formatOutputUInt },\n { type: prefixedType('int'), format: formatOutputInt },\n { type: prefixedType('hash'), format: formatOutputHash },\n { type: prefixedType('string'), format: formatOutputString },\n { type: prefixedType('real'), format: formatOutputReal },\n { type: prefixedType('ureal'), format: formatOutputUReal },\n { type: namedType('address'), format: formatOutputAddress },\n { type: namedType('bool'), format: formatOutputBool }\n ];\n};\n\nvar outputTypes = setupOutputTypes();\n\n/// Formats output bytes back to param list\n/// @param contract json abi\n/// @param name of the method that we want to use\n/// @param bytes representtion of output \n/// @returns array of output params \nvar fromAbiOutput = function (json, methodName, output) {\n \n output = output.slice(2);\n var result = [];\n var method = getMethodWithName(json, methodName);\n var padding = ETH_PADDING * 2;\n\n var dynamicPartLength = method.outputs.reduce(function (acc, curr) {\n return acc + dynamicBytesLength(curr.type);\n }, 0);\n \n var dynamicPart = output.slice(0, dynamicPartLength);\n output = output.slice(dynamicPartLength);\n\n method.outputs.forEach(function (out, i) {\n var typeMatch = false;\n for (var j = 0; j < outputTypes.length && !typeMatch; j++) {\n typeMatch = outputTypes[j].type(method.outputs[i].type);\n }\n\n if (!typeMatch) {\n console.error('output parser does not support type: ' + method.outputs[i].type);\n }\n\n var formatter = outputTypes[j - 1].format;\n if (arrayType(method.outputs[i].type)) {\n var size = formatOutputUInt(dynamicPart.slice(0, padding));\n dynamicPart = dynamicPart.slice(padding);\n var array = [];\n for (var k = 0; k < size; k++) {\n array.push(formatter(output.slice(0, padding))); \n output = output.slice(padding);\n }\n result.push(array);\n }\n else if (prefixedType('string')(method.outputs[i].type)) {\n dynamicPart = dynamicPart.slice(padding); \n result.push(formatter(output.slice(0, padding)));\n output = output.slice(padding);\n } else {\n result.push(formatter(output.slice(0, padding)));\n output = output.slice(padding);\n }\n });\n\n return result;\n};\n\n/// @returns display name for method eg. multiply(uint256) -> multiply\nvar methodDisplayName = function (method) {\n var length = method.indexOf('('); \n return length !== -1 ? method.substr(0, length) : method;\n};\n\n/// @returns overloaded part of method's name\nvar methodTypeName = function (method) {\n /// TODO: make it not vulnerable\n var length = method.indexOf('(');\n return length !== -1 ? method.substr(length + 1, method.length - 1 - (length + 1)) : \"\";\n};\n\n/// @param json abi for contract\n/// @returns input parser object for given json abi\nvar inputParser = function (json) {\n var parser = {};\n filterFunctions(json).forEach(function (method) {\n var displayName = methodDisplayName(method.name); \n var typeName = methodTypeName(method.name);\n\n var impl = function () {\n var params = Array.prototype.slice.call(arguments);\n return toAbiInput(json, method.name, params);\n };\n \n if (parser[displayName] === undefined) {\n parser[displayName] = impl;\n }\n\n parser[displayName][typeName] = impl;\n });\n\n return parser;\n};\n\n/// @param json abi for contract\n/// @returns output parser for given json abi\nvar outputParser = function (json) {\n var parser = {};\n filterFunctions(json).forEach(function (method) {\n\n var displayName = methodDisplayName(method.name); \n var typeName = methodTypeName(method.name);\n\n var impl = function (output) {\n return fromAbiOutput(json, method.name, output);\n };\n\n if (parser[displayName] === undefined) {\n parser[displayName] = impl;\n }\n\n parser[displayName][typeName] = impl;\n });\n\n return parser;\n};\n\n/// @param method name for which we want to get method signature\n/// @returns (promise) contract method signature for method with given name\nvar methodSignature = function (name) {\n return web3.sha3(web3.fromAscii(name)).slice(0, 2 + ETH_METHOD_SIGNATURE_LENGTH * 2);\n};\n\nmodule.exports = {\n inputParser: inputParser,\n outputParser: outputParser,\n methodSignature: methodSignature,\n methodDisplayName: methodDisplayName,\n methodTypeName: methodTypeName,\n getMethodWithName: getMethodWithName,\n filterFunctions: filterFunctions,\n filterEvents: filterEvents\n};\n\n", + "/*\n This file is part of ethereum.js.\n\n ethereum.js is free software: you can redistribute it and/or modify\n it under the terms of the GNU Lesser General Public License as published by\n the Free Software Foundation, either version 3 of the License, or\n (at your option) any later version.\n\n ethereum.js is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n GNU Lesser General Public License for more details.\n\n You should have received a copy of the GNU Lesser General Public License\n along with ethereum.js. If not, see .\n*/\n/** @file contract.js\n * @authors:\n * Marek Kotewicz \n * @date 2014\n */\n\nvar web3 = require('./web3'); \nvar abi = require('./abi');\nvar eventImpl = require('./event');\n\nvar addFunctionRelatedPropertiesToContract = function (contract) {\n \n contract.call = function (options) {\n contract._isTransact = false;\n contract._options = options;\n return contract;\n };\n\n contract.transact = function (options) {\n contract._isTransact = true;\n contract._options = options;\n return contract;\n };\n\n contract._options = {};\n ['gas', 'gasPrice', 'value', 'from'].forEach(function(p) {\n contract[p] = function (v) {\n contract._options[p] = v;\n return contract;\n };\n });\n\n};\n\nvar addFunctionsToContract = function (contract, desc, address) {\n var inputParser = abi.inputParser(desc);\n var outputParser = abi.outputParser(desc);\n\n // create contract functions\n abi.filterFunctions(desc).forEach(function (method) {\n\n var displayName = abi.methodDisplayName(method.name);\n var typeName = abi.methodTypeName(method.name);\n\n var impl = function () {\n var params = Array.prototype.slice.call(arguments);\n var signature = abi.methodSignature(method.name);\n var parsed = inputParser[displayName][typeName].apply(null, params);\n\n var options = contract._options || {};\n options.to = address;\n options.data = signature + parsed;\n \n var isTransact = contract._isTransact === true || (contract._isTransact !== false && !method.constant);\n var collapse = options.collapse !== false;\n \n // reset\n contract._options = {};\n contract._isTransact = null;\n\n if (isTransact) {\n // it's used byt natspec.js\n // TODO: figure out better way to solve this\n web3._currentContractAbi = desc;\n web3._currentContractAddress = address;\n web3._currentContractMethodName = method.name;\n web3._currentContractMethodParams = params;\n\n // transactions do not have any output, cause we do not know, when they will be processed\n web3.eth.transact(options);\n return;\n }\n \n var output = web3.eth.call(options);\n var ret = outputParser[displayName][typeName](output);\n if (collapse)\n {\n if (ret.length === 1)\n ret = ret[0];\n else if (ret.length === 0)\n ret = null;\n }\n return ret;\n };\n\n if (contract[displayName] === undefined) {\n contract[displayName] = impl;\n }\n\n contract[displayName][typeName] = impl;\n });\n};\n\nvar addEventRelatedPropertiesToContract = function (contract, desc, address) {\n contract.address = address;\n \n Object.defineProperty(contract, 'topics', {\n get: function() {\n return abi.filterEvents(desc).map(function (e) {\n return abi.methodSignature(e.name);\n });\n }\n });\n\n};\n\nvar addEventsToContract = function (contract, desc, address) {\n // create contract events\n abi.filterEvents(desc).forEach(function (e) {\n\n var impl = function () {\n var params = Array.prototype.slice.call(arguments);\n var signature = abi.methodSignature(e.name);\n var event = eventImpl(address, signature);\n var o = event.apply(null, params);\n return web3.eth.watch(o); \n };\n\n impl.address = address;\n\n Object.defineProperty(impl, 'topics', {\n get: function() {\n return [abi.methodSignature(e.name)];\n }\n });\n \n // TODO: rename these methods, cause they are used not only for methods\n var displayName = abi.methodDisplayName(e.name);\n var typeName = abi.methodTypeName(e.name);\n\n if (contract[displayName] === undefined) {\n contract[displayName] = impl;\n }\n\n contract[displayName][typeName] = impl;\n\n });\n};\n\n\n/**\n * This method should be called when we want to call / transact some solidity method from javascript\n * it returns an object which has same methods available as solidity contract description\n * usage example: \n *\n * var abi = [{\n * name: 'myMethod',\n * inputs: [{ name: 'a', type: 'string' }],\n * outputs: [{name: 'd', type: 'string' }]\n * }]; // contract abi\n *\n * var myContract = web3.eth.contract('0x0123123121', abi); // creation of contract object\n *\n * myContract.myMethod('this is test string param for call'); // myMethod call (implicit, default)\n * myContract.call().myMethod('this is test string param for call'); // myMethod call (explicit)\n * myContract.transact().myMethod('this is test string param for transact'); // myMethod transact\n *\n * @param address - address of the contract, which should be called\n * @param desc - abi json description of the contract, which is being created\n * @returns contract object\n */\n\nvar contract = function (address, desc) {\n\n // workaround for invalid assumption that method.name is the full anonymous prototype of the method.\n // it's not. it's just the name. the rest of the code assumes it's actually the anonymous\n // prototype, so we make it so as a workaround.\n // TODO: we may not want to modify input params, maybe use copy instead?\n desc.forEach(function (method) {\n if (method.name.indexOf('(') === -1) {\n var displayName = method.name;\n var typeName = method.inputs.map(function(i){return i.type; }).join();\n method.name = displayName + '(' + typeName + ')';\n }\n });\n\n var result = {};\n addFunctionRelatedPropertiesToContract(result);\n addFunctionsToContract(result, desc, address);\n addEventRelatedPropertiesToContract(result, desc, address);\n addEventsToContract(result, desc, address);\n\n return result;\n};\n\nmodule.exports = contract;\n\n", + "/*\n This file is part of ethereum.js.\n\n ethereum.js is free software: you can redistribute it and/or modify\n it under the terms of the GNU Lesser General Public License as published by\n the Free Software Foundation, either version 3 of the License, or\n (at your option) any later version.\n\n ethereum.js is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n GNU Lesser General Public License for more details.\n\n You should have received a copy of the GNU Lesser General Public License\n along with ethereum.js. If not, see .\n*/\n/** @file event.js\n * @authors:\n * Marek Kotewicz \n * @date 2014\n */\n\nvar implementationOfEvent = function (address, signature) {\n \n return function (options) {\n var o = options || {};\n o.address = o.address || address;\n o.topics = o.topics || [];\n o.topics.push(signature);\n return o;\n };\n};\n\nmodule.exports = implementationOfEvent;\n\n", + "/*\n This file is part of ethereum.js.\n\n ethereum.js is free software: you can redistribute it and/or modify\n it under the terms of the GNU Lesser General Public License as published by\n the Free Software Foundation, either version 3 of the License, or\n (at your option) any later version.\n\n ethereum.js is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n GNU Lesser General Public License for more details.\n\n You should have received a copy of the GNU Lesser General Public License\n along with ethereum.js. If not, see .\n*/\n/** @file filter.js\n * @authors:\n * Jeffrey Wilcke \n * Marek Kotewicz \n * Marian Oancea \n * Gav Wood \n * @date 2014\n */\n\nvar web3 = require('./web3'); // jshint ignore:line\n\n/// should be used when we want to watch something\n/// it's using inner polling mechanism and is notified about changes\nvar Filter = function(options, impl) {\n this.impl = impl;\n this.callbacks = [];\n\n if (typeof options !== \"string\") {\n // evaluate lazy properties\n options = {\n to: options.to,\n topics: options.topics,\n earliest: options.earliest,\n latest: options.latest,\n max: options.max,\n skip: options.skip,\n address: options.address\n };\n }\n\n this.id = impl.newFilter(options);\n web3.provider.startPolling({call: impl.changed, args: [this.id]}, this.id, this.trigger.bind(this));\n};\n\n/// alias for changed*\nFilter.prototype.arrived = function(callback) {\n this.changed(callback);\n};\n\n/// gets called when there is new eth/shh message\nFilter.prototype.changed = function(callback) {\n this.callbacks.push(callback);\n};\n\n/// trigger calling new message from people\nFilter.prototype.trigger = function(messages) {\n for (var i = 0; i < this.callbacks.length; i++) {\n for (var j = 0; j < messages.length; j++) {\n this.callbacks[i].call(this, messages[j]);\n }\n }\n};\n\n/// should be called to uninstall current filter\nFilter.prototype.uninstall = function() {\n this.impl.uninstallFilter(this.id);\n web3.provider.stopPolling(this.id);\n};\n\n/// should be called to manually trigger getting latest messages from the client\nFilter.prototype.messages = function() {\n return this.impl.getMessages(this.id);\n};\n\n/// alias for messages\nFilter.prototype.logs = function () {\n return this.messages();\n};\n\nmodule.exports = Filter;\n", "/*\n This file is part of ethereum.js.\n\n ethereum.js is free software: you can redistribute it and/or modify\n it under the terms of the GNU Lesser General Public License as published by\n the Free Software Foundation, either version 3 of the License, or\n (at your option) any later version.\n\n ethereum.js is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n GNU Lesser General Public License for more details.\n\n You should have received a copy of the GNU Lesser General Public License\n along with ethereum.js. If not, see .\n*/\n/** @file httpsync.js\n * @authors:\n * Marek Kotewicz \n * Marian Oancea \n * @date 2014\n */\n\nif (\"build\" !== 'build') {/*\n var XMLHttpRequest = require('xmlhttprequest').XMLHttpRequest; // jshint ignore:line\n*/}\n\nvar HttpSyncProvider = function (host) {\n this.handlers = [];\n this.host = host || 'http://localhost:8080';\n};\n\n/// Transforms inner message to proper jsonrpc object\n/// @param inner message object\n/// @returns jsonrpc object\nfunction formatJsonRpcObject(object) {\n return {\n jsonrpc: '2.0',\n method: object.call,\n params: object.args,\n id: object._id\n };\n}\n\n/// Transforms jsonrpc object to inner message\n/// @param incoming jsonrpc message \n/// @returns inner message object\nfunction formatJsonRpcMessage(message) {\n var object = JSON.parse(message);\n\n return {\n _id: object.id,\n data: object.result,\n error: object.error\n };\n}\n\nHttpSyncProvider.prototype.send = function (payload) {\n var data = formatJsonRpcObject(payload);\n \n var request = new XMLHttpRequest();\n request.open('POST', this.host, false);\n request.send(JSON.stringify(data));\n \n // check request.status\n return request.responseText;\n};\n\nmodule.exports = HttpSyncProvider;\n\n", - "/*\n This file is part of ethereum.js.\n\n ethereum.js is free software: you can redistribute it and/or modify\n it under the terms of the GNU Lesser General Public License as published by\n the Free Software Foundation, either version 3 of the License, or\n (at your option) any later version.\n\n ethereum.js is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n GNU Lesser General Public License for more details.\n\n You should have received a copy of the GNU Lesser General Public License\n along with ethereum.js. If not, see .\n*/\n/** @file providermanager.js\n * @authors:\n * Jeffrey Wilcke \n * Marek Kotewicz \n * Marian Oancea \n * Gav Wood \n * @date 2014\n */\n\nvar web3 = require('./web3'); // jshint ignore:line\n\n/**\n * Provider manager object prototype\n * It's responsible for passing messages to providers\n * If no provider is set it's responsible for queuing requests\n * It's also responsible for polling the ethereum node for incoming messages\n * Default poll timeout is 12 seconds\n * If we are running ethereum.js inside ethereum browser, there are backend based tools responsible for polling,\n * and provider manager polling mechanism is not used\n */\nvar ProviderManager = function() {\n this.polls = [];\n this.provider = undefined;\n this.id = 1;\n\n var self = this;\n var poll = function () {\n if (self.provider) {\n self.polls.forEach(function (data) {\n data.data._id = self.id;\n self.id++;\n var result = self.provider.send(data.data);\n \n result = JSON.parse(result);\n \n // dont call the callback if result is not an array, or empty one\n if (result.error || !(result.result instanceof Array) || result.result.length === 0) {\n return;\n }\n\n data.callback(result.result);\n });\n }\n setTimeout(poll, 1000);\n };\n poll();\n};\n\n/// sends outgoing requests\nProviderManager.prototype.send = function(data) {\n\n data.args = data.args || [];\n data._id = this.id++;\n\n if (this.provider === undefined) {\n console.error('provider is not set');\n return null; \n }\n\n //TODO: handle error here? \n var result = this.provider.send(data);\n result = JSON.parse(result);\n return result.result;\n};\n\n/// setups provider, which will be used for sending messages\nProviderManager.prototype.set = function(provider) {\n this.provider = provider;\n};\n\n/// this method is only used, when we do not have native qt bindings and have to do polling on our own\n/// should be callled, on start watching for eth/shh changes\nProviderManager.prototype.startPolling = function (data, pollId, callback) {\n this.polls.push({data: data, id: pollId, callback: callback});\n};\n\n/// should be called to stop polling for certain watch changes\nProviderManager.prototype.stopPolling = function (pollId) {\n for (var i = this.polls.length; i--;) {\n var poll = this.polls[i];\n if (poll.id === pollId) {\n this.polls.splice(i, 1);\n }\n }\n};\n\nmodule.exports = ProviderManager;\n\n", + "/*\n This file is part of ethereum.js.\n\n ethereum.js is free software: you can redistribute it and/or modify\n it under the terms of the GNU Lesser General Public License as published by\n the Free Software Foundation, either version 3 of the License, or\n (at your option) any later version.\n\n ethereum.js is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n GNU Lesser General Public License for more details.\n\n You should have received a copy of the GNU Lesser General Public License\n along with ethereum.js. If not, see .\n*/\n/** @file providermanager.js\n * @authors:\n * Jeffrey Wilcke \n * Marek Kotewicz \n * Marian Oancea \n * Gav Wood \n * @date 2014\n */\n\nvar web3 = require('./web3'); // jshint ignore:line\n\n/**\n * Provider manager object prototype\n * It's responsible for passing messages to providers\n * If no provider is set it's responsible for queuing requests\n * It's also responsible for polling the ethereum node for incoming messages\n * Default poll timeout is 12 seconds\n * If we are running ethereum.js inside ethereum browser, there are backend based tools responsible for polling,\n * and provider manager polling mechanism is not used\n */\nvar ProviderManager = function() {\n this.polls = [];\n this.provider = undefined;\n this.id = 1;\n\n var self = this;\n var poll = function () {\n if (self.provider) {\n self.polls.forEach(function (data) {\n data.data._id = self.id;\n self.id++;\n var result = self.provider.send(data.data);\n \n result = JSON.parse(result);\n \n // dont call the callback if result is not an array, or empty one\n if (result.error || !(result.result instanceof Array) || result.result.length === 0) {\n return;\n }\n\n data.callback(result.result);\n });\n }\n setTimeout(poll, 1000);\n };\n poll();\n};\n\n/// sends outgoing requests\nProviderManager.prototype.send = function(data) {\n\n data.args = data.args || [];\n data._id = this.id++;\n\n if (this.provider === undefined) {\n console.error('provider is not set');\n return null; \n }\n\n //TODO: handle error here? \n var result = this.provider.send(data);\n result = JSON.parse(result);\n\n if (result.error) {\n console.log(result.error);\n return null;\n }\n\n return result.result;\n};\n\n/// setups provider, which will be used for sending messages\nProviderManager.prototype.set = function(provider) {\n this.provider = provider;\n};\n\n/// this method is only used, when we do not have native qt bindings and have to do polling on our own\n/// should be callled, on start watching for eth/shh changes\nProviderManager.prototype.startPolling = function (data, pollId, callback) {\n this.polls.push({data: data, id: pollId, callback: callback});\n};\n\n/// should be called to stop polling for certain watch changes\nProviderManager.prototype.stopPolling = function (pollId) {\n for (var i = this.polls.length; i--;) {\n var poll = this.polls[i];\n if (poll.id === pollId) {\n this.polls.splice(i, 1);\n }\n }\n};\n\nmodule.exports = ProviderManager;\n\n", "/*\n This file is part of ethereum.js.\n\n ethereum.js is free software: you can redistribute it and/or modify\n it under the terms of the GNU Lesser General Public License as published by\n the Free Software Foundation, either version 3 of the License, or\n (at your option) any later version.\n\n ethereum.js is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n GNU Lesser General Public License for more details.\n\n You should have received a copy of the GNU Lesser General Public License\n along with ethereum.js. If not, see .\n*/\n/** @file qtsync.js\n * @authors:\n * Marek Kotewicz \n * Marian Oancea \n * @date 2014\n */\n\nvar QtSyncProvider = function () {\n};\n\nQtSyncProvider.prototype.send = function (payload) {\n return navigator.qt.callMethod(JSON.stringify(payload));\n};\n\nmodule.exports = QtSyncProvider;\n\n", - "/*\n This file is part of ethereum.js.\n\n ethereum.js is free software: you can redistribute it and/or modify\n it under the terms of the GNU Lesser General Public License as published by\n the Free Software Foundation, either version 3 of the License, or\n (at your option) any later version.\n\n ethereum.js is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n GNU Lesser General Public License for more details.\n\n You should have received a copy of the GNU Lesser General Public License\n along with ethereum.js. If not, see .\n*/\n/** @file web3.js\n * @authors:\n * Jeffrey Wilcke \n * Marek Kotewicz \n * Marian Oancea \n * Gav Wood \n * @date 2014\n */\n\nif (\"build\" !== 'build') {/*\n var BigNumber = require('bignumber.js');\n*/}\n\nvar ETH_UNITS = [ \n 'wei', \n 'Kwei', \n 'Mwei', \n 'Gwei', \n 'szabo', \n 'finney', \n 'ether', \n 'grand', \n 'Mether', \n 'Gether', \n 'Tether', \n 'Pether', \n 'Eether', \n 'Zether', \n 'Yether', \n 'Nether', \n 'Dether', \n 'Vether', \n 'Uether' \n];\n\n/// @returns an array of objects describing web3 api methods\nvar web3Methods = function () {\n return [\n { name: 'sha3', call: 'web3_sha3' }\n ];\n};\n\n/// @returns an array of objects describing web3.eth api methods\nvar ethMethods = function () {\n var blockCall = function (args) {\n return typeof args[0] === \"string\" ? \"eth_blockByHash\" : \"eth_blockByNumber\";\n };\n\n var transactionCall = function (args) {\n return typeof args[0] === \"string\" ? 'eth_transactionByHash' : 'eth_transactionByNumber';\n };\n\n var uncleCall = function (args) {\n return typeof args[0] === \"string\" ? 'eth_uncleByHash' : 'eth_uncleByNumber';\n };\n\n var methods = [\n { name: 'balanceAt', call: 'eth_balanceAt' },\n { name: 'stateAt', call: 'eth_stateAt' },\n { name: 'storageAt', call: 'eth_storageAt' },\n { name: 'countAt', call: 'eth_countAt'},\n { name: 'codeAt', call: 'eth_codeAt' },\n { name: 'transact', call: 'eth_transact' },\n { name: 'call', call: 'eth_call' },\n { name: 'block', call: blockCall },\n { name: 'transaction', call: transactionCall },\n { name: 'uncle', call: uncleCall },\n { name: 'compilers', call: 'eth_compilers' },\n { name: 'flush', call: 'eth_flush' },\n { name: 'lll', call: 'eth_lll' },\n { name: 'solidity', call: 'eth_solidity' },\n { name: 'serpent', call: 'eth_serpent' },\n { name: 'logs', call: 'eth_logs' }\n ];\n return methods;\n};\n\n/// @returns an array of objects describing web3.eth api properties\nvar ethProperties = function () {\n return [\n { name: 'coinbase', getter: 'eth_coinbase', setter: 'eth_setCoinbase' },\n { name: 'listening', getter: 'eth_listening', setter: 'eth_setListening' },\n { name: 'mining', getter: 'eth_mining', setter: 'eth_setMining' },\n { name: 'gasPrice', getter: 'eth_gasPrice' },\n { name: 'account', getter: 'eth_account' },\n { name: 'accounts', getter: 'eth_accounts' },\n { name: 'peerCount', getter: 'eth_peerCount' },\n { name: 'defaultBlock', getter: 'eth_defaultBlock', setter: 'eth_setDefaultBlock' },\n { name: 'number', getter: 'eth_number'}\n ];\n};\n\n/// @returns an array of objects describing web3.db api methods\nvar dbMethods = function () {\n return [\n { name: 'put', call: 'db_put' },\n { name: 'get', call: 'db_get' },\n { name: 'putString', call: 'db_putString' },\n { name: 'getString', call: 'db_getString' }\n ];\n};\n\n/// @returns an array of objects describing web3.shh api methods\nvar shhMethods = function () {\n return [\n { name: 'post', call: 'shh_post' },\n { name: 'newIdentity', call: 'shh_newIdentity' },\n { name: 'haveIdentity', call: 'shh_haveIdentity' },\n { name: 'newGroup', call: 'shh_newGroup' },\n { name: 'addToGroup', call: 'shh_addToGroup' }\n ];\n};\n\n/// @returns an array of objects describing web3.eth.watch api methods\nvar ethWatchMethods = function () {\n var newFilter = function (args) {\n return typeof args[0] === 'string' ? 'eth_newFilterString' : 'eth_newFilter';\n };\n\n return [\n { name: 'newFilter', call: newFilter },\n { name: 'uninstallFilter', call: 'eth_uninstallFilter' },\n { name: 'getMessages', call: 'eth_filterLogs' }\n ];\n};\n\n/// @returns an array of objects describing web3.shh.watch api methods\nvar shhWatchMethods = function () {\n return [\n { name: 'newFilter', call: 'shh_newFilter' },\n { name: 'uninstallFilter', call: 'shh_uninstallFilter' },\n { name: 'getMessages', call: 'shh_getMessages' }\n ];\n};\n\n/// creates methods in a given object based on method description on input\n/// setups api calls for these methods\nvar setupMethods = function (obj, methods) {\n methods.forEach(function (method) {\n obj[method.name] = function () {\n var args = Array.prototype.slice.call(arguments);\n var call = typeof method.call === 'function' ? method.call(args) : method.call;\n return web3.provider.send({\n call: call,\n args: args\n });\n };\n });\n};\n\n/// creates properties in a given object based on properties description on input\n/// setups api calls for these properties\nvar setupProperties = function (obj, properties) {\n properties.forEach(function (property) {\n var proto = {};\n proto.get = function () {\n return web3.provider.send({\n call: property.getter\n });\n };\n\n if (property.setter) {\n proto.set = function (val) {\n return web3.provider.send({\n call: property.setter,\n args: [val]\n });\n };\n }\n Object.defineProperty(obj, property.name, proto);\n });\n};\n\n/// setups web3 object, and it's in-browser executed methods\nvar web3 = {\n _callbacks: {},\n _events: {},\n providers: {},\n\n toHex: function(str) {\n var hex = \"\";\n for(var i = 0; i < str.length; i++) {\n var n = str.charCodeAt(i).toString(16);\n hex += n.length < 2 ? '0' + n : n;\n }\n\n return hex;\n },\n\n /// @returns ascii string representation of hex value prefixed with 0x\n toAscii: function(hex) {\n // Find termination\n var str = \"\";\n var i = 0, l = hex.length;\n if (hex.substring(0, 2) === '0x')\n i = 2;\n for(; i < l; i+=2) {\n var code = parseInt(hex.substr(i, 2), 16);\n if(code === 0) {\n break;\n }\n\n str += String.fromCharCode(code);\n }\n\n return str;\n },\n\n /// @returns hex representation (prefixed by 0x) of ascii string\n fromAscii: function(str, pad) {\n pad = pad === undefined ? 0 : pad;\n var hex = this.toHex(str);\n while(hex.length < pad*2)\n hex += \"00\";\n return \"0x\" + hex;\n },\n\n /// @returns decimal representaton of hex value prefixed by 0x\n toDecimal: function (val) {\n return (new BigNumber(val.substring(2), 16).toString(10));\n },\n\n /// @returns hex representation (prefixed by 0x) of decimal value\n fromDecimal: function (val) {\n return \"0x\" + (new BigNumber(val).toString(16));\n },\n\n /// used to transform value/string to eth string\n /// TODO: use BigNumber.js to parse int\n toEth: function(str) {\n var val = typeof str === \"string\" ? str.indexOf('0x') === 0 ? parseInt(str.substr(2), 16) : parseInt(str) : str;\n var unit = 0;\n var units = ETH_UNITS;\n while (val > 3000 && unit < units.length - 1)\n {\n val /= 1000;\n unit++;\n }\n var s = val.toString().length < val.toFixed(2).length ? val.toString() : val.toFixed(2);\n var replaceFunction = function($0, $1, $2) {\n return $1 + ',' + $2;\n };\n\n while (true) {\n var o = s;\n s = s.replace(/(\\d)(\\d\\d\\d[\\.\\,])/, replaceFunction);\n if (o === s)\n break;\n }\n return s + ' ' + units[unit];\n },\n\n /// eth object prototype\n eth: {\n contractFromAbi: function (abi) {\n return function(addr) {\n // Default to address of Config. TODO: rremove prior to genesis.\n addr = addr || '0xc6d9d2cd449a754c494264e1809c50e34d64562b';\n var ret = web3.eth.contract(addr, abi);\n ret.address = addr;\n return ret;\n };\n },\n watch: function (params) {\n return new web3.filter(params, ethWatch);\n }\n },\n\n /// db object prototype\n db: {},\n\n /// shh object prototype\n shh: {\n watch: function (params) {\n return new web3.filter(params, shhWatch);\n }\n },\n\n /// @returns true if provider is installed\n haveProvider: function() {\n return !!web3.provider.provider;\n }\n};\n\n/// setups all api methods\nsetupMethods(web3, web3Methods());\nsetupMethods(web3.eth, ethMethods());\nsetupProperties(web3.eth, ethProperties());\nsetupMethods(web3.db, dbMethods());\nsetupMethods(web3.shh, shhMethods());\n\nvar ethWatch = {\n changed: 'eth_changed'\n};\n\nsetupMethods(ethWatch, ethWatchMethods());\n\nvar shhWatch = {\n changed: 'shh_changed'\n};\n\nsetupMethods(shhWatch, shhWatchMethods());\n\nweb3.setProvider = function(provider) {\n //provider.onmessage = messageHandler; // there will be no async calls, to remove\n web3.provider.set(provider);\n};\n\nmodule.exports = web3;\n\n", + "/*\n This file is part of ethereum.js.\n\n ethereum.js is free software: you can redistribute it and/or modify\n it under the terms of the GNU Lesser General Public License as published by\n the Free Software Foundation, either version 3 of the License, or\n (at your option) any later version.\n\n ethereum.js is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n GNU Lesser General Public License for more details.\n\n You should have received a copy of the GNU Lesser General Public License\n along with ethereum.js. If not, see .\n*/\n/** @file web3.js\n * @authors:\n * Jeffrey Wilcke \n * Marek Kotewicz \n * Marian Oancea \n * Gav Wood \n * @date 2014\n */\n\nif (\"build\" !== 'build') {/*\n var BigNumber = require('bignumber.js');\n*/}\n\nvar ETH_UNITS = [ \n 'wei', \n 'Kwei', \n 'Mwei', \n 'Gwei', \n 'szabo', \n 'finney', \n 'ether', \n 'grand', \n 'Mether', \n 'Gether', \n 'Tether', \n 'Pether', \n 'Eether', \n 'Zether', \n 'Yether', \n 'Nether', \n 'Dether', \n 'Vether', \n 'Uether' \n];\n\n/// @returns an array of objects describing web3 api methods\nvar web3Methods = function () {\n return [\n { name: 'sha3', call: 'web3_sha3' }\n ];\n};\n\n/// @returns an array of objects describing web3.eth api methods\nvar ethMethods = function () {\n var blockCall = function (args) {\n return typeof args[0] === \"string\" ? \"eth_blockByHash\" : \"eth_blockByNumber\";\n };\n\n var transactionCall = function (args) {\n return typeof args[0] === \"string\" ? 'eth_transactionByHash' : 'eth_transactionByNumber';\n };\n\n var uncleCall = function (args) {\n return typeof args[0] === \"string\" ? 'eth_uncleByHash' : 'eth_uncleByNumber';\n };\n\n var methods = [\n { name: 'balanceAt', call: 'eth_balanceAt' },\n { name: 'stateAt', call: 'eth_stateAt' },\n { name: 'storageAt', call: 'eth_storageAt' },\n { name: 'countAt', call: 'eth_countAt'},\n { name: 'codeAt', call: 'eth_codeAt' },\n { name: 'transact', call: 'eth_transact' },\n { name: 'call', call: 'eth_call' },\n { name: 'block', call: blockCall },\n { name: 'transaction', call: transactionCall },\n { name: 'uncle', call: uncleCall },\n { name: 'compilers', call: 'eth_compilers' },\n { name: 'flush', call: 'eth_flush' },\n { name: 'lll', call: 'eth_lll' },\n { name: 'solidity', call: 'eth_solidity' },\n { name: 'serpent', call: 'eth_serpent' },\n { name: 'logs', call: 'eth_logs' }\n ];\n return methods;\n};\n\n/// @returns an array of objects describing web3.eth api properties\nvar ethProperties = function () {\n return [\n { name: 'coinbase', getter: 'eth_coinbase', setter: 'eth_setCoinbase' },\n { name: 'listening', getter: 'eth_listening', setter: 'eth_setListening' },\n { name: 'mining', getter: 'eth_mining', setter: 'eth_setMining' },\n { name: 'gasPrice', getter: 'eth_gasPrice' },\n { name: 'accounts', getter: 'eth_accounts' },\n { name: 'peerCount', getter: 'eth_peerCount' },\n { name: 'defaultBlock', getter: 'eth_defaultBlock', setter: 'eth_setDefaultBlock' },\n { name: 'number', getter: 'eth_number'}\n ];\n};\n\n/// @returns an array of objects describing web3.db api methods\nvar dbMethods = function () {\n return [\n { name: 'put', call: 'db_put' },\n { name: 'get', call: 'db_get' },\n { name: 'putString', call: 'db_putString' },\n { name: 'getString', call: 'db_getString' }\n ];\n};\n\n/// @returns an array of objects describing web3.shh api methods\nvar shhMethods = function () {\n return [\n { name: 'post', call: 'shh_post' },\n { name: 'newIdentity', call: 'shh_newIdentity' },\n { name: 'haveIdentity', call: 'shh_haveIdentity' },\n { name: 'newGroup', call: 'shh_newGroup' },\n { name: 'addToGroup', call: 'shh_addToGroup' }\n ];\n};\n\n/// @returns an array of objects describing web3.eth.watch api methods\nvar ethWatchMethods = function () {\n var newFilter = function (args) {\n return typeof args[0] === 'string' ? 'eth_newFilterString' : 'eth_newFilter';\n };\n\n return [\n { name: 'newFilter', call: newFilter },\n { name: 'uninstallFilter', call: 'eth_uninstallFilter' },\n { name: 'getMessages', call: 'eth_filterLogs' }\n ];\n};\n\n/// @returns an array of objects describing web3.shh.watch api methods\nvar shhWatchMethods = function () {\n return [\n { name: 'newFilter', call: 'shh_newFilter' },\n { name: 'uninstallFilter', call: 'shh_uninstallFilter' },\n { name: 'getMessages', call: 'shh_getMessages' }\n ];\n};\n\n/// creates methods in a given object based on method description on input\n/// setups api calls for these methods\nvar setupMethods = function (obj, methods) {\n methods.forEach(function (method) {\n obj[method.name] = function () {\n var args = Array.prototype.slice.call(arguments);\n var call = typeof method.call === 'function' ? method.call(args) : method.call;\n return web3.provider.send({\n call: call,\n args: args\n });\n };\n });\n};\n\n/// creates properties in a given object based on properties description on input\n/// setups api calls for these properties\nvar setupProperties = function (obj, properties) {\n properties.forEach(function (property) {\n var proto = {};\n proto.get = function () {\n return web3.provider.send({\n call: property.getter\n });\n };\n\n if (property.setter) {\n proto.set = function (val) {\n return web3.provider.send({\n call: property.setter,\n args: [val]\n });\n };\n }\n Object.defineProperty(obj, property.name, proto);\n });\n};\n\n/// setups web3 object, and it's in-browser executed methods\nvar web3 = {\n _callbacks: {},\n _events: {},\n providers: {},\n\n toHex: function(str) {\n var hex = \"\";\n for(var i = 0; i < str.length; i++) {\n var n = str.charCodeAt(i).toString(16);\n hex += n.length < 2 ? '0' + n : n;\n }\n\n return hex;\n },\n\n /// @returns ascii string representation of hex value prefixed with 0x\n toAscii: function(hex) {\n // Find termination\n var str = \"\";\n var i = 0, l = hex.length;\n if (hex.substring(0, 2) === '0x')\n i = 2;\n for(; i < l; i+=2) {\n var code = parseInt(hex.substr(i, 2), 16);\n if(code === 0) {\n break;\n }\n\n str += String.fromCharCode(code);\n }\n\n return str;\n },\n\n /// @returns hex representation (prefixed by 0x) of ascii string\n fromAscii: function(str, pad) {\n pad = pad === undefined ? 0 : pad;\n var hex = this.toHex(str);\n while(hex.length < pad*2)\n hex += \"00\";\n return \"0x\" + hex;\n },\n\n /// @returns decimal representaton of hex value prefixed by 0x\n toDecimal: function (val) {\n // remove 0x and place 0, if it's required\n val = val.length > 2 ? val.substring(2) : \"0\";\n return (new BigNumber(val, 16).toString(10));\n },\n\n /// @returns hex representation (prefixed by 0x) of decimal value\n fromDecimal: function (val) {\n return \"0x\" + (new BigNumber(val).toString(16));\n },\n\n /// used to transform value/string to eth string\n /// TODO: use BigNumber.js to parse int\n toEth: function(str) {\n var val = typeof str === \"string\" ? str.indexOf('0x') === 0 ? parseInt(str.substr(2), 16) : parseInt(str) : str;\n var unit = 0;\n var units = ETH_UNITS;\n while (val > 3000 && unit < units.length - 1)\n {\n val /= 1000;\n unit++;\n }\n var s = val.toString().length < val.toFixed(2).length ? val.toString() : val.toFixed(2);\n var replaceFunction = function($0, $1, $2) {\n return $1 + ',' + $2;\n };\n\n while (true) {\n var o = s;\n s = s.replace(/(\\d)(\\d\\d\\d[\\.\\,])/, replaceFunction);\n if (o === s)\n break;\n }\n return s + ' ' + units[unit];\n },\n\n /// eth object prototype\n eth: {\n contractFromAbi: function (abi) {\n return function(addr) {\n // Default to address of Config. TODO: rremove prior to genesis.\n addr = addr || '0xc6d9d2cd449a754c494264e1809c50e34d64562b';\n var ret = web3.eth.contract(addr, abi);\n ret.address = addr;\n return ret;\n };\n },\n watch: function (params) {\n return new web3.filter(params, ethWatch);\n }\n },\n\n /// db object prototype\n db: {},\n\n /// shh object prototype\n shh: {\n watch: function (params) {\n return new web3.filter(params, shhWatch);\n }\n },\n\n /// @returns true if provider is installed\n haveProvider: function() {\n return !!web3.provider.provider;\n }\n};\n\n/// setups all api methods\nsetupMethods(web3, web3Methods());\nsetupMethods(web3.eth, ethMethods());\nsetupProperties(web3.eth, ethProperties());\nsetupMethods(web3.db, dbMethods());\nsetupMethods(web3.shh, shhMethods());\n\nvar ethWatch = {\n changed: 'eth_changed'\n};\n\nsetupMethods(ethWatch, ethWatchMethods());\n\nvar shhWatch = {\n changed: 'shh_changed'\n};\n\nsetupMethods(shhWatch, shhWatchMethods());\n\nweb3.setProvider = function(provider) {\n //provider.onmessage = messageHandler; // there will be no async calls, to remove\n web3.provider.set(provider);\n};\n\nmodule.exports = web3;\n\n", "var web3 = require('./lib/web3');\nvar ProviderManager = require('./lib/providermanager');\nweb3.provider = new ProviderManager();\nweb3.filter = require('./lib/filter');\nweb3.providers.HttpSyncProvider = require('./lib/httpsync');\nweb3.providers.QtSyncProvider = require('./lib/qtsync');\nweb3.eth.contract = require('./lib/contract');\nweb3.abi = require('./lib/abi');\n\n\nmodule.exports = web3;\n" ] } \ No newline at end of file diff --git a/dist/ethereum.min.js b/dist/ethereum.min.js index 53f39e1a7..23e474fe2 100644 --- a/dist/ethereum.min.js +++ b/dist/ethereum.min.js @@ -1 +1 @@ -require=function t(e,n,r){function i(a,f){if(!n[a]){if(!e[a]){var u="function"==typeof require&&require;if(!f&&u)return u(a,!0);if(o)return o(a,!0);var s=new Error("Cannot find module '"+a+"'");throw s.code="MODULE_NOT_FOUND",s}var c=n[a]={exports:{}};e[a][0].call(c.exports,function(t){var n=e[a][1][t];return i(n?n:t)},c,c.exports,t,e,n,r)}return n[a].exports}for(var o="function"==typeof require&&require,a=0;ad;d++)p.push(u(n.slice(0,a))),n=n.slice(a);i.push(p)}else s("string")(o.outputs[e].type)?(c=c.slice(a),i.push(u(n.slice(0,a))),n=n.slice(a)):(i.push(u(n.slice(0,a))),n=n.slice(a))}),i},D=function(t){var e=t.indexOf("(");return-1!==e?t.substr(0,e):t},M=function(t){var e=t.indexOf("(");return-1!==e?t.substr(e+1,t.length-1-(e+1)):""},q=function(t){var e={};return t.forEach(function(n){var r=D(n.name),i=M(n.name),o=function(){var e=Array.prototype.slice.call(arguments);return y(t,n.name,e)};void 0===e[r]&&(e[r]=o),e[r][i]=o}),e},C=function(t){var e={};return t.forEach(function(n){var r=D(n.name),i=M(n.name),o=function(e){return T(t,n.name,e)};void 0===e[r]&&(e[r]=o),e[r][i]=o}),e},I=function(t){return n.sha3(n.fromAscii(t)).slice(0,2+2*i)};e.exports={inputParser:q,outputParser:C,methodSignature:I,methodDisplayName:D,methodTypeName:M,getMethodWithName:f}},{"./web3":7}],2:[function(t,e){var n=t("./web3"),r=t("./abi"),i=function(t,e){e.forEach(function(t){if(-1===t.name.indexOf("(")){var e=t.name,n=t.inputs.map(function(t){return t.type}).join();t.name=e+"("+n+")"}});var i=r.inputParser(e),o=r.outputParser(e),a={};return a.call=function(t){return a._isTransact=!1,a._options=t,a},a.transact=function(t){return a._isTransact=!0,a._options=t,a},a._options={},["gas","gasPrice","value","from"].forEach(function(t){a[t]=function(e){return a._options[t]=e,a}}),e.forEach(function(f){var u=r.methodDisplayName(f.name),s=r.methodTypeName(f.name),c=function(){var c=Array.prototype.slice.call(arguments),l=r.methodSignature(f.name),h=i[u][s].apply(null,c),p=a._options||{};p.to=t,p.data=l+h;var d=a._isTransact===!0||a._isTransact!==!1&&!f.constant,m=p.collapse!==!1;if(a._options={},a._isTransact=null,d)return n._currentContractAbi=e,n._currentContractAddress=t,void n.eth.transact(p);var g=n.eth.call(p),v=o[u][s](g);return m&&(1===v.length?v=v[0]:0===v.length&&(v=null)),v};void 0===a[u]&&(a[u]=c),a[u][s]=c}),a};e.exports=i},{"./abi":1,"./web3":7}],3:[function(t,e){var n=t("./web3"),r=function(t,e){this.impl=e,this.callbacks=[],this.id=e.newFilter(t),n.provider.startPolling({call:e.changed,args:[this.id]},this.id,this.trigger.bind(this))};r.prototype.arrived=function(t){this.changed(t)},r.prototype.changed=function(t){this.callbacks.push(t)},r.prototype.trigger=function(t){for(var e=0;en;n++)this.callbacks[e].call(this,t[n])},r.prototype.uninstall=function(){this.impl.uninstallFilter(this.id),n.provider.stopPolling(this.id)},r.prototype.messages=function(){return this.impl.getMessages(this.id)},r.prototype.logs=function(){return this.messages()},e.exports=r},{"./web3":7}],4:[function(t,e){function n(t){return{jsonrpc:"2.0",method:t.call,params:t.args,id:t._id}}var r=function(t){this.handlers=[],this.host=t||"http://localhost:8080"};r.prototype.send=function(t){var e=n(t),r=new XMLHttpRequest;return r.open("POST",this.host,!1),r.send(JSON.stringify(e)),r.responseText},e.exports=r},{}],5:[function(t,e){var n=(t("./web3"),function(){this.polls=[],this.provider=void 0,this.id=1;var t=this,e=function(){t.provider&&t.polls.forEach(function(e){e.data._id=t.id,t.id++;var n=t.provider.send(e.data);n=JSON.parse(n),!n.error&&n.result instanceof Array&&0!==n.result.length&&e.callback(n.result)}),setTimeout(e,1e3)};e()});n.prototype.send=function(t){if(t.args=t.args||[],t._id=this.id++,void 0===this.provider)return console.error("provider is not set"),null;var e=this.provider.send(t);return e=JSON.parse(e),e.result},n.prototype.set=function(t){this.provider=t},n.prototype.startPolling=function(t,e,n){this.polls.push({data:t,id:e,callback:n})},n.prototype.stopPolling=function(t){for(var e=this.polls.length;e--;){var n=this.polls[e];n.id===t&&this.polls.splice(e,1)}},e.exports=n},{"./web3":7}],6:[function(t,e){var n=function(){};n.prototype.send=function(t){return navigator.qt.callMethod(JSON.stringify(t))},e.exports=n},{}],7:[function(t,e){var n=["wei","Kwei","Mwei","Gwei","szabo","finney","ether","grand","Mether","Gether","Tether","Pether","Eether","Zether","Yether","Nether","Dether","Vether","Uether"],r=function(){return[{name:"sha3",call:"web3_sha3"}]},i=function(){var t=function(t){return"string"==typeof t[0]?"eth_blockByHash":"eth_blockByNumber"},e=function(t){return"string"==typeof t[0]?"eth_transactionByHash":"eth_transactionByNumber"},n=function(t){return"string"==typeof t[0]?"eth_uncleByHash":"eth_uncleByNumber"},r=[{name:"balanceAt",call:"eth_balanceAt"},{name:"stateAt",call:"eth_stateAt"},{name:"storageAt",call:"eth_storageAt"},{name:"countAt",call:"eth_countAt"},{name:"codeAt",call:"eth_codeAt"},{name:"transact",call:"eth_transact"},{name:"call",call:"eth_call"},{name:"block",call:t},{name:"transaction",call:e},{name:"uncle",call:n},{name:"compilers",call:"eth_compilers"},{name:"flush",call:"eth_flush"},{name:"lll",call:"eth_lll"},{name:"solidity",call:"eth_solidity"},{name:"serpent",call:"eth_serpent"},{name:"logs",call:"eth_logs"}];return r},o=function(){return[{name:"coinbase",getter:"eth_coinbase",setter:"eth_setCoinbase"},{name:"listening",getter:"eth_listening",setter:"eth_setListening"},{name:"mining",getter:"eth_mining",setter:"eth_setMining"},{name:"gasPrice",getter:"eth_gasPrice"},{name:"account",getter:"eth_account"},{name:"accounts",getter:"eth_accounts"},{name:"peerCount",getter:"eth_peerCount"},{name:"defaultBlock",getter:"eth_defaultBlock",setter:"eth_setDefaultBlock"},{name:"number",getter:"eth_number"}]},a=function(){return[{name:"put",call:"db_put"},{name:"get",call:"db_get"},{name:"putString",call:"db_putString"},{name:"getString",call:"db_getString"}]},f=function(){return[{name:"post",call:"shh_post"},{name:"newIdentity",call:"shh_newIdentity"},{name:"haveIdentity",call:"shh_haveIdentity"},{name:"newGroup",call:"shh_newGroup"},{name:"addToGroup",call:"shh_addToGroup"}]},u=function(){var t=function(t){return"string"==typeof t[0]?"eth_newFilterString":"eth_newFilter"};return[{name:"newFilter",call:t},{name:"uninstallFilter",call:"eth_uninstallFilter"},{name:"getMessages",call:"eth_filterLogs"}]},s=function(){return[{name:"newFilter",call:"shh_newFilter"},{name:"uninstallFilter",call:"shh_uninstallFilter"},{name:"getMessages",call:"shh_getMessages"}]},c=function(t,e){e.forEach(function(e){t[e.name]=function(){var t=Array.prototype.slice.call(arguments),n="function"==typeof e.call?e.call(t):e.call;return h.provider.send({call:n,args:t})}})},l=function(t,e){e.forEach(function(e){var n={};n.get=function(){return h.provider.send({call:e.getter})},e.setter&&(n.set=function(t){return h.provider.send({call:e.setter,args:[t]})}),Object.defineProperty(t,e.name,n)})},h={_callbacks:{},_events:{},providers:{},toHex:function(t){for(var e="",n=0;nn;n+=2){var i=parseInt(t.substr(n,2),16);if(0===i)break;e+=String.fromCharCode(i)}return e},fromAscii:function(t,e){e=void 0===e?0:e;for(var n=this.toHex(t);n.length<2*e;)n+="00";return"0x"+n},toDecimal:function(t){return new BigNumber(t.substring(2),16).toString(10)},fromDecimal:function(t){return"0x"+new BigNumber(t).toString(16)},toEth:function(t){for(var e="string"==typeof t?0===t.indexOf("0x")?parseInt(t.substr(2),16):parseInt(t):t,r=0,i=n;e>3e3&&rd;d++)p.push(s(n.slice(0,a))),n=n.slice(a);i.push(p)}else l("string")(o.outputs[e].type)?(f=f.slice(a),i.push(s(n.slice(0,a))),n=n.slice(a)):(i.push(s(n.slice(0,a))),n=n.slice(a))}),i},C=function(t){var e=t.indexOf("(");return-1!==e?t.substr(0,e):t},q=function(t){var e=t.indexOf("(");return-1!==e?t.substr(e+1,t.length-1-(e+1)):""},I=function(t){var e={};return s(t).forEach(function(n){var r=C(n.name),i=q(n.name),o=function(){var e=Array.prototype.slice.call(arguments);return w(t,n.name,e)};void 0===e[r]&&(e[r]=o),e[r][i]=o}),e},G=function(t){var e={};return s(t).forEach(function(n){var r=C(n.name),i=q(n.name),o=function(e){return D(t,n.name,e)};void 0===e[r]&&(e[r]=o),e[r][i]=o}),e},H=function(t){return n.sha3(n.fromAscii(t)).slice(0,2+2*i)};e.exports={inputParser:I,outputParser:G,methodSignature:H,methodDisplayName:C,methodTypeName:q,getMethodWithName:u,filterFunctions:s,filterEvents:f}},{"./web3":8}],2:[function(t,e){var n=t("./web3"),r=t("./abi"),i=t("./event"),o=function(t){t.call=function(e){return t._isTransact=!1,t._options=e,t},t.transact=function(e){return t._isTransact=!0,t._options=e,t},t._options={},["gas","gasPrice","value","from"].forEach(function(e){t[e]=function(n){return t._options[e]=n,t}})},a=function(t,e,i){var o=r.inputParser(e),a=r.outputParser(e);r.filterFunctions(e).forEach(function(u){var s=r.methodDisplayName(u.name),f=r.methodTypeName(u.name),c=function(){var c=Array.prototype.slice.call(arguments),l=r.methodSignature(u.name),p=o[s][f].apply(null,c),h=t._options||{};h.to=i,h.data=l+p;var d=t._isTransact===!0||t._isTransact!==!1&&!u.constant,m=h.collapse!==!1;if(t._options={},t._isTransact=null,d)return n._currentContractAbi=e,n._currentContractAddress=i,n._currentContractMethodName=u.name,n._currentContractMethodParams=c,void n.eth.transact(h);var g=n.eth.call(h),v=a[s][f](g);return m&&(1===v.length?v=v[0]:0===v.length&&(v=null)),v};void 0===t[s]&&(t[s]=c),t[s][f]=c})},u=function(t,e,n){t.address=n,Object.defineProperty(t,"topics",{get:function(){return r.filterEvents(e).map(function(t){return r.methodSignature(t.name)})}})},s=function(t,e,o){r.filterEvents(e).forEach(function(e){var a=function(){var t=Array.prototype.slice.call(arguments),a=r.methodSignature(e.name),u=i(o,a),s=u.apply(null,t);return n.eth.watch(s)};a.address=o,Object.defineProperty(a,"topics",{get:function(){return[r.methodSignature(e.name)]}});var u=r.methodDisplayName(e.name),s=r.methodTypeName(e.name);void 0===t[u]&&(t[u]=a),t[u][s]=a})},f=function(t,e){e.forEach(function(t){if(-1===t.name.indexOf("(")){var e=t.name,n=t.inputs.map(function(t){return t.type}).join();t.name=e+"("+n+")"}});var n={};return o(n),a(n,e,t),u(n,e,t),s(n,e,t),n};e.exports=f},{"./abi":1,"./event":3,"./web3":8}],3:[function(t,e){var n=function(t,e){return function(n){var r=n||{};return r.address=r.address||t,r.topics=r.topics||[],r.topics.push(e),r}};e.exports=n},{}],4:[function(t,e){var n=t("./web3"),r=function(t,e){this.impl=e,this.callbacks=[],"string"!=typeof t&&(t={to:t.to,topics:t.topics,earliest:t.earliest,latest:t.latest,max:t.max,skip:t.skip,address:t.address}),this.id=e.newFilter(t),n.provider.startPolling({call:e.changed,args:[this.id]},this.id,this.trigger.bind(this))};r.prototype.arrived=function(t){this.changed(t)},r.prototype.changed=function(t){this.callbacks.push(t)},r.prototype.trigger=function(t){for(var e=0;en;n+=2){var i=parseInt(t.substr(n,2),16);if(0===i)break;e+=String.fromCharCode(i)}return e},fromAscii:function(t,e){e=void 0===e?0:e;for(var n=this.toHex(t);n.length<2*e;)n+="00";return"0x"+n},toDecimal:function(t){return t=t.length>2?t.substring(2):"0",new BigNumber(t,16).toString(10)},fromDecimal:function(t){return"0x"+new BigNumber(t).toString(16)},toEth:function(t){for(var e="string"==typeof t?0===t.indexOf("0x")?parseInt(t.substr(2),16):parseInt(t):t,r=0,i=n;e>3e3&&r + + + + + + + + +
+ +
+
+ +
+
+ +
+ + diff --git a/example/natspec_contract.html b/example/natspec_contract.html index 40561a27c..212e582dc 100644 --- a/example/natspec_contract.html +++ b/example/natspec_contract.html @@ -21,6 +21,7 @@ // contract description, this will be autogenerated somehow var desc = [{ "name": "multiply(uint256)", + "type": "function", "inputs": [ { "name": "a", diff --git a/lib/abi.js b/lib/abi.js index 0989f4e15..a0c862593 100644 --- a/lib/abi.js +++ b/lib/abi.js @@ -65,6 +65,22 @@ var getMethodWithName = function (json, methodName) { 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 number of characters that result string should have /// @param sign, by default 0 @@ -211,6 +227,7 @@ var signedIsNegative = function (value) { /// Formats input right-aligned input bytes to int /// @returns right-aligned input bytes formatted to int var formatOutputInt = function (value) { + value = value || "0"; // check if it's negative number // it it is, return two's complement if (signedIsNegative(value)) { @@ -222,6 +239,7 @@ var formatOutputInt = function (value) { /// Formats big right-aligned input bytes to uint /// @returns right-aligned input bytes formatted to uint var formatOutputUInt = function (value) { + value = value || "0"; return new BigNumber(value, 16); }; @@ -349,7 +367,7 @@ var methodTypeName = function (method) { /// @returns input parser object for given json abi var inputParser = function (json) { var parser = {}; - json.forEach(function (method) { + filterFunctions(json).forEach(function (method) { var displayName = methodDisplayName(method.name); var typeName = methodTypeName(method.name); @@ -372,7 +390,7 @@ var inputParser = function (json) { /// @returns output parser for given json abi var outputParser = function (json) { var parser = {}; - json.forEach(function (method) { + filterFunctions(json).forEach(function (method) { var displayName = methodDisplayName(method.name); var typeName = methodTypeName(method.name); @@ -403,6 +421,8 @@ module.exports = { methodSignature: methodSignature, methodDisplayName: methodDisplayName, methodTypeName: methodTypeName, - getMethodWithName: getMethodWithName + getMethodWithName: getMethodWithName, + filterFunctions: filterFunctions, + filterEvents: filterEvents }; diff --git a/lib/contract.js b/lib/contract.js index 65115a211..0bf3ee471 100644 --- a/lib/contract.js +++ b/lib/contract.js @@ -20,71 +20,40 @@ * @date 2014 */ -var web3 = require('./web3'); // jshint ignore:line +var web3 = require('./web3'); var abi = require('./abi'); - -/** - * This method should be called when we want to call / transact some solidity method from javascript - * it returns an object which has same methods available as solidity contract description - * usage example: - * - * var abi = [{ - * name: 'myMethod', - * inputs: [{ name: 'a', type: 'string' }], - * outputs: [{name: 'd', type: 'string' }] - * }]; // contract abi - * - * var myContract = web3.eth.contract('0x0123123121', abi); // creation of contract object - * - * myContract.myMethod('this is test string param for call'); // myMethod call (implicit, default) - * myContract.call().myMethod('this is test string param for call'); // myMethod call (explicit) - * myContract.transact().myMethod('this is test string param for transact'); // myMethod transact - * - * @param address - address of the contract, which should be called - * @param desc - abi json description of the contract, which is being created - * @returns contract object - */ - -var contract = function (address, desc) { - - desc.forEach(function (method) { - // workaround for invalid assumption that method.name is the full anonymous prototype of the method. - // it's not. it's just the name. the rest of the code assumes it's actually the anonymous - // prototype, so we make it so as a workaround. - if (method.name.indexOf('(') === -1) { - var displayName = method.name; - var typeName = method.inputs.map(function(i){return i.type; }).join(); - method.name = displayName + '(' + typeName + ')'; - } - }); - - var inputParser = abi.inputParser(desc); - var outputParser = abi.outputParser(desc); - - var result = {}; - - result.call = function (options) { - result._isTransact = false; - result._options = options; - return result; +var eventImpl = require('./event'); + +var addFunctionRelatedPropertiesToContract = function (contract) { + + contract.call = function (options) { + contract._isTransact = false; + contract._options = options; + return contract; }; - result.transact = function (options) { - result._isTransact = true; - result._options = options; - return result; + contract.transact = function (options) { + contract._isTransact = true; + contract._options = options; + return contract; }; - result._options = {}; + contract._options = {}; ['gas', 'gasPrice', 'value', 'from'].forEach(function(p) { - result[p] = function (v) { - result._options[p] = v; - return result; + contract[p] = function (v) { + contract._options[p] = v; + return contract; }; }); +}; - desc.forEach(function (method) { +var addFunctionsToContract = function (contract, desc, address) { + var inputParser = abi.inputParser(desc); + var outputParser = abi.outputParser(desc); + + // create contract functions + abi.filterFunctions(desc).forEach(function (method) { var displayName = abi.methodDisplayName(method.name); var typeName = abi.methodTypeName(method.name); @@ -94,16 +63,16 @@ var contract = function (address, desc) { var signature = abi.methodSignature(method.name); var parsed = inputParser[displayName][typeName].apply(null, params); - var options = result._options || {}; + var options = contract._options || {}; options.to = address; options.data = signature + parsed; - var isTransact = result._isTransact === true || (result._isTransact !== false && !method.constant); + var isTransact = contract._isTransact === true || (contract._isTransact !== false && !method.constant); var collapse = options.collapse !== false; // reset - result._options = {}; - result._isTransact = null; + contract._options = {}; + contract._isTransact = null; if (isTransact) { // it's used byt natspec.js @@ -111,6 +80,7 @@ var contract = function (address, desc) { web3._currentContractAbi = desc; web3._currentContractAddress = address; web3._currentContractMethodName = method.name; + web3._currentContractMethodParams = params; // transactions do not have any output, cause we do not know, when they will be processed web3.eth.transact(options); @@ -129,14 +99,103 @@ var contract = function (address, desc) { return ret; }; - if (result[displayName] === undefined) { - result[displayName] = impl; + if (contract[displayName] === undefined) { + contract[displayName] = impl; + } + + contract[displayName][typeName] = impl; + }); +}; + +var addEventRelatedPropertiesToContract = function (contract, desc, address) { + contract.address = address; + + Object.defineProperty(contract, 'topics', { + 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, 'topics', { + 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; } - result[displayName][typeName] = 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; }; diff --git a/lib/event.js b/lib/event.js new file mode 100644 index 000000000..ae2195381 --- /dev/null +++ b/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 . +*/ +/** @file event.js + * @authors: + * Marek Kotewicz + * @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; + diff --git a/lib/filter.js b/lib/filter.js index 8c7dc6e33..677b0657c 100644 --- a/lib/filter.js +++ b/lib/filter.js @@ -31,6 +31,19 @@ var Filter = function(options, impl) { this.impl = impl; this.callbacks = []; + if (typeof options !== "string") { + // evaluate lazy properties + options = { + to: options.to, + topics: options.topics, + earliest: options.earliest, + latest: options.latest, + max: options.max, + skip: options.skip, + address: options.address + }; + } + this.id = impl.newFilter(options); web3.provider.startPolling({call: impl.changed, args: [this.id]}, this.id, this.trigger.bind(this)); }; @@ -48,7 +61,7 @@ Filter.prototype.changed = function(callback) { /// trigger calling new message from people Filter.prototype.trigger = function(messages) { for (var i = 0; i < this.callbacks.length; i++) { - for (var j = 0; j < messages; j++) { + for (var j = 0; j < messages.length; j++) { this.callbacks[i].call(this, messages[j]); } } diff --git a/lib/providermanager.js b/lib/providermanager.js index 1a550e5f4..25cd14288 100644 --- a/lib/providermanager.js +++ b/lib/providermanager.js @@ -76,6 +76,12 @@ ProviderManager.prototype.send = function(data) { //TODO: handle error here? var result = this.provider.send(data); result = JSON.parse(result); + + if (result.error) { + console.log(result.error); + return null; + } + return result.result; }; diff --git a/lib/web3.js b/lib/web3.js index 7cf624c9c..7b8bbd28a 100644 --- a/lib/web3.js +++ b/lib/web3.js @@ -98,7 +98,6 @@ var ethProperties = function () { { name: 'listening', getter: 'eth_listening', setter: 'eth_setListening' }, { name: 'mining', getter: 'eth_mining', setter: 'eth_setMining' }, { name: 'gasPrice', getter: 'eth_gasPrice' }, - { name: 'account', getter: 'eth_account' }, { name: 'accounts', getter: 'eth_accounts' }, { name: 'peerCount', getter: 'eth_peerCount' }, { name: 'defaultBlock', getter: 'eth_defaultBlock', setter: 'eth_setDefaultBlock' }, @@ -233,7 +232,9 @@ var web3 = { /// @returns decimal representaton of hex value prefixed by 0x toDecimal: function (val) { - return (new BigNumber(val.substring(2), 16).toString(10)); + // remove 0x and place 0, if it's required + val = val.length > 2 ? val.substring(2) : "0"; + return (new BigNumber(val, 16).toString(10)); }, /// @returns hex representation (prefixed by 0x) of decimal value diff --git a/test/abi.filters.js b/test/abi.filters.js new file mode 100644 index 000000000..42385fd2a --- /dev/null +++ b/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'); + + }); +}); diff --git a/test/abi.parsers.js b/test/abi.parsers.js index 19fa1d4cf..12bccf5a5 100644 --- a/test/abi.parsers.js +++ b/test/abi.parsers.js @@ -5,6 +5,7 @@ var clone = function (object) { return JSON.parse(JSON.stringify(object)); }; var description = [{ "name": "test", + "type": "function", "inputs": [{ "name": "a", "type": "uint256" @@ -339,10 +340,12 @@ describe('abi', function() { // given var d = [{ name: "test", + type: "function", inputs: [{ type: "int" }], outputs: [{ type: "int" }] },{ name: "test2", + type: "function", inputs: [{ type: "string" }], outputs: [{ type: "string" }] }]; @@ -775,10 +778,12 @@ describe('abi', function() { // given var d = [{ name: "test", + type: "function", inputs: [{ type: "int" }], outputs: [{ type: "int" }] },{ name: "test2", + type: "function", inputs: [{ type: "string" }], outputs: [{ type: "string" }] }]; @@ -823,6 +828,38 @@ describe('abi', function() { }); + it('should parse 0x value', function () { + + // given + var d = clone(description); + d[0].outputs = [ + { type: 'int' } + ]; + + // when + var parser = abi.outputParser(d); + + // then + assert.equal(parser.test("0x")[0], 0); + + }); + + it('should parse 0x value', function () { + + // given + var d = clone(description); + d[0].outputs = [ + { type: 'uint' } + ]; + + // when + var parser = abi.outputParser(d); + + // then + assert.equal(parser.test("0x")[0], 0); + + }); + }); }); diff --git a/test/eth.contract.js b/test/eth.contract.js new file mode 100644 index 000000000..1a92ec88f --- /dev/null +++ b/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']); + + }); + +}); + diff --git a/test/eth.methods.js b/test/eth.methods.js index 002268565..7a031c4c8 100644 --- a/test/eth.methods.js +++ b/test/eth.methods.js @@ -24,7 +24,6 @@ describe('web3', function() { u.propertyExists(web3.eth, 'listening'); u.propertyExists(web3.eth, 'mining'); u.propertyExists(web3.eth, 'gasPrice'); - u.propertyExists(web3.eth, 'account'); u.propertyExists(web3.eth, 'accounts'); u.propertyExists(web3.eth, 'peerCount'); u.propertyExists(web3.eth, 'defaultBlock'); diff --git a/test/event.js b/test/event.js new file mode 100644 index 000000000..781f42e5e --- /dev/null +++ b/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); + + }); +}); + diff --git a/test/web3.methods.js b/test/web3.methods.js index 1b5792110..d08495dd9 100644 --- a/test/web3.methods.js +++ b/test/web3.methods.js @@ -6,8 +6,5 @@ describe('web3', function() { u.methodExists(web3, 'sha3'); u.methodExists(web3, 'toAscii'); u.methodExists(web3, 'fromAscii'); - u.methodExists(web3, 'toFixed'); - u.methodExists(web3, 'fromFixed'); - u.methodExists(web3, 'offset'); }); From f3012d7dc0222eec540a810def136e09d62c5675 Mon Sep 17 00:00:00 2001 From: Marek Kotewicz Date: Thu, 29 Jan 2015 15:45:38 +0100 Subject: [PATCH 05/21] Squashed 'libjsqrc/ethereumjs/' changes from 94e0e5a..600c9dd 600c9dd topics are deprecated e380462 topic for backwards compatibility git-subtree-dir: libjsqrc/ethereumjs git-subtree-split: 600c9dd27dde3269a1682b875f82d3db46cee2c9 --- dist/ethereum.js | 10 +++++++--- dist/ethereum.js.map | 6 +++--- dist/ethereum.min.js | 2 +- lib/contract.js | 4 ++-- lib/filter.js | 6 +++++- 5 files changed, 18 insertions(+), 10 deletions(-) diff --git a/dist/ethereum.js b/dist/ethereum.js index fc7bf09ca..6a8c2a78f 100644 --- a/dist/ethereum.js +++ b/dist/ethereum.js @@ -541,7 +541,7 @@ var addFunctionsToContract = function (contract, desc, address) { var addEventRelatedPropertiesToContract = function (contract, desc, address) { contract.address = address; - Object.defineProperty(contract, 'topics', { + Object.defineProperty(contract, 'topic', { get: function() { return abi.filterEvents(desc).map(function (e) { return abi.methodSignature(e.name); @@ -565,7 +565,7 @@ var addEventsToContract = function (contract, desc, address) { impl.address = address; - Object.defineProperty(impl, 'topics', { + Object.defineProperty(impl, 'topic', { get: function() { return [abi.methodSignature(e.name)]; } @@ -706,9 +706,13 @@ var Filter = function(options, impl) { if (typeof options !== "string") { // evaluate lazy properties + if (options.topics) { + console.warn('"topics" is deprecated, use "topic" instead'); + } + options = { to: options.to, - topics: options.topics, + topic: options.topic, earliest: options.earliest, latest: options.latest, max: options.max, diff --git a/dist/ethereum.js.map b/dist/ethereum.js.map index 8f6bd1a1f..670cbea7e 100644 --- a/dist/ethereum.js.map +++ b/dist/ethereum.js.map @@ -13,15 +13,15 @@ "index.js" ], "names": [], - "mappings": "AAAA;ACAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AC5aA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AC3MA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACnCA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACtFA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACtEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AC9GA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AChCA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACvUA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA", + "mappings": "AAAA;ACAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AC5aA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AC3MA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACnCA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AC1FA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACtEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AC9GA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AChCA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACvUA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA", "file": "generated.js", "sourceRoot": "", "sourcesContent": [ "(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require==\"function\"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error(\"Cannot find module '\"+o+\"'\");throw f.code=\"MODULE_NOT_FOUND\",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require==\"function\"&&require;for(var o=0;o.\n*/\n/** @file abi.js\n * @authors:\n * Marek Kotewicz \n * Gav Wood \n * @date 2014\n */\n\n// TODO: is these line is supposed to be here? \nif (\"build\" !== 'build') {/*\n var BigNumber = require('bignumber.js'); // jshint ignore:line\n*/}\n\nvar web3 = require('./web3'); // jshint ignore:line\n\nBigNumber.config({ ROUNDING_MODE: BigNumber.ROUND_DOWN });\n\nvar ETH_PADDING = 32;\n\n/// method signature length in bytes\nvar ETH_METHOD_SIGNATURE_LENGTH = 4;\n\n/// Finds first index of array element matching pattern\n/// @param array\n/// @param callback pattern\n/// @returns index of element\nvar findIndex = function (array, callback) {\n var end = false;\n var i = 0;\n for (; i < array.length && !end; i++) {\n end = callback(array[i]);\n }\n return end ? i - 1 : -1;\n};\n\n/// @returns a function that is used as a pattern for 'findIndex'\nvar findMethodIndex = function (json, methodName) {\n return findIndex(json, function (method) {\n return method.name === methodName;\n });\n};\n\n/// @returns method with given method name\nvar getMethodWithName = function (json, methodName) {\n var index = findMethodIndex(json, methodName);\n if (index === -1) {\n console.error('method ' + methodName + ' not found in the abi');\n return undefined;\n }\n return json[index];\n};\n\n/// Filters all function from input abi\n/// @returns abi array with filtered objects of type 'function'\nvar filterFunctions = function (json) {\n return json.filter(function (current) {\n return current.type === 'function'; \n }); \n};\n\n/// Filters all events form input abi\n/// @returns abi array with filtered objects of type 'event'\nvar filterEvents = function (json) {\n return json.filter(function (current) {\n return current.type === 'event';\n });\n};\n\n/// @param string string to be padded\n/// @param number of characters that result string should have\n/// @param sign, by default 0\n/// @returns right aligned string\nvar padLeft = function (string, chars, sign) {\n return new Array(chars - string.length + 1).join(sign ? sign : \"0\") + string;\n};\n\n/// @param expected type prefix (string)\n/// @returns function which checks if type has matching prefix. if yes, returns true, otherwise false\nvar prefixedType = function (prefix) {\n return function (type) {\n return type.indexOf(prefix) === 0;\n };\n};\n\n/// @param expected type name (string)\n/// @returns function which checks if type is matching expected one. if yes, returns true, otherwise false\nvar namedType = function (name) {\n return function (type) {\n return name === type;\n };\n};\n\nvar arrayType = function (type) {\n return type.slice(-2) === '[]';\n};\n\n/// Formats input value to byte representation of int\n/// If value is negative, return it's two's complement\n/// If the value is floating point, round it down\n/// @returns right-aligned byte representation of int\nvar formatInputInt = function (value) {\n var padding = ETH_PADDING * 2;\n if (value instanceof BigNumber || typeof value === 'number') {\n if (typeof value === 'number')\n value = new BigNumber(value);\n value = value.round();\n\n if (value.lessThan(0)) \n value = new BigNumber(\"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff\", 16).plus(value).plus(1);\n value = value.toString(16);\n }\n else if (value.indexOf('0x') === 0)\n value = value.substr(2);\n else if (typeof value === 'string')\n value = formatInputInt(new BigNumber(value));\n else\n value = (+value).toString(16);\n return padLeft(value, padding);\n};\n\n/// Formats input value to byte representation of string\n/// @returns left-algined byte representation of string\nvar formatInputString = function (value) {\n return web3.fromAscii(value, ETH_PADDING).substr(2);\n};\n\n/// Formats input value to byte representation of bool\n/// @returns right-aligned byte representation bool\nvar formatInputBool = function (value) {\n return '000000000000000000000000000000000000000000000000000000000000000' + (value ? '1' : '0');\n};\n\n/// Formats input value to byte representation of real\n/// Values are multiplied by 2^m and encoded as integers\n/// @returns byte representation of real\nvar formatInputReal = function (value) {\n return formatInputInt(new BigNumber(value).times(new BigNumber(2).pow(128))); \n};\n\nvar dynamicTypeBytes = function (type, value) {\n // TODO: decide what to do with array of strings\n if (arrayType(type) || type === 'string') // only string itself that is dynamic; stringX is static length.\n return formatInputInt(value.length); \n return \"\";\n};\n\n/// Setups input formatters for solidity types\n/// @returns an array of input formatters \nvar setupInputTypes = function () {\n \n return [\n { type: prefixedType('uint'), format: formatInputInt },\n { type: prefixedType('int'), format: formatInputInt },\n { type: prefixedType('hash'), format: formatInputInt },\n { type: prefixedType('string'), format: formatInputString }, \n { type: prefixedType('real'), format: formatInputReal },\n { type: prefixedType('ureal'), format: formatInputReal },\n { type: namedType('address'), format: formatInputInt },\n { type: namedType('bool'), format: formatInputBool }\n ];\n};\n\nvar inputTypes = setupInputTypes();\n\n/// Formats input params to bytes\n/// @param contract json abi\n/// @param name of the method that we want to use\n/// @param array of params that will be formatted to bytes\n/// @returns bytes representation of input params\nvar toAbiInput = function (json, methodName, params) {\n var bytes = \"\";\n\n var method = getMethodWithName(json, methodName);\n var padding = ETH_PADDING * 2;\n\n /// first we iterate in search for dynamic \n method.inputs.forEach(function (input, index) {\n bytes += dynamicTypeBytes(input.type, params[index]);\n });\n\n method.inputs.forEach(function (input, i) {\n var typeMatch = false;\n for (var j = 0; j < inputTypes.length && !typeMatch; j++) {\n typeMatch = inputTypes[j].type(method.inputs[i].type, params[i]);\n }\n if (!typeMatch) {\n console.error('input parser does not support type: ' + method.inputs[i].type);\n }\n\n var formatter = inputTypes[j - 1].format;\n var toAppend = \"\";\n\n if (arrayType(method.inputs[i].type))\n toAppend = params[i].reduce(function (acc, curr) {\n return acc + formatter(curr);\n }, \"\");\n else\n toAppend = formatter(params[i]);\n\n bytes += toAppend; \n });\n return bytes;\n};\n\n/// Check if input value is negative\n/// @param value is hex format\n/// @returns true if it is negative, otherwise false\nvar signedIsNegative = function (value) {\n return (new BigNumber(value.substr(0, 1), 16).toString(2).substr(0, 1)) === '1';\n};\n\n/// Formats input right-aligned input bytes to int\n/// @returns right-aligned input bytes formatted to int\nvar formatOutputInt = function (value) {\n value = value || \"0\";\n // check if it's negative number\n // it it is, return two's complement\n if (signedIsNegative(value)) {\n return new BigNumber(value, 16).minus(new BigNumber('ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', 16)).minus(1);\n }\n return new BigNumber(value, 16);\n};\n\n/// Formats big right-aligned input bytes to uint\n/// @returns right-aligned input bytes formatted to uint\nvar formatOutputUInt = function (value) {\n value = value || \"0\";\n return new BigNumber(value, 16);\n};\n\n/// @returns input bytes formatted to real\nvar formatOutputReal = function (value) {\n return formatOutputInt(value).dividedBy(new BigNumber(2).pow(128)); \n};\n\n/// @returns input bytes formatted to ureal\nvar formatOutputUReal = function (value) {\n return formatOutputUInt(value).dividedBy(new BigNumber(2).pow(128)); \n};\n\n/// @returns right-aligned input bytes formatted to hex\nvar formatOutputHash = function (value) {\n return \"0x\" + value;\n};\n\n/// @returns right-aligned input bytes formatted to bool\nvar formatOutputBool = function (value) {\n return value === '0000000000000000000000000000000000000000000000000000000000000001' ? true : false;\n};\n\n/// @returns left-aligned input bytes formatted to ascii string\nvar formatOutputString = function (value) {\n return web3.toAscii(value);\n};\n\n/// @returns right-aligned input bytes formatted to address\nvar formatOutputAddress = function (value) {\n return \"0x\" + value.slice(value.length - 40, value.length);\n};\n\nvar dynamicBytesLength = function (type) {\n if (arrayType(type) || type === 'string') // only string itself that is dynamic; stringX is static length.\n return ETH_PADDING * 2;\n return 0;\n};\n\n/// Setups output formaters for solidity types\n/// @returns an array of output formatters\nvar setupOutputTypes = function () {\n\n return [\n { type: prefixedType('uint'), format: formatOutputUInt },\n { type: prefixedType('int'), format: formatOutputInt },\n { type: prefixedType('hash'), format: formatOutputHash },\n { type: prefixedType('string'), format: formatOutputString },\n { type: prefixedType('real'), format: formatOutputReal },\n { type: prefixedType('ureal'), format: formatOutputUReal },\n { type: namedType('address'), format: formatOutputAddress },\n { type: namedType('bool'), format: formatOutputBool }\n ];\n};\n\nvar outputTypes = setupOutputTypes();\n\n/// Formats output bytes back to param list\n/// @param contract json abi\n/// @param name of the method that we want to use\n/// @param bytes representtion of output \n/// @returns array of output params \nvar fromAbiOutput = function (json, methodName, output) {\n \n output = output.slice(2);\n var result = [];\n var method = getMethodWithName(json, methodName);\n var padding = ETH_PADDING * 2;\n\n var dynamicPartLength = method.outputs.reduce(function (acc, curr) {\n return acc + dynamicBytesLength(curr.type);\n }, 0);\n \n var dynamicPart = output.slice(0, dynamicPartLength);\n output = output.slice(dynamicPartLength);\n\n method.outputs.forEach(function (out, i) {\n var typeMatch = false;\n for (var j = 0; j < outputTypes.length && !typeMatch; j++) {\n typeMatch = outputTypes[j].type(method.outputs[i].type);\n }\n\n if (!typeMatch) {\n console.error('output parser does not support type: ' + method.outputs[i].type);\n }\n\n var formatter = outputTypes[j - 1].format;\n if (arrayType(method.outputs[i].type)) {\n var size = formatOutputUInt(dynamicPart.slice(0, padding));\n dynamicPart = dynamicPart.slice(padding);\n var array = [];\n for (var k = 0; k < size; k++) {\n array.push(formatter(output.slice(0, padding))); \n output = output.slice(padding);\n }\n result.push(array);\n }\n else if (prefixedType('string')(method.outputs[i].type)) {\n dynamicPart = dynamicPart.slice(padding); \n result.push(formatter(output.slice(0, padding)));\n output = output.slice(padding);\n } else {\n result.push(formatter(output.slice(0, padding)));\n output = output.slice(padding);\n }\n });\n\n return result;\n};\n\n/// @returns display name for method eg. multiply(uint256) -> multiply\nvar methodDisplayName = function (method) {\n var length = method.indexOf('('); \n return length !== -1 ? method.substr(0, length) : method;\n};\n\n/// @returns overloaded part of method's name\nvar methodTypeName = function (method) {\n /// TODO: make it not vulnerable\n var length = method.indexOf('(');\n return length !== -1 ? method.substr(length + 1, method.length - 1 - (length + 1)) : \"\";\n};\n\n/// @param json abi for contract\n/// @returns input parser object for given json abi\nvar inputParser = function (json) {\n var parser = {};\n filterFunctions(json).forEach(function (method) {\n var displayName = methodDisplayName(method.name); \n var typeName = methodTypeName(method.name);\n\n var impl = function () {\n var params = Array.prototype.slice.call(arguments);\n return toAbiInput(json, method.name, params);\n };\n \n if (parser[displayName] === undefined) {\n parser[displayName] = impl;\n }\n\n parser[displayName][typeName] = impl;\n });\n\n return parser;\n};\n\n/// @param json abi for contract\n/// @returns output parser for given json abi\nvar outputParser = function (json) {\n var parser = {};\n filterFunctions(json).forEach(function (method) {\n\n var displayName = methodDisplayName(method.name); \n var typeName = methodTypeName(method.name);\n\n var impl = function (output) {\n return fromAbiOutput(json, method.name, output);\n };\n\n if (parser[displayName] === undefined) {\n parser[displayName] = impl;\n }\n\n parser[displayName][typeName] = impl;\n });\n\n return parser;\n};\n\n/// @param method name for which we want to get method signature\n/// @returns (promise) contract method signature for method with given name\nvar methodSignature = function (name) {\n return web3.sha3(web3.fromAscii(name)).slice(0, 2 + ETH_METHOD_SIGNATURE_LENGTH * 2);\n};\n\nmodule.exports = {\n inputParser: inputParser,\n outputParser: outputParser,\n methodSignature: methodSignature,\n methodDisplayName: methodDisplayName,\n methodTypeName: methodTypeName,\n getMethodWithName: getMethodWithName,\n filterFunctions: filterFunctions,\n filterEvents: filterEvents\n};\n\n", - "/*\n This file is part of ethereum.js.\n\n ethereum.js is free software: you can redistribute it and/or modify\n it under the terms of the GNU Lesser General Public License as published by\n the Free Software Foundation, either version 3 of the License, or\n (at your option) any later version.\n\n ethereum.js is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n GNU Lesser General Public License for more details.\n\n You should have received a copy of the GNU Lesser General Public License\n along with ethereum.js. If not, see .\n*/\n/** @file contract.js\n * @authors:\n * Marek Kotewicz \n * @date 2014\n */\n\nvar web3 = require('./web3'); \nvar abi = require('./abi');\nvar eventImpl = require('./event');\n\nvar addFunctionRelatedPropertiesToContract = function (contract) {\n \n contract.call = function (options) {\n contract._isTransact = false;\n contract._options = options;\n return contract;\n };\n\n contract.transact = function (options) {\n contract._isTransact = true;\n contract._options = options;\n return contract;\n };\n\n contract._options = {};\n ['gas', 'gasPrice', 'value', 'from'].forEach(function(p) {\n contract[p] = function (v) {\n contract._options[p] = v;\n return contract;\n };\n });\n\n};\n\nvar addFunctionsToContract = function (contract, desc, address) {\n var inputParser = abi.inputParser(desc);\n var outputParser = abi.outputParser(desc);\n\n // create contract functions\n abi.filterFunctions(desc).forEach(function (method) {\n\n var displayName = abi.methodDisplayName(method.name);\n var typeName = abi.methodTypeName(method.name);\n\n var impl = function () {\n var params = Array.prototype.slice.call(arguments);\n var signature = abi.methodSignature(method.name);\n var parsed = inputParser[displayName][typeName].apply(null, params);\n\n var options = contract._options || {};\n options.to = address;\n options.data = signature + parsed;\n \n var isTransact = contract._isTransact === true || (contract._isTransact !== false && !method.constant);\n var collapse = options.collapse !== false;\n \n // reset\n contract._options = {};\n contract._isTransact = null;\n\n if (isTransact) {\n // it's used byt natspec.js\n // TODO: figure out better way to solve this\n web3._currentContractAbi = desc;\n web3._currentContractAddress = address;\n web3._currentContractMethodName = method.name;\n web3._currentContractMethodParams = params;\n\n // transactions do not have any output, cause we do not know, when they will be processed\n web3.eth.transact(options);\n return;\n }\n \n var output = web3.eth.call(options);\n var ret = outputParser[displayName][typeName](output);\n if (collapse)\n {\n if (ret.length === 1)\n ret = ret[0];\n else if (ret.length === 0)\n ret = null;\n }\n return ret;\n };\n\n if (contract[displayName] === undefined) {\n contract[displayName] = impl;\n }\n\n contract[displayName][typeName] = impl;\n });\n};\n\nvar addEventRelatedPropertiesToContract = function (contract, desc, address) {\n contract.address = address;\n \n Object.defineProperty(contract, 'topics', {\n get: function() {\n return abi.filterEvents(desc).map(function (e) {\n return abi.methodSignature(e.name);\n });\n }\n });\n\n};\n\nvar addEventsToContract = function (contract, desc, address) {\n // create contract events\n abi.filterEvents(desc).forEach(function (e) {\n\n var impl = function () {\n var params = Array.prototype.slice.call(arguments);\n var signature = abi.methodSignature(e.name);\n var event = eventImpl(address, signature);\n var o = event.apply(null, params);\n return web3.eth.watch(o); \n };\n\n impl.address = address;\n\n Object.defineProperty(impl, 'topics', {\n get: function() {\n return [abi.methodSignature(e.name)];\n }\n });\n \n // TODO: rename these methods, cause they are used not only for methods\n var displayName = abi.methodDisplayName(e.name);\n var typeName = abi.methodTypeName(e.name);\n\n if (contract[displayName] === undefined) {\n contract[displayName] = impl;\n }\n\n contract[displayName][typeName] = impl;\n\n });\n};\n\n\n/**\n * This method should be called when we want to call / transact some solidity method from javascript\n * it returns an object which has same methods available as solidity contract description\n * usage example: \n *\n * var abi = [{\n * name: 'myMethod',\n * inputs: [{ name: 'a', type: 'string' }],\n * outputs: [{name: 'd', type: 'string' }]\n * }]; // contract abi\n *\n * var myContract = web3.eth.contract('0x0123123121', abi); // creation of contract object\n *\n * myContract.myMethod('this is test string param for call'); // myMethod call (implicit, default)\n * myContract.call().myMethod('this is test string param for call'); // myMethod call (explicit)\n * myContract.transact().myMethod('this is test string param for transact'); // myMethod transact\n *\n * @param address - address of the contract, which should be called\n * @param desc - abi json description of the contract, which is being created\n * @returns contract object\n */\n\nvar contract = function (address, desc) {\n\n // workaround for invalid assumption that method.name is the full anonymous prototype of the method.\n // it's not. it's just the name. the rest of the code assumes it's actually the anonymous\n // prototype, so we make it so as a workaround.\n // TODO: we may not want to modify input params, maybe use copy instead?\n desc.forEach(function (method) {\n if (method.name.indexOf('(') === -1) {\n var displayName = method.name;\n var typeName = method.inputs.map(function(i){return i.type; }).join();\n method.name = displayName + '(' + typeName + ')';\n }\n });\n\n var result = {};\n addFunctionRelatedPropertiesToContract(result);\n addFunctionsToContract(result, desc, address);\n addEventRelatedPropertiesToContract(result, desc, address);\n addEventsToContract(result, desc, address);\n\n return result;\n};\n\nmodule.exports = contract;\n\n", + "/*\n This file is part of ethereum.js.\n\n ethereum.js is free software: you can redistribute it and/or modify\n it under the terms of the GNU Lesser General Public License as published by\n the Free Software Foundation, either version 3 of the License, or\n (at your option) any later version.\n\n ethereum.js is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n GNU Lesser General Public License for more details.\n\n You should have received a copy of the GNU Lesser General Public License\n along with ethereum.js. If not, see .\n*/\n/** @file contract.js\n * @authors:\n * Marek Kotewicz \n * @date 2014\n */\n\nvar web3 = require('./web3'); \nvar abi = require('./abi');\nvar eventImpl = require('./event');\n\nvar addFunctionRelatedPropertiesToContract = function (contract) {\n \n contract.call = function (options) {\n contract._isTransact = false;\n contract._options = options;\n return contract;\n };\n\n contract.transact = function (options) {\n contract._isTransact = true;\n contract._options = options;\n return contract;\n };\n\n contract._options = {};\n ['gas', 'gasPrice', 'value', 'from'].forEach(function(p) {\n contract[p] = function (v) {\n contract._options[p] = v;\n return contract;\n };\n });\n\n};\n\nvar addFunctionsToContract = function (contract, desc, address) {\n var inputParser = abi.inputParser(desc);\n var outputParser = abi.outputParser(desc);\n\n // create contract functions\n abi.filterFunctions(desc).forEach(function (method) {\n\n var displayName = abi.methodDisplayName(method.name);\n var typeName = abi.methodTypeName(method.name);\n\n var impl = function () {\n var params = Array.prototype.slice.call(arguments);\n var signature = abi.methodSignature(method.name);\n var parsed = inputParser[displayName][typeName].apply(null, params);\n\n var options = contract._options || {};\n options.to = address;\n options.data = signature + parsed;\n \n var isTransact = contract._isTransact === true || (contract._isTransact !== false && !method.constant);\n var collapse = options.collapse !== false;\n \n // reset\n contract._options = {};\n contract._isTransact = null;\n\n if (isTransact) {\n // it's used byt natspec.js\n // TODO: figure out better way to solve this\n web3._currentContractAbi = desc;\n web3._currentContractAddress = address;\n web3._currentContractMethodName = method.name;\n web3._currentContractMethodParams = params;\n\n // transactions do not have any output, cause we do not know, when they will be processed\n web3.eth.transact(options);\n return;\n }\n \n var output = web3.eth.call(options);\n var ret = outputParser[displayName][typeName](output);\n if (collapse)\n {\n if (ret.length === 1)\n ret = ret[0];\n else if (ret.length === 0)\n ret = null;\n }\n return ret;\n };\n\n if (contract[displayName] === undefined) {\n contract[displayName] = impl;\n }\n\n contract[displayName][typeName] = impl;\n });\n};\n\nvar addEventRelatedPropertiesToContract = function (contract, desc, address) {\n contract.address = address;\n \n Object.defineProperty(contract, 'topic', {\n get: function() {\n return abi.filterEvents(desc).map(function (e) {\n return abi.methodSignature(e.name);\n });\n }\n });\n\n};\n\nvar addEventsToContract = function (contract, desc, address) {\n // create contract events\n abi.filterEvents(desc).forEach(function (e) {\n\n var impl = function () {\n var params = Array.prototype.slice.call(arguments);\n var signature = abi.methodSignature(e.name);\n var event = eventImpl(address, signature);\n var o = event.apply(null, params);\n return web3.eth.watch(o); \n };\n\n impl.address = address;\n\n Object.defineProperty(impl, 'topic', {\n get: function() {\n return [abi.methodSignature(e.name)];\n }\n });\n \n // TODO: rename these methods, cause they are used not only for methods\n var displayName = abi.methodDisplayName(e.name);\n var typeName = abi.methodTypeName(e.name);\n\n if (contract[displayName] === undefined) {\n contract[displayName] = impl;\n }\n\n contract[displayName][typeName] = impl;\n\n });\n};\n\n\n/**\n * This method should be called when we want to call / transact some solidity method from javascript\n * it returns an object which has same methods available as solidity contract description\n * usage example: \n *\n * var abi = [{\n * name: 'myMethod',\n * inputs: [{ name: 'a', type: 'string' }],\n * outputs: [{name: 'd', type: 'string' }]\n * }]; // contract abi\n *\n * var myContract = web3.eth.contract('0x0123123121', abi); // creation of contract object\n *\n * myContract.myMethod('this is test string param for call'); // myMethod call (implicit, default)\n * myContract.call().myMethod('this is test string param for call'); // myMethod call (explicit)\n * myContract.transact().myMethod('this is test string param for transact'); // myMethod transact\n *\n * @param address - address of the contract, which should be called\n * @param desc - abi json description of the contract, which is being created\n * @returns contract object\n */\n\nvar contract = function (address, desc) {\n\n // workaround for invalid assumption that method.name is the full anonymous prototype of the method.\n // it's not. it's just the name. the rest of the code assumes it's actually the anonymous\n // prototype, so we make it so as a workaround.\n // TODO: we may not want to modify input params, maybe use copy instead?\n desc.forEach(function (method) {\n if (method.name.indexOf('(') === -1) {\n var displayName = method.name;\n var typeName = method.inputs.map(function(i){return i.type; }).join();\n method.name = displayName + '(' + typeName + ')';\n }\n });\n\n var result = {};\n addFunctionRelatedPropertiesToContract(result);\n addFunctionsToContract(result, desc, address);\n addEventRelatedPropertiesToContract(result, desc, address);\n addEventsToContract(result, desc, address);\n\n return result;\n};\n\nmodule.exports = contract;\n\n", "/*\n This file is part of ethereum.js.\n\n ethereum.js is free software: you can redistribute it and/or modify\n it under the terms of the GNU Lesser General Public License as published by\n the Free Software Foundation, either version 3 of the License, or\n (at your option) any later version.\n\n ethereum.js is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n GNU Lesser General Public License for more details.\n\n You should have received a copy of the GNU Lesser General Public License\n along with ethereum.js. If not, see .\n*/\n/** @file event.js\n * @authors:\n * Marek Kotewicz \n * @date 2014\n */\n\nvar implementationOfEvent = function (address, signature) {\n \n return function (options) {\n var o = options || {};\n o.address = o.address || address;\n o.topics = o.topics || [];\n o.topics.push(signature);\n return o;\n };\n};\n\nmodule.exports = implementationOfEvent;\n\n", - "/*\n This file is part of ethereum.js.\n\n ethereum.js is free software: you can redistribute it and/or modify\n it under the terms of the GNU Lesser General Public License as published by\n the Free Software Foundation, either version 3 of the License, or\n (at your option) any later version.\n\n ethereum.js is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n GNU Lesser General Public License for more details.\n\n You should have received a copy of the GNU Lesser General Public License\n along with ethereum.js. If not, see .\n*/\n/** @file filter.js\n * @authors:\n * Jeffrey Wilcke \n * Marek Kotewicz \n * Marian Oancea \n * Gav Wood \n * @date 2014\n */\n\nvar web3 = require('./web3'); // jshint ignore:line\n\n/// should be used when we want to watch something\n/// it's using inner polling mechanism and is notified about changes\nvar Filter = function(options, impl) {\n this.impl = impl;\n this.callbacks = [];\n\n if (typeof options !== \"string\") {\n // evaluate lazy properties\n options = {\n to: options.to,\n topics: options.topics,\n earliest: options.earliest,\n latest: options.latest,\n max: options.max,\n skip: options.skip,\n address: options.address\n };\n }\n\n this.id = impl.newFilter(options);\n web3.provider.startPolling({call: impl.changed, args: [this.id]}, this.id, this.trigger.bind(this));\n};\n\n/// alias for changed*\nFilter.prototype.arrived = function(callback) {\n this.changed(callback);\n};\n\n/// gets called when there is new eth/shh message\nFilter.prototype.changed = function(callback) {\n this.callbacks.push(callback);\n};\n\n/// trigger calling new message from people\nFilter.prototype.trigger = function(messages) {\n for (var i = 0; i < this.callbacks.length; i++) {\n for (var j = 0; j < messages.length; j++) {\n this.callbacks[i].call(this, messages[j]);\n }\n }\n};\n\n/// should be called to uninstall current filter\nFilter.prototype.uninstall = function() {\n this.impl.uninstallFilter(this.id);\n web3.provider.stopPolling(this.id);\n};\n\n/// should be called to manually trigger getting latest messages from the client\nFilter.prototype.messages = function() {\n return this.impl.getMessages(this.id);\n};\n\n/// alias for messages\nFilter.prototype.logs = function () {\n return this.messages();\n};\n\nmodule.exports = Filter;\n", + "/*\n This file is part of ethereum.js.\n\n ethereum.js is free software: you can redistribute it and/or modify\n it under the terms of the GNU Lesser General Public License as published by\n the Free Software Foundation, either version 3 of the License, or\n (at your option) any later version.\n\n ethereum.js is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n GNU Lesser General Public License for more details.\n\n You should have received a copy of the GNU Lesser General Public License\n along with ethereum.js. If not, see .\n*/\n/** @file filter.js\n * @authors:\n * Jeffrey Wilcke \n * Marek Kotewicz \n * Marian Oancea \n * Gav Wood \n * @date 2014\n */\n\nvar web3 = require('./web3'); // jshint ignore:line\n\n/// should be used when we want to watch something\n/// it's using inner polling mechanism and is notified about changes\nvar Filter = function(options, impl) {\n this.impl = impl;\n this.callbacks = [];\n\n if (typeof options !== \"string\") {\n // evaluate lazy properties\n if (options.topics) {\n console.warn('\"topics\" is deprecated, use \"topic\" instead');\n }\n\n options = {\n to: options.to,\n topic: options.topic,\n earliest: options.earliest,\n latest: options.latest,\n max: options.max,\n skip: options.skip,\n address: options.address\n };\n }\n\n this.id = impl.newFilter(options);\n web3.provider.startPolling({call: impl.changed, args: [this.id]}, this.id, this.trigger.bind(this));\n};\n\n/// alias for changed*\nFilter.prototype.arrived = function(callback) {\n this.changed(callback);\n};\n\n/// gets called when there is new eth/shh message\nFilter.prototype.changed = function(callback) {\n this.callbacks.push(callback);\n};\n\n/// trigger calling new message from people\nFilter.prototype.trigger = function(messages) {\n for (var i = 0; i < this.callbacks.length; i++) {\n for (var j = 0; j < messages.length; j++) {\n this.callbacks[i].call(this, messages[j]);\n }\n }\n};\n\n/// should be called to uninstall current filter\nFilter.prototype.uninstall = function() {\n this.impl.uninstallFilter(this.id);\n web3.provider.stopPolling(this.id);\n};\n\n/// should be called to manually trigger getting latest messages from the client\nFilter.prototype.messages = function() {\n return this.impl.getMessages(this.id);\n};\n\n/// alias for messages\nFilter.prototype.logs = function () {\n return this.messages();\n};\n\nmodule.exports = Filter;\n", "/*\n This file is part of ethereum.js.\n\n ethereum.js is free software: you can redistribute it and/or modify\n it under the terms of the GNU Lesser General Public License as published by\n the Free Software Foundation, either version 3 of the License, or\n (at your option) any later version.\n\n ethereum.js is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n GNU Lesser General Public License for more details.\n\n You should have received a copy of the GNU Lesser General Public License\n along with ethereum.js. If not, see .\n*/\n/** @file httpsync.js\n * @authors:\n * Marek Kotewicz \n * Marian Oancea \n * @date 2014\n */\n\nif (\"build\" !== 'build') {/*\n var XMLHttpRequest = require('xmlhttprequest').XMLHttpRequest; // jshint ignore:line\n*/}\n\nvar HttpSyncProvider = function (host) {\n this.handlers = [];\n this.host = host || 'http://localhost:8080';\n};\n\n/// Transforms inner message to proper jsonrpc object\n/// @param inner message object\n/// @returns jsonrpc object\nfunction formatJsonRpcObject(object) {\n return {\n jsonrpc: '2.0',\n method: object.call,\n params: object.args,\n id: object._id\n };\n}\n\n/// Transforms jsonrpc object to inner message\n/// @param incoming jsonrpc message \n/// @returns inner message object\nfunction formatJsonRpcMessage(message) {\n var object = JSON.parse(message);\n\n return {\n _id: object.id,\n data: object.result,\n error: object.error\n };\n}\n\nHttpSyncProvider.prototype.send = function (payload) {\n var data = formatJsonRpcObject(payload);\n \n var request = new XMLHttpRequest();\n request.open('POST', this.host, false);\n request.send(JSON.stringify(data));\n \n // check request.status\n return request.responseText;\n};\n\nmodule.exports = HttpSyncProvider;\n\n", "/*\n This file is part of ethereum.js.\n\n ethereum.js is free software: you can redistribute it and/or modify\n it under the terms of the GNU Lesser General Public License as published by\n the Free Software Foundation, either version 3 of the License, or\n (at your option) any later version.\n\n ethereum.js is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n GNU Lesser General Public License for more details.\n\n You should have received a copy of the GNU Lesser General Public License\n along with ethereum.js. If not, see .\n*/\n/** @file providermanager.js\n * @authors:\n * Jeffrey Wilcke \n * Marek Kotewicz \n * Marian Oancea \n * Gav Wood \n * @date 2014\n */\n\nvar web3 = require('./web3'); // jshint ignore:line\n\n/**\n * Provider manager object prototype\n * It's responsible for passing messages to providers\n * If no provider is set it's responsible for queuing requests\n * It's also responsible for polling the ethereum node for incoming messages\n * Default poll timeout is 12 seconds\n * If we are running ethereum.js inside ethereum browser, there are backend based tools responsible for polling,\n * and provider manager polling mechanism is not used\n */\nvar ProviderManager = function() {\n this.polls = [];\n this.provider = undefined;\n this.id = 1;\n\n var self = this;\n var poll = function () {\n if (self.provider) {\n self.polls.forEach(function (data) {\n data.data._id = self.id;\n self.id++;\n var result = self.provider.send(data.data);\n \n result = JSON.parse(result);\n \n // dont call the callback if result is not an array, or empty one\n if (result.error || !(result.result instanceof Array) || result.result.length === 0) {\n return;\n }\n\n data.callback(result.result);\n });\n }\n setTimeout(poll, 1000);\n };\n poll();\n};\n\n/// sends outgoing requests\nProviderManager.prototype.send = function(data) {\n\n data.args = data.args || [];\n data._id = this.id++;\n\n if (this.provider === undefined) {\n console.error('provider is not set');\n return null; \n }\n\n //TODO: handle error here? \n var result = this.provider.send(data);\n result = JSON.parse(result);\n\n if (result.error) {\n console.log(result.error);\n return null;\n }\n\n return result.result;\n};\n\n/// setups provider, which will be used for sending messages\nProviderManager.prototype.set = function(provider) {\n this.provider = provider;\n};\n\n/// this method is only used, when we do not have native qt bindings and have to do polling on our own\n/// should be callled, on start watching for eth/shh changes\nProviderManager.prototype.startPolling = function (data, pollId, callback) {\n this.polls.push({data: data, id: pollId, callback: callback});\n};\n\n/// should be called to stop polling for certain watch changes\nProviderManager.prototype.stopPolling = function (pollId) {\n for (var i = this.polls.length; i--;) {\n var poll = this.polls[i];\n if (poll.id === pollId) {\n this.polls.splice(i, 1);\n }\n }\n};\n\nmodule.exports = ProviderManager;\n\n", "/*\n This file is part of ethereum.js.\n\n ethereum.js is free software: you can redistribute it and/or modify\n it under the terms of the GNU Lesser General Public License as published by\n the Free Software Foundation, either version 3 of the License, or\n (at your option) any later version.\n\n ethereum.js is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n GNU Lesser General Public License for more details.\n\n You should have received a copy of the GNU Lesser General Public License\n along with ethereum.js. If not, see .\n*/\n/** @file qtsync.js\n * @authors:\n * Marek Kotewicz \n * Marian Oancea \n * @date 2014\n */\n\nvar QtSyncProvider = function () {\n};\n\nQtSyncProvider.prototype.send = function (payload) {\n return navigator.qt.callMethod(JSON.stringify(payload));\n};\n\nmodule.exports = QtSyncProvider;\n\n", diff --git a/dist/ethereum.min.js b/dist/ethereum.min.js index 23e474fe2..28d5ee6da 100644 --- a/dist/ethereum.min.js +++ b/dist/ethereum.min.js @@ -1 +1 @@ -require=function t(e,n,r){function i(a,u){if(!n[a]){if(!e[a]){var s="function"==typeof require&&require;if(!u&&s)return s(a,!0);if(o)return o(a,!0);var f=new Error("Cannot find module '"+a+"'");throw f.code="MODULE_NOT_FOUND",f}var c=n[a]={exports:{}};e[a][0].call(c.exports,function(t){var n=e[a][1][t];return i(n?n:t)},c,c.exports,t,e,n,r)}return n[a].exports}for(var o="function"==typeof require&&require,a=0;ad;d++)p.push(s(n.slice(0,a))),n=n.slice(a);i.push(p)}else l("string")(o.outputs[e].type)?(f=f.slice(a),i.push(s(n.slice(0,a))),n=n.slice(a)):(i.push(s(n.slice(0,a))),n=n.slice(a))}),i},C=function(t){var e=t.indexOf("(");return-1!==e?t.substr(0,e):t},q=function(t){var e=t.indexOf("(");return-1!==e?t.substr(e+1,t.length-1-(e+1)):""},I=function(t){var e={};return s(t).forEach(function(n){var r=C(n.name),i=q(n.name),o=function(){var e=Array.prototype.slice.call(arguments);return w(t,n.name,e)};void 0===e[r]&&(e[r]=o),e[r][i]=o}),e},G=function(t){var e={};return s(t).forEach(function(n){var r=C(n.name),i=q(n.name),o=function(e){return D(t,n.name,e)};void 0===e[r]&&(e[r]=o),e[r][i]=o}),e},H=function(t){return n.sha3(n.fromAscii(t)).slice(0,2+2*i)};e.exports={inputParser:I,outputParser:G,methodSignature:H,methodDisplayName:C,methodTypeName:q,getMethodWithName:u,filterFunctions:s,filterEvents:f}},{"./web3":8}],2:[function(t,e){var n=t("./web3"),r=t("./abi"),i=t("./event"),o=function(t){t.call=function(e){return t._isTransact=!1,t._options=e,t},t.transact=function(e){return t._isTransact=!0,t._options=e,t},t._options={},["gas","gasPrice","value","from"].forEach(function(e){t[e]=function(n){return t._options[e]=n,t}})},a=function(t,e,i){var o=r.inputParser(e),a=r.outputParser(e);r.filterFunctions(e).forEach(function(u){var s=r.methodDisplayName(u.name),f=r.methodTypeName(u.name),c=function(){var c=Array.prototype.slice.call(arguments),l=r.methodSignature(u.name),p=o[s][f].apply(null,c),h=t._options||{};h.to=i,h.data=l+p;var d=t._isTransact===!0||t._isTransact!==!1&&!u.constant,m=h.collapse!==!1;if(t._options={},t._isTransact=null,d)return n._currentContractAbi=e,n._currentContractAddress=i,n._currentContractMethodName=u.name,n._currentContractMethodParams=c,void n.eth.transact(h);var g=n.eth.call(h),v=a[s][f](g);return m&&(1===v.length?v=v[0]:0===v.length&&(v=null)),v};void 0===t[s]&&(t[s]=c),t[s][f]=c})},u=function(t,e,n){t.address=n,Object.defineProperty(t,"topics",{get:function(){return r.filterEvents(e).map(function(t){return r.methodSignature(t.name)})}})},s=function(t,e,o){r.filterEvents(e).forEach(function(e){var a=function(){var t=Array.prototype.slice.call(arguments),a=r.methodSignature(e.name),u=i(o,a),s=u.apply(null,t);return n.eth.watch(s)};a.address=o,Object.defineProperty(a,"topics",{get:function(){return[r.methodSignature(e.name)]}});var u=r.methodDisplayName(e.name),s=r.methodTypeName(e.name);void 0===t[u]&&(t[u]=a),t[u][s]=a})},f=function(t,e){e.forEach(function(t){if(-1===t.name.indexOf("(")){var e=t.name,n=t.inputs.map(function(t){return t.type}).join();t.name=e+"("+n+")"}});var n={};return o(n),a(n,e,t),u(n,e,t),s(n,e,t),n};e.exports=f},{"./abi":1,"./event":3,"./web3":8}],3:[function(t,e){var n=function(t,e){return function(n){var r=n||{};return r.address=r.address||t,r.topics=r.topics||[],r.topics.push(e),r}};e.exports=n},{}],4:[function(t,e){var n=t("./web3"),r=function(t,e){this.impl=e,this.callbacks=[],"string"!=typeof t&&(t={to:t.to,topics:t.topics,earliest:t.earliest,latest:t.latest,max:t.max,skip:t.skip,address:t.address}),this.id=e.newFilter(t),n.provider.startPolling({call:e.changed,args:[this.id]},this.id,this.trigger.bind(this))};r.prototype.arrived=function(t){this.changed(t)},r.prototype.changed=function(t){this.callbacks.push(t)},r.prototype.trigger=function(t){for(var e=0;en;n+=2){var i=parseInt(t.substr(n,2),16);if(0===i)break;e+=String.fromCharCode(i)}return e},fromAscii:function(t,e){e=void 0===e?0:e;for(var n=this.toHex(t);n.length<2*e;)n+="00";return"0x"+n},toDecimal:function(t){return t=t.length>2?t.substring(2):"0",new BigNumber(t,16).toString(10)},fromDecimal:function(t){return"0x"+new BigNumber(t).toString(16)},toEth:function(t){for(var e="string"==typeof t?0===t.indexOf("0x")?parseInt(t.substr(2),16):parseInt(t):t,r=0,i=n;e>3e3&&rd;d++)p.push(s(n.slice(0,a))),n=n.slice(a);i.push(p)}else l("string")(o.outputs[e].type)?(f=f.slice(a),i.push(s(n.slice(0,a))),n=n.slice(a)):(i.push(s(n.slice(0,a))),n=n.slice(a))}),i},C=function(t){var e=t.indexOf("(");return-1!==e?t.substr(0,e):t},q=function(t){var e=t.indexOf("(");return-1!==e?t.substr(e+1,t.length-1-(e+1)):""},I=function(t){var e={};return s(t).forEach(function(n){var r=C(n.name),i=q(n.name),o=function(){var e=Array.prototype.slice.call(arguments);return w(t,n.name,e)};void 0===e[r]&&(e[r]=o),e[r][i]=o}),e},G=function(t){var e={};return s(t).forEach(function(n){var r=C(n.name),i=q(n.name),o=function(e){return D(t,n.name,e)};void 0===e[r]&&(e[r]=o),e[r][i]=o}),e},H=function(t){return n.sha3(n.fromAscii(t)).slice(0,2+2*i)};e.exports={inputParser:I,outputParser:G,methodSignature:H,methodDisplayName:C,methodTypeName:q,getMethodWithName:u,filterFunctions:s,filterEvents:f}},{"./web3":8}],2:[function(t,e){var n=t("./web3"),r=t("./abi"),i=t("./event"),o=function(t){t.call=function(e){return t._isTransact=!1,t._options=e,t},t.transact=function(e){return t._isTransact=!0,t._options=e,t},t._options={},["gas","gasPrice","value","from"].forEach(function(e){t[e]=function(n){return t._options[e]=n,t}})},a=function(t,e,i){var o=r.inputParser(e),a=r.outputParser(e);r.filterFunctions(e).forEach(function(u){var s=r.methodDisplayName(u.name),f=r.methodTypeName(u.name),c=function(){var c=Array.prototype.slice.call(arguments),l=r.methodSignature(u.name),p=o[s][f].apply(null,c),h=t._options||{};h.to=i,h.data=l+p;var d=t._isTransact===!0||t._isTransact!==!1&&!u.constant,m=h.collapse!==!1;if(t._options={},t._isTransact=null,d)return n._currentContractAbi=e,n._currentContractAddress=i,n._currentContractMethodName=u.name,n._currentContractMethodParams=c,void n.eth.transact(h);var g=n.eth.call(h),v=a[s][f](g);return m&&(1===v.length?v=v[0]:0===v.length&&(v=null)),v};void 0===t[s]&&(t[s]=c),t[s][f]=c})},u=function(t,e,n){t.address=n,Object.defineProperty(t,"topic",{get:function(){return r.filterEvents(e).map(function(t){return r.methodSignature(t.name)})}})},s=function(t,e,o){r.filterEvents(e).forEach(function(e){var a=function(){var t=Array.prototype.slice.call(arguments),a=r.methodSignature(e.name),u=i(o,a),s=u.apply(null,t);return n.eth.watch(s)};a.address=o,Object.defineProperty(a,"topic",{get:function(){return[r.methodSignature(e.name)]}});var u=r.methodDisplayName(e.name),s=r.methodTypeName(e.name);void 0===t[u]&&(t[u]=a),t[u][s]=a})},f=function(t,e){e.forEach(function(t){if(-1===t.name.indexOf("(")){var e=t.name,n=t.inputs.map(function(t){return t.type}).join();t.name=e+"("+n+")"}});var n={};return o(n),a(n,e,t),u(n,e,t),s(n,e,t),n};e.exports=f},{"./abi":1,"./event":3,"./web3":8}],3:[function(t,e){var n=function(t,e){return function(n){var r=n||{};return r.address=r.address||t,r.topics=r.topics||[],r.topics.push(e),r}};e.exports=n},{}],4:[function(t,e){var n=t("./web3"),r=function(t,e){this.impl=e,this.callbacks=[],"string"!=typeof t&&(t.topics&&console.warn('"topics" is deprecated, use "topic" instead'),t={to:t.to,topic:t.topic,earliest:t.earliest,latest:t.latest,max:t.max,skip:t.skip,address:t.address}),this.id=e.newFilter(t),n.provider.startPolling({call:e.changed,args:[this.id]},this.id,this.trigger.bind(this))};r.prototype.arrived=function(t){this.changed(t)},r.prototype.changed=function(t){this.callbacks.push(t)},r.prototype.trigger=function(t){for(var e=0;en;n+=2){var i=parseInt(t.substr(n,2),16);if(0===i)break;e+=String.fromCharCode(i)}return e},fromAscii:function(t,e){e=void 0===e?0:e;for(var n=this.toHex(t);n.length<2*e;)n+="00";return"0x"+n},toDecimal:function(t){return t=t.length>2?t.substring(2):"0",new BigNumber(t,16).toString(10)},fromDecimal:function(t){return"0x"+new BigNumber(t).toString(16)},toEth:function(t){for(var e="string"==typeof t?0===t.indexOf("0x")?parseInt(t.substr(2),16):parseInt(t):t,r=0,i=n;e>3e3&&r Date: Thu, 29 Jan 2015 15:46:04 +0100 Subject: [PATCH 06/21] topics -> topic --- libweb3jsonrpc/WebThreeStubServerBase.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/libweb3jsonrpc/WebThreeStubServerBase.cpp b/libweb3jsonrpc/WebThreeStubServerBase.cpp index 35848cf78..0d2c07747 100644 --- a/libweb3jsonrpc/WebThreeStubServerBase.cpp +++ b/libweb3jsonrpc/WebThreeStubServerBase.cpp @@ -77,7 +77,7 @@ static Json::Value toJson(dev::eth::LocalisedLogEntry const& _e) res["data"] = jsFromBinary(_e.data); res["address"] = toJS(_e.address); for (auto const& t: _e.topics) - res["topics"].append(toJS(t)); + res["topic"].append(toJS(t)); res["number"] = _e.number; return res; } @@ -123,10 +123,10 @@ static dev::eth::LogFilter toLogFilter(Json::Value const& _json) // commented to else if (_json["address"].isString()) filter.address(jsToAddress(_json["address"].asString())); } - if (!_json["topics"].empty() && _json["topics"].isArray()) + if (!_json["topic"].empty() && _json["topic"].isArray()) { unsigned i = 0; - for (auto t: _json["topics"]) + for (auto t: _json["topic"]) { if (t.isArray()) for (auto tt: t) @@ -201,8 +201,8 @@ static Json::Value toJson(h256 const& _h, shh::Envelope const& _e, shh::Message res["sent"] = (int)_e.sent(); res["ttl"] = (int)_e.ttl(); res["workProved"] = (int)_e.workProved(); - for (auto const& t: _e.topics()) - res["topics"].append(toJS(t)); + for (auto const& t: _e.topic()) + res["topic"].append(toJS(t)); res["payload"] = toJS(_m.payload()); res["from"] = toJS(_m.from()); res["to"] = toJS(_m.to()); From 122aada70fc2bfaf61d6b97f6f62b878bc5ad6b7 Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Thu, 29 Jan 2015 16:39:30 +0100 Subject: [PATCH 07/21] Contract Interface Functions now return FunctionType - Enchanced Function Type by declaration so that it can provide all the required information at each place interface functions are consumed - Changed all places where interface functions was used. - Simplified Mix's FunctionDefinition code --- alethzero/MainWin.cpp | 2 +- libsolidity/AST.cpp | 21 ++++++------ libsolidity/AST.h | 6 ++-- libsolidity/Compiler.cpp | 6 ++-- libsolidity/InterfaceHandler.cpp | 44 ++++++++++++++---------- libsolidity/Types.cpp | 46 +++++++++++++++++++++++--- libsolidity/Types.h | 15 ++++++++- mix/QContractDefinition.cpp | 5 +-- mix/QFunctionDefinition.cpp | 34 ++++++------------- mix/QFunctionDefinition.h | 2 +- mix/QVariableDeclaration.h | 2 +- test/SolidityNameAndTypeResolution.cpp | 18 +++++----- 12 files changed, 121 insertions(+), 80 deletions(-) diff --git a/alethzero/MainWin.cpp b/alethzero/MainWin.cpp index 103dfbb33..289603b60 100644 --- a/alethzero/MainWin.cpp +++ b/alethzero/MainWin.cpp @@ -1674,7 +1674,7 @@ string const Main::getFunctionHashes(dev::solidity::CompilerStack const &_compil { ret += it.first.abridged(); ret += " :"; - ret += it.second.getName() + "\n"; + ret += it.second->getDeclaration()->getName() + "\n"; } return ret; } diff --git a/libsolidity/AST.cpp b/libsolidity/AST.cpp index d95a254e9..bd333b91e 100644 --- a/libsolidity/AST.cpp +++ b/libsolidity/AST.cpp @@ -66,24 +66,25 @@ void ContractDefinition::checkTypeRequirements() // check for hash collisions in function signatures set> hashes; - for (auto const& hashAndFunction: getInterfaceFunctionList()) + for (auto const& it: getInterfaceFunctionList()) { - FixedHash<4> const& hash = std::get<0>(hashAndFunction); + FixedHash<4> const& hash = it.first; if (hashes.count(hash)) BOOST_THROW_EXCEPTION(createTypeError( std::string("Function signature hash collision for ") + - std::get<1>(hashAndFunction)->getCanonicalSignature(std::get<2>(hashAndFunction)->getName()))); + it.second->getCanonicalSignature())); hashes.insert(hash); } } -map, FunctionDescription> ContractDefinition::getInterfaceFunctions() const +map, std::shared_ptr> ContractDefinition::getInterfaceFunctions() const { auto exportedFunctionList = getInterfaceFunctionList(); - map, FunctionDescription> exportedFunctions; + map, std::shared_ptr> exportedFunctions; for (auto const& it: exportedFunctionList) - exportedFunctions.insert(make_pair(std::get<0>(it), FunctionDescription(std::get<1>(it), std::get<2>(it)))); + // exportedFunctions.insert(make_pair(std::get<0>(it), FunctionDescription(std::get<1>(it), std::get<2>(it)))); + exportedFunctions.insert(it); solAssert(exportedFunctionList.size() == exportedFunctions.size(), "Hash collision at Function Definition Hash calculation"); @@ -138,12 +139,12 @@ void ContractDefinition::checkIllegalOverrides() const } } -vector, std::shared_ptr, Declaration const*>> const& ContractDefinition::getInterfaceFunctionList() const +vector, shared_ptr>> const& ContractDefinition::getInterfaceFunctionList() const { if (!m_interfaceFunctionList) { set functionsSeen; - m_interfaceFunctionList.reset(new vector, std::shared_ptr, Declaration const*>>()); + m_interfaceFunctionList.reset(new vector, shared_ptr>>()); for (ContractDefinition const* contract: getLinearizedBaseContracts()) { for (ASTPointer const& f: contract->getDefinedFunctions()) @@ -151,7 +152,7 @@ vector, std::shared_ptr, Declaration cons { functionsSeen.insert(f->getName()); FixedHash<4> hash(dev::sha3(f->getCanonicalSignature())); - m_interfaceFunctionList->push_back(make_tuple(hash, make_shared(*f, false), f.get())); + m_interfaceFunctionList->push_back(make_pair(hash, make_shared(*f, false))); } for (ASTPointer const& v: contract->getStateVariables()) @@ -160,7 +161,7 @@ vector, std::shared_ptr, Declaration cons FunctionType ftype(*v); functionsSeen.insert(v->getName()); FixedHash<4> hash(dev::sha3(ftype.getCanonicalSignature(v->getName()))); - m_interfaceFunctionList->push_back(make_tuple(hash, make_shared(*v), v.get())); + m_interfaceFunctionList->push_back(make_pair(hash, make_shared(*v))); } } } diff --git a/libsolidity/AST.h b/libsolidity/AST.h index f3b18d392..e0fb14854 100755 --- a/libsolidity/AST.h +++ b/libsolidity/AST.h @@ -294,7 +294,7 @@ public: /// @returns a map of canonical function signatures to FunctionDefinitions /// as intended for use by the ABI. - std::map, FunctionDescription> getInterfaceFunctions() const; + std::map, std::shared_ptr> getInterfaceFunctions() const; /// List of all (direct and indirect) base contracts in order from derived to base, including /// the contract itself. Available after name resolution @@ -307,7 +307,7 @@ public: private: void checkIllegalOverrides() const; - std::vector, std::shared_ptr, Declaration const*>> const& getInterfaceFunctionList() const; + std::vector, std::shared_ptr>> const& getInterfaceFunctionList() const; std::vector> m_baseContracts; std::vector> m_definedStructs; @@ -316,7 +316,7 @@ private: std::vector> m_functionModifiers; std::vector m_linearizedBaseContracts; - mutable std::unique_ptr, std::shared_ptr, Declaration const*>>> m_interfaceFunctionList; + mutable std::unique_ptr, std::shared_ptr>>> m_interfaceFunctionList; }; class InheritanceSpecifier: public ASTNode diff --git a/libsolidity/Compiler.cpp b/libsolidity/Compiler.cpp index 93784adf2..33a71bca0 100644 --- a/libsolidity/Compiler.cpp +++ b/libsolidity/Compiler.cpp @@ -142,7 +142,7 @@ void Compiler::appendConstructorCall(FunctionDefinition const& _constructor) void Compiler::appendFunctionSelector(ContractDefinition const& _contract) { - map, FunctionDescription> interfaceFunctions = _contract.getInterfaceFunctions(); + map, FunctionTypePointer> interfaceFunctions = _contract.getInterfaceFunctions(); map, const eth::AssemblyItem> callDataUnpackerEntryPoints; // retrieve the function signature hash from the calldata @@ -160,11 +160,11 @@ void Compiler::appendFunctionSelector(ContractDefinition const& _contract) for (auto const& it: interfaceFunctions) { - FunctionType const* functionType = it.second.getFunctionType(); + FunctionTypePointer functionType = it.second; m_context << callDataUnpackerEntryPoints.at(it.first); eth::AssemblyItem returnTag = m_context.pushNewTag(); appendCalldataUnpacker(functionType->getParameterTypes()); - m_context.appendJumpTo(m_context.getFunctionEntryLabel(*it.second.getDeclaration())); + m_context.appendJumpTo(m_context.getFunctionEntryLabel(*it.second->getDeclaration())); m_context << returnTag; appendReturnValuePacker(functionType->getReturnParameterTypes()); } diff --git a/libsolidity/InterfaceHandler.cpp b/libsolidity/InterfaceHandler.cpp index 9b6327782..dd569946a 100644 --- a/libsolidity/InterfaceHandler.cpp +++ b/libsolidity/InterfaceHandler.cpp @@ -45,23 +45,27 @@ std::unique_ptr InterfaceHandler::getABIInterface(ContractDefinitio Json::Value inputs(Json::arrayValue); Json::Value outputs(Json::arrayValue); - auto populateParameters = [](vector const& _params) + auto populateParameters = [](vector const& _paramNames, + vector const& _paramTypes) { 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; - input["name"] = param.getName(); - input["type"] = param.getType(); + input["name"] = _paramNames[i]; + input["type"] = _paramTypes[i]; params.append(input); } return params; }; - method["name"] = it.second.getName(); - method["constant"] = it.second.isConstant(); - method["inputs"] = populateParameters(it.second.getParameters()); - method["outputs"] = populateParameters(it.second.getReturnParameters()); + method["name"] = it.second->getDeclaration()->getName(); + method["constant"] = it.second->isConstant(); + method["inputs"] = populateParameters(it.second->getParameterNames(), + it.second->getParameterTypeNames()); + method["outputs"] = populateParameters(it.second->getReturnParameterNames(), + it.second->getReturnParameterTypeNames()); methods.append(method); } return std::unique_ptr(new std::string(m_writer.write(methods))); @@ -72,16 +76,20 @@ unique_ptr InterfaceHandler::getABISolidityInterface(ContractDefinition string ret = "contract " + _contractDef.getName() + "{"; for (auto const& it: _contractDef.getInterfaceFunctions()) { - auto populateParameters = [](vector const& _params) + auto populateParameters = [](vector const& _paramNames, + vector const& _paramTypes) { string r = ""; - for (auto const& param: _params) - r += (r.size() ? "," : "(") + param.getType() + " " + param.getName(); + solAssert(_paramNames.size() == _paramTypes.size(), "Names and types vector size does not match"); + for (unsigned i = 0; i < _paramNames.size(); ++i) + r += (r.size() ? "," : "(") + _paramTypes[i] + " " + _paramNames[i]; return r.size() ? r + ")" : "()"; }; - ret += "function " + it.second.getName() + populateParameters(it.second.getParameters()) + (it.second.isConstant() ? "constant " : ""); - if (it.second.getReturnParameters().size()) - ret += "returns" + populateParameters(it.second.getReturnParameters()); + ret += "function " + it.second->getDeclaration()->getName() + + populateParameters(it.second->getParameterNames(), it.second->getParameterTypeNames()) + + (it.second->isConstant() ? "constant " : ""); + if (it.second->getReturnParameterTypes().size()) + ret += "returns" + populateParameters(it.second->getReturnParameterNames(), it.second->getReturnParameterTypeNames()); else if (ret.back() == ' ') ret.pop_back(); ret += "{}"; @@ -97,7 +105,7 @@ std::unique_ptr InterfaceHandler::getUserDocumentation(ContractDefi for (auto const& it: _contractDef.getInterfaceFunctions()) { Json::Value user; - auto strPtr = it.second.getDocumentation(); + auto strPtr = it.second->getDocumentation(); if (strPtr) { resetUser(); @@ -105,7 +113,7 @@ std::unique_ptr InterfaceHandler::getUserDocumentation(ContractDefi if (!m_notice.empty()) {// since @notice is the only user tag if missing function should not appear user["notice"] = Json::Value(m_notice); - methods[it.second.getSignature()] = user; + methods[it.second->getCanonicalSignature()] = user; } } } @@ -138,7 +146,7 @@ std::unique_ptr InterfaceHandler::getDevDocumentation(ContractDefin for (auto const& it: _contractDef.getInterfaceFunctions()) { Json::Value method; - auto strPtr = it.second.getDocumentation(); + auto strPtr = it.second->getDocumentation(); if (strPtr) { resetDev(); @@ -161,7 +169,7 @@ std::unique_ptr InterfaceHandler::getDevDocumentation(ContractDefin method["return"] = m_return; if (!method.empty()) // add the function, only if we have any documentation to add - methods[it.second.getSignature()] = method; + methods[it.second->getCanonicalSignature()] = method; } } doc["methods"] = methods; diff --git a/libsolidity/Types.cpp b/libsolidity/Types.cpp index 3d6c4e96c..e7765e1b7 100644 --- a/libsolidity/Types.cpp +++ b/libsolidity/Types.cpp @@ -499,7 +499,7 @@ MemberList const& ContractType::getMembers() const } else for (auto const& it: m_contract.getInterfaceFunctions()) - members[it.second.getName()] = it.second.getFunctionTypeShared(); + members[it.second->getDeclaration()->getName()] = it.second; m_members.reset(new MemberList(members)); } return *m_members; @@ -522,7 +522,7 @@ u256 ContractType::getFunctionIdentifier(string const& _functionName) const { auto interfaceFunctions = m_contract.getInterfaceFunctions(); for (auto const& it: m_contract.getInterfaceFunctions()) - if (it.second.getName() == _functionName) + if (it.second->getDeclaration()->getName() == _functionName) return FixedHash<4>::Arith(it.first); return Invalid256; @@ -589,12 +589,15 @@ u256 StructType::getStorageOffsetOfMember(string const& _name) const } FunctionType::FunctionType(FunctionDefinition const& _function, bool _isInternal): - m_location(_isInternal ? Location::INTERNAL : Location::EXTERNAL) + m_location(_isInternal ? Location::INTERNAL : Location::EXTERNAL), + m_isConstant(_function.isDeclaredConst()), + m_declaration(&_function) { TypePointers params; vector paramNames; TypePointers retParams; vector retParamNames; + params.reserve(_function.getParameters().size()); paramNames.reserve(_function.getParameters().size()); for (ASTPointer const& var: _function.getParameters()) @@ -616,7 +619,7 @@ FunctionType::FunctionType(FunctionDefinition const& _function, bool _isInternal } FunctionType::FunctionType(VariableDeclaration const& _varDecl): - m_location(Location::EXTERNAL) + m_location(Location::EXTERNAL), m_isConstant(true), m_declaration(&_varDecl) { TypePointers params({}); vector paramNames({}); @@ -711,7 +714,13 @@ MemberList const& FunctionType::getMembers() const string FunctionType::getCanonicalSignature(std::string const& _name) const { - string ret = _name + "("; + std::string funcName = _name; + if (_name == "") + { + solAssert(m_declaration != nullptr, "Function type without name needs a declaration"); + funcName = m_declaration->getName(); + } + string ret = funcName + "("; for (auto it = m_parameterTypes.cbegin(); it != m_parameterTypes.cend(); ++it) ret += (*it)->toString() + (it + 1 == m_parameterTypes.cend() ? "" : ","); @@ -734,6 +743,33 @@ TypePointer FunctionType::copyAndSetGasOrValue(bool _setGas, bool _setValue) con m_gasSet || _setGas, m_valueSet || _setValue); } +vector const FunctionType::getParameterTypeNames() const +{ + vector names; + for (TypePointer const& t: m_parameterTypes) + names.push_back(t->toString()); + + return names; +} + +vector const FunctionType::getReturnParameterTypeNames() const +{ + vector names; + for (TypePointer const& t: m_returnParameterTypes) + names.push_back(t->toString()); + + return names; +} + +ASTPointer FunctionType::getDocumentation() const +{ + auto function = dynamic_cast(m_declaration); + if (function) + return function->getDocumentation(); + + return ASTPointer(); +} + bool MappingType::operator==(Type const& _other) const { if (_other.getCategory() != getCategory()) diff --git a/libsolidity/Types.h b/libsolidity/Types.h index 3f6df13ee..e23939699 100644 --- a/libsolidity/Types.h +++ b/libsolidity/Types.h @@ -41,6 +41,7 @@ namespace solidity class Type; // forward class FunctionType; // forward using TypePointer = std::shared_ptr; +using FunctionTypePointer = std::shared_ptr; using TypePointers = std::vector; /** @@ -371,8 +372,10 @@ public: TypePointers const& getParameterTypes() const { return m_parameterTypes; } std::vector const& getParameterNames() const { return m_parameterNames; } + std::vector const getParameterTypeNames() const; TypePointers const& getReturnParameterTypes() const { return m_returnParameterTypes; } std::vector const& getReturnParameterNames() const { return m_returnParameterNames; } + std::vector const getReturnParameterTypeNames() const; virtual bool operator==(Type const& _other) const override; virtual std::string toString() const override; @@ -383,7 +386,15 @@ public: virtual MemberList const& getMembers() const override; Location const& getLocation() const { return m_location; } - std::string getCanonicalSignature(std::string const& _name) const; + /// @returns the canonical signature of this function type given the function name + /// If @a _name is not provided (empty string) then the @c m_declaration member of the + /// function type is used + std::string getCanonicalSignature(std::string const& _name = "") const; + Declaration const* getDeclaration() const { 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 getDocumentation() const; bool gasSet() const { return m_gasSet; } bool valueSet() const { return m_valueSet; } @@ -402,7 +413,9 @@ private: Location const m_location; bool const m_gasSet = false; ///< true iff the gas value to be used is on the stack bool const m_valueSet = false; ///< true iff the value to be sent is on the stack + bool m_isConstant; mutable std::unique_ptr m_members; + Declaration const* m_declaration = nullptr; }; /** diff --git a/mix/QContractDefinition.cpp b/mix/QContractDefinition.cpp index 503f93c39..c94de17c7 100644 --- a/mix/QContractDefinition.cpp +++ b/mix/QContractDefinition.cpp @@ -34,10 +34,7 @@ using namespace dev::mix; QContractDefinition::QContractDefinition(dev::solidity::ContractDefinition const* _contract): QBasicNodeDefinition(_contract) { if (_contract->getConstructor() != nullptr) - { - FunctionDescription desc(_contract->getConstructor()); - m_constructor = new QFunctionDefinition(desc); - } + m_constructor = new QFunctionDefinition(ContractType(*_contract).getConstructorType()); else m_constructor = new QFunctionDefinition(); diff --git a/mix/QFunctionDefinition.cpp b/mix/QFunctionDefinition.cpp index 3c1e800ca..3aaec2cb3 100644 --- a/mix/QFunctionDefinition.cpp +++ b/mix/QFunctionDefinition.cpp @@ -28,29 +28,15 @@ using namespace dev::solidity; using namespace dev::mix; -QFunctionDefinition::QFunctionDefinition(dev::solidity::FunctionDescription const& _f): QBasicNodeDefinition(_f.getDeclaration()), m_hash(dev::sha3(_f.getSignature())) +QFunctionDefinition::QFunctionDefinition(dev::solidity::FunctionTypePointer const& _f): QBasicNodeDefinition(_f->getDeclaration()), m_hash(dev::sha3(_f->getCanonicalSignature())) { - FunctionDefinition const* funcDef; - VariableDeclaration const* varDecl; - if ((funcDef = _f.getFunctionDefinition())) - { - std::vector> parameters = funcDef->getParameterList().getParameters(); - for (unsigned i = 0; i < parameters.size(); i++) - m_parameters.append(new QVariableDeclaration(parameters.at(i).get())); - - std::vector> returnParameters = funcDef->getReturnParameters(); - for (unsigned i = 0; i < returnParameters.size(); i++) - m_returnParameters.append(new QVariableDeclaration(returnParameters.at(i).get())); - } - else - { - if (!(varDecl = _f.getVariableDeclaration())) - BOOST_THROW_EXCEPTION(Exception() << errinfo_comment("Malformed FunctionDescription. Should never happen.")); - - // only the return parameter for now. - // TODO: change this for other state variables like mapping and maybe abstract this inside solidity and not here - auto returnParams = _f.getReturnParameters(); - m_returnParameters.append(new QVariableDeclaration(returnParams[0])); - - } + auto paramNames = _f->getParameterNames(); + auto paramTypes = _f->getParameterTypeNames(); + auto returnNames = _f->getReturnParameterNames(); + auto returnTypes = _f->getReturnParameterTypeNames(); + for (unsigned i = 0; i < paramNames.size(); ++i) + m_parameters.append(new QVariableDeclaration(paramNames[i], paramTypes[i])); + + for (unsigned i = 0; i < returnNames.size(); ++i) + m_returnParameters.append(new QVariableDeclaration(returnNames[i], returnTypes[i])); } diff --git a/mix/QFunctionDefinition.h b/mix/QFunctionDefinition.h index 2b7084206..0bbf093b5 100644 --- a/mix/QFunctionDefinition.h +++ b/mix/QFunctionDefinition.h @@ -39,7 +39,7 @@ class QFunctionDefinition: public QBasicNodeDefinition public: QFunctionDefinition() {} - QFunctionDefinition(solidity::FunctionDescription const& _f); + QFunctionDefinition(solidity::FunctionTypePointer const& _f); /// Get all input parameters of this function. QList const& parametersList() const { return m_parameters; } /// Get all input parameters of this function as QML property. diff --git a/mix/QVariableDeclaration.h b/mix/QVariableDeclaration.h index ff4018fa3..f9cc5265f 100644 --- a/mix/QVariableDeclaration.h +++ b/mix/QVariableDeclaration.h @@ -37,7 +37,7 @@ class QVariableDeclaration: public QBasicNodeDefinition public: QVariableDeclaration() {} QVariableDeclaration(solidity::VariableDeclaration const* _v): QBasicNodeDefinition(_v), m_type(QString::fromStdString(_v->getType()->toString())) {} - QVariableDeclaration(solidity::ParamDescription const& _v): QBasicNodeDefinition(_v.getName()), m_type(QString::fromStdString(_v.getType())) {} + QVariableDeclaration(std::string const& _name, std::string const& _type): QBasicNodeDefinition(_name), m_type(QString::fromStdString(_type)) {} QString type() const { return m_type; } private: QString m_type; diff --git a/test/SolidityNameAndTypeResolution.cpp b/test/SolidityNameAndTypeResolution.cpp index 5ae854bc0..26406e1ff 100644 --- a/test/SolidityNameAndTypeResolution.cpp +++ b/test/SolidityNameAndTypeResolution.cpp @@ -93,8 +93,8 @@ static ContractDefinition const* retrieveContract(ASTPointer _source return NULL; } -static FunctionDescription const& retrieveFunctionBySignature(ContractDefinition const* _contract, - std::string const& _signature) +static std::shared_ptr const& retrieveFunctionBySignature(ContractDefinition const* _contract, + std::string const& _signature) { FixedHash<4> hash(dev::sha3(_signature)); return _contract->getInterfaceFunctions()[hash]; @@ -643,11 +643,11 @@ BOOST_AUTO_TEST_CASE(state_variable_accessors) ContractDefinition const* contract; BOOST_CHECK_NO_THROW(source = parseTextAndResolveNamesWithChecks(text)); BOOST_REQUIRE((contract = retrieveContract(source, 0)) != nullptr); - FunctionDescription function = retrieveFunctionBySignature(contract, "foo()"); - BOOST_REQUIRE(function.getDeclaration() != nullptr); - auto returnParams = function.getReturnParameters(); - BOOST_CHECK_EQUAL(returnParams.at(0).getType(), "uint256"); - BOOST_CHECK(function.isConstant()); + std::shared_ptr function = retrieveFunctionBySignature(contract, "foo()"); + BOOST_CHECK_MESSAGE(function->getDeclaration() != nullptr, "Could not find the accessor function"); + auto returnParams = function->getReturnParameterTypeNames(); + BOOST_CHECK_EQUAL(returnParams.at(0), "uint256"); + BOOST_CHECK(function->isConstant()); } BOOST_AUTO_TEST_CASE(function_clash_with_state_variable_accessor) @@ -676,8 +676,8 @@ BOOST_AUTO_TEST_CASE(private_state_variable) ContractDefinition const* contract; BOOST_CHECK_NO_THROW(source = parseTextAndResolveNamesWithChecks(text)); BOOST_CHECK((contract = retrieveContract(source, 0)) != nullptr); - FunctionDescription function = retrieveFunctionBySignature(contract, "foo()"); - BOOST_CHECK_MESSAGE(function.getDeclaration() == nullptr, "Accessor function of a private variable should not exist"); + FunctionTypePointer function = retrieveFunctionBySignature(contract, "foo()"); + BOOST_CHECK_MESSAGE(function == nullptr, "Accessor function of a private variable should not exist"); } BOOST_AUTO_TEST_SUITE_END() From 23d92e0d2e2f0d2ef93da9e7b1345a751eed8628 Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Thu, 29 Jan 2015 16:48:39 +0100 Subject: [PATCH 08/21] Removing Function and Param Description - Removing FunctionDescription and ParamDescription. All the data should now be in the FunctionType - Plus using the FunctionTypePointer alias in a few places --- libsolidity/AST.cpp | 102 +----------------------- libsolidity/AST.h | 103 +------------------------ libsolidity/Types.h | 4 +- test/SolidityNameAndTypeResolution.cpp | 6 +- 4 files changed, 10 insertions(+), 205 deletions(-) diff --git a/libsolidity/AST.cpp b/libsolidity/AST.cpp index bd333b91e..74d40da43 100644 --- a/libsolidity/AST.cpp +++ b/libsolidity/AST.cpp @@ -77,11 +77,11 @@ void ContractDefinition::checkTypeRequirements() } } -map, std::shared_ptr> ContractDefinition::getInterfaceFunctions() const +map, FunctionTypePointer> ContractDefinition::getInterfaceFunctions() const { auto exportedFunctionList = getInterfaceFunctionList(); - map, std::shared_ptr> exportedFunctions; + map, FunctionTypePointer> exportedFunctions; for (auto const& it: exportedFunctionList) // exportedFunctions.insert(make_pair(std::get<0>(it), FunctionDescription(std::get<1>(it), std::get<2>(it)))); exportedFunctions.insert(it); @@ -520,103 +520,5 @@ void Literal::checkTypeRequirements() BOOST_THROW_EXCEPTION(createTypeError("Invalid literal value.")); } -std::string const& ParamDescription::getName() const -{ - return m_description.first; -} - -std::string const& ParamDescription::getType() const -{ - return m_description.second; -} - -ASTPointer FunctionDescription::getDocumentation() const -{ - auto function = dynamic_cast(m_description.second); - if (function) - return function->getDocumentation(); - - return ASTPointer(); -} - -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(m_description.second); - if (function) - return function->isDeclaredConst(); - - return true; -} - -vector const FunctionDescription::getParameters() const -{ - auto function = dynamic_cast(m_description.second); - if (function) - { - vector 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 const FunctionDescription::getReturnParameters() const -{ - auto function = dynamic_cast(m_description.second); - if (function) - { - vector paramsDescription; - for (auto const& param: function->getReturnParameters()) - paramsDescription.push_back(ParamDescription(param->getName(), param->getType()->toString())); - - return paramsDescription; - } - - auto vardecl = dynamic_cast(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(m_description.second); -} - -FunctionDefinition const* FunctionDescription::getFunctionDefinition() const -{ - return dynamic_cast(m_description.second); -} - -shared_ptr FunctionDescription::getFunctionTypeShared() const -{ - return m_description.first; -} - - -FunctionType const* FunctionDescription::getFunctionType() const -{ - return m_description.first.get(); -} - - } } diff --git a/libsolidity/AST.h b/libsolidity/AST.h index e0fb14854..9ea6c144b 100755 --- a/libsolidity/AST.h +++ b/libsolidity/AST.h @@ -156,103 +156,6 @@ private: Declaration const* m_scope; }; - - -/// Traits and Helpers (@todo: move to their own header) -/// @{ - -/** - * Generic Parameter description used by @see FunctionDescription to return - * a descripton of its parameters. - */ -struct ParamDescription -{ - ParamDescription(std::string const& _name, std::string const& _type): - m_description(_name, _type){} - - std::string const& getName() const; - std::string const& getType() const; - - std::pair m_description; -}; - - -/** - * Generic function description able to describe both normal functions and - * functions that should be made as accessors to state variables - */ -struct FunctionDescription -{ - FunctionDescription(std::shared_ptr _type, Declaration const* _decl): - m_description(_type, _decl){} - - /// constructor for a constructor's function definition. Used only inside mix. - FunctionDescription(Declaration const* _def): - m_description(nullptr, _def){} - - FunctionDescription(): - m_description(nullptr, nullptr){} - - /// @returns the natspec documentation of the function if existing. Accessor (for now) don't have natspec doc - ASTPointer getDocumentation() const; - /// @returns the canonical signature of the function - std::string getSignature() const; - /// @returns the name of the function, basically that of the declaration - std::string getName() const; - /// @returns whether the function is constant. IF it's an accessor this is always true - bool isConstant() const; - /// @returns the argument parameters of the function - std::vector const getParameters() const; - /// @returns the return parameters of the function - std::vector 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 makeFunctionType() const; - /// @returns a pointer to the function type - FunctionType const* getFunctionType() const; - /// @returns a shared pointer to the function type - std::shared_ptr getFunctionTypeShared() const; - - std::pair, Declaration const*> m_description; -}; - -/** - * 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 const& getLocalVariables() const { return m_localVariables; } - -private: - std::vector m_localVariables; -}; - -/** - * Abstract class that is added to each AST node that can receive documentation. - */ -class Documented -{ -public: - explicit Documented(ASTPointer const& _documentation): m_documentation(_documentation) {} - - /// @return A shared pointer of an ASTString. - /// Can contain a nullptr in which case indicates absence of documentation - ASTPointer const& getDocumentation() const { return m_documentation; } - -protected: - ASTPointer m_documentation; -}; - -/// @} - - /** * 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 @@ -294,7 +197,7 @@ public: /// @returns a map of canonical function signatures to FunctionDefinitions /// as intended for use by the ABI. - std::map, std::shared_ptr> getInterfaceFunctions() const; + std::map, FunctionTypePointer> getInterfaceFunctions() const; /// List of all (direct and indirect) base contracts in order from derived to base, including /// the contract itself. Available after name resolution @@ -307,7 +210,7 @@ public: private: void checkIllegalOverrides() const; - std::vector, std::shared_ptr>> const& getInterfaceFunctionList() const; + std::vector, FunctionTypePointer>> const& getInterfaceFunctionList() const; std::vector> m_baseContracts; std::vector> m_definedStructs; @@ -316,7 +219,7 @@ private: std::vector> m_functionModifiers; std::vector m_linearizedBaseContracts; - mutable std::unique_ptr, std::shared_ptr>>> m_interfaceFunctionList; + mutable std::unique_ptr, FunctionTypePointer>>> m_interfaceFunctionList; }; class InheritanceSpecifier: public ASTNode diff --git a/libsolidity/Types.h b/libsolidity/Types.h index e23939699..b8ad7449e 100644 --- a/libsolidity/Types.h +++ b/libsolidity/Types.h @@ -296,7 +296,7 @@ public: /// Returns the function type of the constructor. Note that the location part of the function type /// is not used, as this type cannot be the type of a variable or expression. - std::shared_ptr const& getConstructorType() const; + FunctionTypePointer const& getConstructorType() const; /// @returns the identifier of the function with the given name or Invalid256 if such a name does /// not exist. @@ -308,7 +308,7 @@ private: /// members. bool m_super; /// Type of the constructor, @see getConstructorType. Lazily initialized. - mutable std::shared_ptr m_constructorType; + mutable FunctionTypePointer m_constructorType; /// List of member types, will be lazy-initialized because of recursive references. mutable std::unique_ptr m_members; }; diff --git a/test/SolidityNameAndTypeResolution.cpp b/test/SolidityNameAndTypeResolution.cpp index 26406e1ff..e330d772c 100644 --- a/test/SolidityNameAndTypeResolution.cpp +++ b/test/SolidityNameAndTypeResolution.cpp @@ -93,8 +93,8 @@ static ContractDefinition const* retrieveContract(ASTPointer _source return NULL; } -static std::shared_ptr const& retrieveFunctionBySignature(ContractDefinition const* _contract, - std::string const& _signature) +static FunctionTypePointer const& retrieveFunctionBySignature(ContractDefinition const* _contract, + std::string const& _signature) { FixedHash<4> hash(dev::sha3(_signature)); return _contract->getInterfaceFunctions()[hash]; @@ -643,7 +643,7 @@ BOOST_AUTO_TEST_CASE(state_variable_accessors) ContractDefinition const* contract; BOOST_CHECK_NO_THROW(source = parseTextAndResolveNamesWithChecks(text)); BOOST_REQUIRE((contract = retrieveContract(source, 0)) != nullptr); - std::shared_ptr function = retrieveFunctionBySignature(contract, "foo()"); + FunctionTypePointer function = retrieveFunctionBySignature(contract, "foo()"); BOOST_CHECK_MESSAGE(function->getDeclaration() != nullptr, "Could not find the accessor function"); auto returnParams = function->getReturnParameterTypeNames(); BOOST_CHECK_EQUAL(returnParams.at(0), "uint256"); From 941a424e7135fee16015b523395dcb2693b56659 Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Thu, 29 Jan 2015 17:28:14 +0100 Subject: [PATCH 09/21] Minor fixes plus a rebase merge fix --- libsolidity/AST.cpp | 5 ++--- libsolidity/AST.h | 31 +++++++++++++++++++++++++++++++ libsolidity/Compiler.cpp | 2 +- libsolidity/InterfaceHandler.cpp | 1 + libsolidity/Types.cpp | 7 +++++-- 5 files changed, 40 insertions(+), 6 deletions(-) diff --git a/libsolidity/AST.cpp b/libsolidity/AST.cpp index 74d40da43..bc6be6008 100644 --- a/libsolidity/AST.cpp +++ b/libsolidity/AST.cpp @@ -83,7 +83,6 @@ map, FunctionTypePointer> ContractDefinition::getInterfaceFunctions map, FunctionTypePointer> exportedFunctions; for (auto const& it: exportedFunctionList) - // exportedFunctions.insert(make_pair(std::get<0>(it), FunctionDescription(std::get<1>(it), std::get<2>(it)))); exportedFunctions.insert(it); solAssert(exportedFunctionList.size() == exportedFunctions.size(), @@ -139,12 +138,12 @@ void ContractDefinition::checkIllegalOverrides() const } } -vector, shared_ptr>> const& ContractDefinition::getInterfaceFunctionList() const +vector, FunctionTypePointer>> const& ContractDefinition::getInterfaceFunctionList() const { if (!m_interfaceFunctionList) { set functionsSeen; - m_interfaceFunctionList.reset(new vector, shared_ptr>>()); + m_interfaceFunctionList.reset(new vector, FunctionTypePointer>>()); for (ContractDefinition const* contract: getLinearizedBaseContracts()) { for (ASTPointer const& f: contract->getDefinedFunctions()) diff --git a/libsolidity/AST.h b/libsolidity/AST.h index 9ea6c144b..6c207290c 100755 --- a/libsolidity/AST.h +++ b/libsolidity/AST.h @@ -156,6 +156,37 @@ private: Declaration const* m_scope; }; +/** + * 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 const& getLocalVariables() const { return m_localVariables; } + +private: + std::vector m_localVariables; +}; + +/** + * Abstract class that is added to each AST node that can receive documentation. + */ +class Documented +{ +public: + explicit Documented(ASTPointer const& _documentation): m_documentation(_documentation) {} + + /// @return A shared pointer of an ASTString. + /// Can contain a nullptr in which case indicates absence of documentation + ASTPointer const& getDocumentation() const { return m_documentation; } + +protected: + ASTPointer m_documentation; +}; + +/// @} + /** * 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 diff --git a/libsolidity/Compiler.cpp b/libsolidity/Compiler.cpp index 33a71bca0..79672ca10 100644 --- a/libsolidity/Compiler.cpp +++ b/libsolidity/Compiler.cpp @@ -160,7 +160,7 @@ void Compiler::appendFunctionSelector(ContractDefinition const& _contract) for (auto const& it: interfaceFunctions) { - FunctionTypePointer functionType = it.second; + FunctionTypePointer const& functionType = it.second; m_context << callDataUnpackerEntryPoints.at(it.first); eth::AssemblyItem returnTag = m_context.pushNewTag(); appendCalldataUnpacker(functionType->getParameterTypes()); diff --git a/libsolidity/InterfaceHandler.cpp b/libsolidity/InterfaceHandler.cpp index dd569946a..4d3ec4d50 100644 --- a/libsolidity/InterfaceHandler.cpp +++ b/libsolidity/InterfaceHandler.cpp @@ -60,6 +60,7 @@ std::unique_ptr InterfaceHandler::getABIInterface(ContractDefinitio return params; }; + solAssert(it.second->getDeclaration(), "All function interface types should contain a declaration"); method["name"] = it.second->getDeclaration()->getName(); method["constant"] = it.second->isConstant(); method["inputs"] = populateParameters(it.second->getParameterNames(), diff --git a/libsolidity/Types.cpp b/libsolidity/Types.cpp index e7765e1b7..38caaf8ff 100644 --- a/libsolidity/Types.cpp +++ b/libsolidity/Types.cpp @@ -591,7 +591,7 @@ u256 StructType::getStorageOffsetOfMember(string const& _name) const FunctionType::FunctionType(FunctionDefinition const& _function, bool _isInternal): m_location(_isInternal ? Location::INTERNAL : Location::EXTERNAL), m_isConstant(_function.isDeclaredConst()), - m_declaration(&_function) + m_declaration(&_function) { TypePointers params; vector paramNames; @@ -641,6 +641,9 @@ bool FunctionType::operator==(Type const& _other) const if (m_location != other.m_location) return false; + if (m_isConstant != other.isConstant()) + return false; + if (m_parameterTypes.size() != other.m_parameterTypes.size() || m_returnParameterTypes.size() != other.m_returnParameterTypes.size()) return false; @@ -763,7 +766,7 @@ vector const FunctionType::getReturnParameterTypeNames() const ASTPointer FunctionType::getDocumentation() const { - auto function = dynamic_cast(m_declaration); + auto function = dynamic_cast(m_declaration); if (function) return function->getDocumentation(); From 93abe457719f3faf150416540956a63537743606 Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Thu, 29 Jan 2015 18:44:14 +0100 Subject: [PATCH 10/21] FunctionType now returns const ref for Declaration --- alethzero/MainWin.cpp | 2 +- libsolidity/Compiler.cpp | 2 +- libsolidity/InterfaceHandler.cpp | 6 ++---- libsolidity/Types.cpp | 4 ++-- libsolidity/Types.h | 7 ++++++- mix/QFunctionDefinition.cpp | 2 +- test/SolidityNameAndTypeResolution.cpp | 2 +- 7 files changed, 14 insertions(+), 11 deletions(-) diff --git a/alethzero/MainWin.cpp b/alethzero/MainWin.cpp index 289603b60..8d889730c 100644 --- a/alethzero/MainWin.cpp +++ b/alethzero/MainWin.cpp @@ -1674,7 +1674,7 @@ string const Main::getFunctionHashes(dev::solidity::CompilerStack const &_compil { ret += it.first.abridged(); ret += " :"; - ret += it.second->getDeclaration()->getName() + "\n"; + ret += it.second->getDeclaration().getName() + "\n"; } return ret; } diff --git a/libsolidity/Compiler.cpp b/libsolidity/Compiler.cpp index 79672ca10..1fa31ce7d 100644 --- a/libsolidity/Compiler.cpp +++ b/libsolidity/Compiler.cpp @@ -164,7 +164,7 @@ void Compiler::appendFunctionSelector(ContractDefinition const& _contract) m_context << callDataUnpackerEntryPoints.at(it.first); eth::AssemblyItem returnTag = m_context.pushNewTag(); appendCalldataUnpacker(functionType->getParameterTypes()); - m_context.appendJumpTo(m_context.getFunctionEntryLabel(*it.second->getDeclaration())); + m_context.appendJumpTo(m_context.getFunctionEntryLabel(it.second->getDeclaration())); m_context << returnTag; appendReturnValuePacker(functionType->getReturnParameterTypes()); } diff --git a/libsolidity/InterfaceHandler.cpp b/libsolidity/InterfaceHandler.cpp index 4d3ec4d50..92cd51562 100644 --- a/libsolidity/InterfaceHandler.cpp +++ b/libsolidity/InterfaceHandler.cpp @@ -59,9 +59,7 @@ std::unique_ptr InterfaceHandler::getABIInterface(ContractDefinitio } return params; }; - - solAssert(it.second->getDeclaration(), "All function interface types should contain a declaration"); - method["name"] = it.second->getDeclaration()->getName(); + method["name"] = it.second->getDeclaration().getName(); method["constant"] = it.second->isConstant(); method["inputs"] = populateParameters(it.second->getParameterNames(), it.second->getParameterTypeNames()); @@ -86,7 +84,7 @@ unique_ptr InterfaceHandler::getABISolidityInterface(ContractDefinition r += (r.size() ? "," : "(") + _paramTypes[i] + " " + _paramNames[i]; return r.size() ? r + ")" : "()"; }; - ret += "function " + it.second->getDeclaration()->getName() + + ret += "function " + it.second->getDeclaration().getName() + populateParameters(it.second->getParameterNames(), it.second->getParameterTypeNames()) + (it.second->isConstant() ? "constant " : ""); if (it.second->getReturnParameterTypes().size()) diff --git a/libsolidity/Types.cpp b/libsolidity/Types.cpp index 38caaf8ff..bebb4be17 100644 --- a/libsolidity/Types.cpp +++ b/libsolidity/Types.cpp @@ -499,7 +499,7 @@ MemberList const& ContractType::getMembers() const } else for (auto const& it: m_contract.getInterfaceFunctions()) - members[it.second->getDeclaration()->getName()] = it.second; + members[it.second->getDeclaration().getName()] = it.second; m_members.reset(new MemberList(members)); } return *m_members; @@ -522,7 +522,7 @@ u256 ContractType::getFunctionIdentifier(string const& _functionName) const { auto interfaceFunctions = m_contract.getInterfaceFunctions(); for (auto const& it: m_contract.getInterfaceFunctions()) - if (it.second->getDeclaration()->getName() == _functionName) + if (it.second->getDeclaration().getName() == _functionName) return FixedHash<4>::Arith(it.first); return Invalid256; diff --git a/libsolidity/Types.h b/libsolidity/Types.h index b8ad7449e..4b4d17d0a 100644 --- a/libsolidity/Types.h +++ b/libsolidity/Types.h @@ -390,7 +390,12 @@ public: /// 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 { return m_declaration; } + 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 diff --git a/mix/QFunctionDefinition.cpp b/mix/QFunctionDefinition.cpp index 3aaec2cb3..20dbe070b 100644 --- a/mix/QFunctionDefinition.cpp +++ b/mix/QFunctionDefinition.cpp @@ -28,7 +28,7 @@ using namespace dev::solidity; using namespace dev::mix; -QFunctionDefinition::QFunctionDefinition(dev::solidity::FunctionTypePointer const& _f): QBasicNodeDefinition(_f->getDeclaration()), m_hash(dev::sha3(_f->getCanonicalSignature())) +QFunctionDefinition::QFunctionDefinition(dev::solidity::FunctionTypePointer const& _f): QBasicNodeDefinition(&_f->getDeclaration()), m_hash(dev::sha3(_f->getCanonicalSignature())) { auto paramNames = _f->getParameterNames(); auto paramTypes = _f->getParameterTypeNames(); diff --git a/test/SolidityNameAndTypeResolution.cpp b/test/SolidityNameAndTypeResolution.cpp index e330d772c..07f2d638a 100644 --- a/test/SolidityNameAndTypeResolution.cpp +++ b/test/SolidityNameAndTypeResolution.cpp @@ -644,7 +644,7 @@ BOOST_AUTO_TEST_CASE(state_variable_accessors) BOOST_CHECK_NO_THROW(source = parseTextAndResolveNamesWithChecks(text)); BOOST_REQUIRE((contract = retrieveContract(source, 0)) != nullptr); FunctionTypePointer function = retrieveFunctionBySignature(contract, "foo()"); - BOOST_CHECK_MESSAGE(function->getDeclaration() != nullptr, "Could not find the accessor function"); + BOOST_REQUIRE(function->hasDeclaration()); auto returnParams = function->getReturnParameterTypeNames(); BOOST_CHECK_EQUAL(returnParams.at(0), "uint256"); BOOST_CHECK(function->isConstant()); From d3e2d2adf3d1e73be785c738bfefd32e76481834 Mon Sep 17 00:00:00 2001 From: Christian Date: Thu, 29 Jan 2015 14:35:28 +0100 Subject: [PATCH 11/21] Parsing of events. --- libsolidity/AST.cpp | 14 ++++++ libsolidity/AST.h | 48 ++++++++++++++++++--- libsolidity/ASTForward.h | 1 + libsolidity/ASTPrinter.cpp | 12 ++++++ libsolidity/ASTPrinter.h | 2 + libsolidity/ASTVisitor.h | 4 ++ libsolidity/AST_accept.h | 20 ++++++++- libsolidity/NameAndTypeResolver.cpp | 8 ++++ libsolidity/NameAndTypeResolver.h | 1 + libsolidity/Parser.cpp | 59 ++++++++++++++++++++------ libsolidity/Parser.h | 13 +++++- libsolidity/Token.h | 2 + libsolidity/Types.cpp | 16 +++++++ libsolidity/Types.h | 4 +- test/SolidityNameAndTypeResolution.cpp | 43 +++++++++++++++++++ test/SolidityParser.cpp | 27 ++++++++++++ 16 files changed, 250 insertions(+), 24 deletions(-) diff --git a/libsolidity/AST.cpp b/libsolidity/AST.cpp index bc6be6008..82d58d2a6 100644 --- a/libsolidity/AST.cpp +++ b/libsolidity/AST.cpp @@ -271,6 +271,20 @@ void ModifierInvocation::checkTypeRequirements() BOOST_THROW_EXCEPTION(createTypeError("Invalid type for argument in modifier invocation.")); } +void EventDefinition::checkTypeRequirements() +{ + int numIndexed = 0; + for (ASTPointer const& var: getParameters()) + { + if (var->isIndexed()) + numIndexed++; + if (!var->getType()->canLiveOutsideStorage()) + BOOST_THROW_EXCEPTION(var->createTypeError("Type is required to live outside storage.")); + } + if (numIndexed > 3) + BOOST_THROW_EXCEPTION(createTypeError("More than 3 indexed arguments for event.")); +} + void Block::checkTypeRequirements() { for (shared_ptr const& statement: m_statements) diff --git a/libsolidity/AST.h b/libsolidity/AST.h index 6c207290c..f0d6cb331 100755 --- a/libsolidity/AST.h +++ b/libsolidity/AST.h @@ -202,13 +202,15 @@ public: std::vector> const& _definedStructs, std::vector> const& _stateVariables, std::vector> const& _definedFunctions, - std::vector> const& _functionModifiers): + std::vector> const& _functionModifiers, + std::vector> const& _events): Declaration(_location, _name), Documented(_documentation), m_baseContracts(_baseContracts), m_definedStructs(_definedStructs), m_stateVariables(_stateVariables), m_definedFunctions(_definedFunctions), - m_functionModifiers(_functionModifiers) + m_functionModifiers(_functionModifiers), + m_events(_events) {} virtual void accept(ASTVisitor& _visitor) override; @@ -219,6 +221,7 @@ public: std::vector> const& getStateVariables() const { return m_stateVariables; } std::vector> const& getFunctionModifiers() const { return m_functionModifiers; } std::vector> const& getDefinedFunctions() const { return m_definedFunctions; } + std::vector> const& getEvents() const { return m_events; } virtual TypePointer getType(ContractDefinition const* m_currentContract) const override; @@ -248,6 +251,7 @@ private: std::vector> m_stateVariables; std::vector> m_definedFunctions; std::vector> m_functionModifiers; + std::vector> m_events; std::vector m_linearizedBaseContracts; mutable std::unique_ptr, FunctionTypePointer>>> m_interfaceFunctionList; @@ -380,8 +384,10 @@ class VariableDeclaration: public Declaration { public: VariableDeclaration(Location const& _location, ASTPointer const& _type, - ASTPointer const& _name, bool _isPublic, bool _isStateVar = false): - Declaration(_location, _name), m_typeName(_type), m_isPublic(_isPublic), m_isStateVariable(_isStateVar) {} + ASTPointer const& _name, bool _isPublic, bool _isStateVar = false, + bool _isIndexed = false): + Declaration(_location, _name), m_typeName(_type), + m_isPublic(_isPublic), m_isStateVariable(_isStateVar), m_isIndexed(_isIndexed) {} virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTConstVisitor& _visitor) const override; @@ -396,12 +402,14 @@ public: bool isLocalVariable() const { return !!dynamic_cast(getScope()); } bool isPublic() const { return m_isPublic; } bool isStateVariable() const { return m_isStateVariable; } + bool isIndexed() const { return m_isIndexed; } private: ASTPointer m_typeName; ///< can be empty ("var") bool m_isPublic; ///< Whether there is an accessor for it or not bool m_isStateVariable; ///< Whether or not this is a contract state variable + bool m_isIndexed; ///< Whether this is an indexed variable (used by events). std::shared_ptr m_type; ///< derived type, initially empty }; @@ -429,7 +437,6 @@ public: virtual TypePointer getType(ContractDefinition const* = nullptr) const override; - void checkTypeRequirements(); private: @@ -460,6 +467,37 @@ private: std::vector> m_arguments; }; +/** + * Definition of a (loggable) event. + */ +class EventDefinition: public Declaration, public Documented +{ +public: + EventDefinition(Location const& _location, + ASTPointer const& _name, + ASTPointer const& _documentation, + ASTPointer 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> 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(*this); + } + + void checkTypeRequirements(); + +private: + ASTPointer m_parameters; + ASTPointer m_body; +}; + /** * Pseudo AST node that is used as declaration for "this", "msg", "tx", "block" and the global * functions when such an identifier is encountered. Will never have a valid location in the source code. diff --git a/libsolidity/ASTForward.h b/libsolidity/ASTForward.h index aa5cd49cd..22015f26b 100644 --- a/libsolidity/ASTForward.h +++ b/libsolidity/ASTForward.h @@ -45,6 +45,7 @@ class FunctionDefinition; class VariableDeclaration; class ModifierDefinition; class ModifierInvocation; +class EventDefinition; class MagicVariableDeclaration; class TypeName; class ElementaryTypeName; diff --git a/libsolidity/ASTPrinter.cpp b/libsolidity/ASTPrinter.cpp index 05b24c63a..949740e89 100644 --- a/libsolidity/ASTPrinter.cpp +++ b/libsolidity/ASTPrinter.cpp @@ -108,6 +108,13 @@ bool ASTPrinter::visit(ModifierInvocation const& _node) return goDeeper(); } +bool ASTPrinter::visit(EventDefinition const& _node) +{ + writeLine("EventDefinition \"" + _node.getName() + "\""); + printSourcePart(_node); + return goDeeper(); +} + bool ASTPrinter::visit(TypeName const& _node) { writeLine("TypeName"); @@ -365,6 +372,11 @@ void ASTPrinter::endVisit(ModifierInvocation const&) m_indentation--; } +void ASTPrinter::endVisit(EventDefinition const&) +{ + m_indentation--; +} + void ASTPrinter::endVisit(TypeName const&) { m_indentation--; diff --git a/libsolidity/ASTPrinter.h b/libsolidity/ASTPrinter.h index 77025b2d0..ebc163e31 100644 --- a/libsolidity/ASTPrinter.h +++ b/libsolidity/ASTPrinter.h @@ -51,6 +51,7 @@ public: bool visit(VariableDeclaration const& _node) override; bool visit(ModifierDefinition const& _node) override; bool visit(ModifierInvocation const& _node) override; + bool visit(EventDefinition const& _node) override; bool visit(TypeName const& _node) override; bool visit(ElementaryTypeName const& _node) override; bool visit(UserDefinedTypeName const& _node) override; @@ -89,6 +90,7 @@ public: void endVisit(VariableDeclaration const&) override; void endVisit(ModifierDefinition const&) override; void endVisit(ModifierInvocation const&) override; + void endVisit(EventDefinition const&) override; void endVisit(TypeName const&) override; void endVisit(ElementaryTypeName const&) override; void endVisit(UserDefinedTypeName const&) override; diff --git a/libsolidity/ASTVisitor.h b/libsolidity/ASTVisitor.h index 53fbd1918..294902778 100644 --- a/libsolidity/ASTVisitor.h +++ b/libsolidity/ASTVisitor.h @@ -52,6 +52,7 @@ public: virtual bool visit(VariableDeclaration&) { return true; } virtual bool visit(ModifierDefinition&) { return true; } virtual bool visit(ModifierInvocation&) { return true; } + virtual bool visit(EventDefinition&) { return true; } virtual bool visit(TypeName&) { return true; } virtual bool visit(ElementaryTypeName&) { return true; } virtual bool visit(UserDefinedTypeName&) { return true; } @@ -92,6 +93,7 @@ public: virtual void endVisit(VariableDeclaration&) { } virtual void endVisit(ModifierDefinition&) { } virtual void endVisit(ModifierInvocation&) { } + virtual void endVisit(EventDefinition&) { } virtual void endVisit(TypeName&) { } virtual void endVisit(ElementaryTypeName&) { } virtual void endVisit(UserDefinedTypeName&) { } @@ -136,6 +138,7 @@ public: virtual bool visit(VariableDeclaration const&) { return true; } virtual bool visit(ModifierDefinition const&) { return true; } virtual bool visit(ModifierInvocation const&) { return true; } + virtual bool visit(EventDefinition const&) { return true; } virtual bool visit(TypeName const&) { return true; } virtual bool visit(ElementaryTypeName const&) { return true; } virtual bool visit(UserDefinedTypeName const&) { return true; } @@ -176,6 +179,7 @@ public: virtual void endVisit(VariableDeclaration const&) { } virtual void endVisit(ModifierDefinition const&) { } virtual void endVisit(ModifierInvocation const&) { } + virtual void endVisit(EventDefinition const&) { } virtual void endVisit(TypeName const&) { } virtual void endVisit(ElementaryTypeName const&) { } virtual void endVisit(UserDefinedTypeName const&) { } diff --git a/libsolidity/AST_accept.h b/libsolidity/AST_accept.h index 481b150bd..38108cd72 100644 --- a/libsolidity/AST_accept.h +++ b/libsolidity/AST_accept.h @@ -64,8 +64,9 @@ void ContractDefinition::accept(ASTVisitor& _visitor) listAccept(m_baseContracts, _visitor); listAccept(m_definedStructs, _visitor); listAccept(m_stateVariables, _visitor); - listAccept(m_definedFunctions, _visitor); + listAccept(m_events, _visitor); listAccept(m_functionModifiers, _visitor); + listAccept(m_definedFunctions, _visitor); } _visitor.endVisit(*this); } @@ -77,8 +78,9 @@ void ContractDefinition::accept(ASTConstVisitor& _visitor) const listAccept(m_baseContracts, _visitor); listAccept(m_definedStructs, _visitor); listAccept(m_stateVariables, _visitor); - listAccept(m_definedFunctions, _visitor); + listAccept(m_events, _visitor); listAccept(m_functionModifiers, _visitor); + listAccept(m_definedFunctions, _visitor); } _visitor.endVisit(*this); } @@ -219,6 +221,20 @@ void ModifierInvocation::accept(ASTConstVisitor& _visitor) const _visitor.endVisit(*this); } +void EventDefinition::accept(ASTVisitor& _visitor) +{ + if (_visitor.visit(*this)) + m_parameters->accept(_visitor); + _visitor.endVisit(*this); +} + +void EventDefinition::accept(ASTConstVisitor& _visitor) const +{ + if (_visitor.visit(*this)) + m_parameters->accept(_visitor); + _visitor.endVisit(*this); +} + void TypeName::accept(ASTVisitor& _visitor) { _visitor.visit(*this); diff --git a/libsolidity/NameAndTypeResolver.cpp b/libsolidity/NameAndTypeResolver.cpp index 43201fe12..75df637c4 100644 --- a/libsolidity/NameAndTypeResolver.cpp +++ b/libsolidity/NameAndTypeResolver.cpp @@ -60,6 +60,8 @@ void NameAndTypeResolver::resolveNamesAndTypes(ContractDefinition& _contract) ReferencesResolver resolver(*structDef, *this, &_contract, nullptr); for (ASTPointer const& variable: _contract.getStateVariables()) ReferencesResolver resolver(*variable, *this, &_contract, nullptr); + for (ASTPointer const& event: _contract.getEvents()) + ReferencesResolver resolver(*event, *this, &_contract, nullptr); for (ASTPointer const& modifier: _contract.getFunctionModifiers()) { m_currentScope = &m_scopes[modifier.get()]; @@ -259,6 +261,12 @@ bool DeclarationRegistrationHelper::visit(VariableDeclaration& _declaration) return true; } +bool DeclarationRegistrationHelper::visit(EventDefinition& _event) +{ + registerDeclaration(_event, false); + return true; +} + void DeclarationRegistrationHelper::enterNewSubScope(Declaration const& _declaration) { map::iterator iter; diff --git a/libsolidity/NameAndTypeResolver.h b/libsolidity/NameAndTypeResolver.h index ba327a59e..4555491f4 100644 --- a/libsolidity/NameAndTypeResolver.h +++ b/libsolidity/NameAndTypeResolver.h @@ -104,6 +104,7 @@ private: void endVisit(ModifierDefinition& _modifier); void endVisit(VariableDefinition& _variableDefinition); bool visit(VariableDeclaration& _declaration); + bool visit(EventDefinition& _event); void enterNewSubScope(Declaration const& _declaration); void closeCurrentScope(); diff --git a/libsolidity/Parser.cpp b/libsolidity/Parser.cpp index 5cfc8f462..48cca6126 100644 --- a/libsolidity/Parser.cpp +++ b/libsolidity/Parser.cpp @@ -122,6 +122,7 @@ ASTPointer Parser::parseContractDefinition() vector> stateVariables; vector> functions; vector> modifiers; + vector> events; if (m_scanner->getCurrentToken() == Token::IS) do { @@ -149,19 +150,23 @@ ASTPointer Parser::parseContractDefinition() else if (currentToken == Token::IDENTIFIER || currentToken == Token::MAPPING || Token::isElementaryTypeName(currentToken)) { - bool const allowVar = false; - stateVariables.push_back(parseVariableDeclaration(allowVar, visibilityIsPublic, true)); + VarDeclParserOptions options; + options.isPublic = visibilityIsPublic; + options.isStateVariable = true; + stateVariables.push_back(parseVariableDeclaration(options)); expectToken(Token::SEMICOLON); } else if (currentToken == Token::MODIFIER) modifiers.push_back(parseModifierDefinition()); + else if (currentToken == Token::EVENT) + events.push_back(parseEventDefinition()); else BOOST_THROW_EXCEPTION(createParserError("Function, variable, struct or modifier declaration expected.")); } nodeFactory.markEndPosition(); expectToken(Token::RBRACE); return nodeFactory.createNode(name, docString, baseContracts, structs, - stateVariables, functions, modifiers); + stateVariables, functions, modifiers, events); } ASTPointer Parser::parseInheritanceSpecifier() @@ -236,8 +241,7 @@ ASTPointer Parser::parseStructDefinition() expectToken(Token::LBRACE); while (m_scanner->getCurrentToken() != Token::RBRACE) { - bool const allowVar = false; - members.push_back(parseVariableDeclaration(allowVar)); + members.push_back(parseVariableDeclaration()); expectToken(Token::SEMICOLON); } nodeFactory.markEndPosition(); @@ -245,12 +249,20 @@ ASTPointer Parser::parseStructDefinition() return nodeFactory.createNode(name, members); } -ASTPointer Parser::parseVariableDeclaration(bool _allowVar, bool _isPublic, bool _isStateVariable) +ASTPointer Parser::parseVariableDeclaration(VarDeclParserOptions const& _options) { ASTNodeFactory nodeFactory(*this); - ASTPointer type = parseTypeName(_allowVar); + ASTPointer type = parseTypeName(_options.allowVar); + bool isIndexed = false; + if (_options.allowIndexed && m_scanner->getCurrentToken() == Token::INDEXED) + { + isIndexed = true; + m_scanner->next(); + } nodeFactory.markEndPosition(); - return nodeFactory.createNode(type, expectIdentifierToken(), _isPublic, _isStateVariable); + return nodeFactory.createNode(type, expectIdentifierToken(), + _options.isPublic, _options.isStateVariable, + isIndexed); } ASTPointer Parser::parseModifierDefinition() @@ -280,6 +292,23 @@ ASTPointer Parser::parseModifierDefinition() return nodeFactory.createNode(name, docstring, parameters, block); } +ASTPointer Parser::parseEventDefinition() +{ + ASTNodeFactory nodeFactory(*this); + ASTPointer docstring; + if (m_scanner->getCurrentCommentLiteral() != "") + docstring = make_shared(m_scanner->getCurrentCommentLiteral()); + + expectToken(Token::EVENT); + ASTPointer name(expectIdentifierToken()); + ASTPointer parameters; + if (m_scanner->getCurrentToken() == Token::LPAREN) + parameters = parseParameterList(true, true); + nodeFactory.markEndPosition(); + expectToken(Token::SEMICOLON); + return nodeFactory.createNode(name, docstring, parameters); +} + ASTPointer Parser::parseModifierInvocation() { ASTNodeFactory nodeFactory(*this); @@ -352,19 +381,20 @@ ASTPointer Parser::parseMapping() return nodeFactory.createNode(keyType, valueType); } -ASTPointer Parser::parseParameterList(bool _allowEmpty) +ASTPointer Parser::parseParameterList(bool _allowEmpty, bool _allowIndexed) { ASTNodeFactory nodeFactory(*this); vector> parameters; + VarDeclParserOptions options; + options.allowIndexed = _allowIndexed; expectToken(Token::LPAREN); if (!_allowEmpty || m_scanner->getCurrentToken() != Token::RPAREN) { - bool const allowVar = false; - parameters.push_back(parseVariableDeclaration(allowVar)); + parameters.push_back(parseVariableDeclaration(options)); while (m_scanner->getCurrentToken() != Token::RPAREN) { expectToken(Token::COMMA); - parameters.push_back(parseVariableDeclaration(allowVar)); + parameters.push_back(parseVariableDeclaration(options)); } } nodeFactory.markEndPosition(); @@ -506,8 +536,9 @@ ASTPointer Parser::parseVarDefOrExprStmt() ASTPointer Parser::parseVariableDefinition() { ASTNodeFactory nodeFactory(*this); - bool const allowVar = true; - ASTPointer variable = parseVariableDeclaration(allowVar); + VarDeclParserOptions options; + options.allowVar = true; + ASTPointer variable = parseVariableDeclaration(options); ASTPointer value; if (m_scanner->getCurrentToken() == Token::ASSIGN) { diff --git a/libsolidity/Parser.h b/libsolidity/Parser.h index d3bff67e5..69478c81e 100644 --- a/libsolidity/Parser.h +++ b/libsolidity/Parser.h @@ -45,6 +45,14 @@ private: /// End position of the current token int getEndPosition() const; + struct VarDeclParserOptions { + VarDeclParserOptions() {} + bool allowVar = false; + bool isPublic = false; + bool isStateVariable = false; + bool allowIndexed = false; + }; + ///@{ ///@name Parsing functions for the AST nodes ASTPointer parseImportDirective(); @@ -52,13 +60,14 @@ private: ASTPointer parseInheritanceSpecifier(); ASTPointer parseFunctionDefinition(bool _isPublic, ASTString const* _contractName); ASTPointer parseStructDefinition(); - ASTPointer parseVariableDeclaration(bool _allowVar, bool _isPublic = false, bool _isStateVar = false); + ASTPointer parseVariableDeclaration(VarDeclParserOptions const& _options = VarDeclParserOptions()); ASTPointer parseModifierDefinition(); + ASTPointer parseEventDefinition(); ASTPointer parseModifierInvocation(); ASTPointer parseIdentifier(); ASTPointer parseTypeName(bool _allowVar); ASTPointer parseMapping(); - ASTPointer parseParameterList(bool _allowEmpty = true); + ASTPointer parseParameterList(bool _allowEmpty = true, bool _allowIndexed = false); ASTPointer parseBlock(); ASTPointer parseStatement(); ASTPointer parseIfStatement(); diff --git a/libsolidity/Token.h b/libsolidity/Token.h index 55ec8e8e0..ed42f90cc 100644 --- a/libsolidity/Token.h +++ b/libsolidity/Token.h @@ -153,7 +153,9 @@ namespace solidity K(DEFAULT, "default", 0) \ K(DO, "do", 0) \ K(ELSE, "else", 0) \ + K(EVENT, "event", 0) \ K(IS, "is", 0) \ + K(INDEXED, "indexed", 0) \ K(FOR, "for", 0) \ K(FUNCTION, "function", 0) \ K(IF, "if", 0) \ diff --git a/libsolidity/Types.cpp b/libsolidity/Types.cpp index bebb4be17..0842e40ba 100644 --- a/libsolidity/Types.cpp +++ b/libsolidity/Types.cpp @@ -633,6 +633,22 @@ FunctionType::FunctionType(VariableDeclaration const& _varDecl): swap(retParamNames, m_returnParameterNames); } +FunctionType::FunctionType(const EventDefinition& _event): + m_location(Location::EVENT), m_declaration(&_event) +{ + TypePointers params; + vector paramNames; + params.reserve(_event.getParameters().size()); + paramNames.reserve(_event.getParameters().size()); + for (ASTPointer const& var: _event.getParameters()) + { + paramNames.push_back(var->getName()); + params.push_back(var->getType()); + } + swap(params, m_parameterTypes); + swap(paramNames, m_parameterNames); +} + bool FunctionType::operator==(Type const& _other) const { if (_other.getCategory() != getCategory()) diff --git a/libsolidity/Types.h b/libsolidity/Types.h index 4b4d17d0a..1f4d27a25 100644 --- a/libsolidity/Types.h +++ b/libsolidity/Types.h @@ -350,16 +350,18 @@ public: /// INTERNAL: jump tag, EXTERNAL: contract address + function identifier, /// BARE: contract address (non-abi contract call) /// OTHERS: special virtual function, nothing on the stack + /// @todo This documentation is outdated, and Location should rather be named "Type" enum class Location { INTERNAL, EXTERNAL, CREATION, SEND, SHA3, SUICIDE, ECRECOVER, SHA256, RIPEMD160, - LOG0, LOG1, LOG2, LOG3, LOG4, + LOG0, LOG1, LOG2, LOG3, LOG4, EVENT, SET_GAS, SET_VALUE, BLOCKHASH, BARE }; virtual Category getCategory() const override { return Category::FUNCTION; } explicit FunctionType(FunctionDefinition const& _function, bool _isInternal = true); explicit FunctionType(VariableDeclaration const& _varDecl); + explicit FunctionType(EventDefinition const& _event); FunctionType(strings const& _parameterTypes, strings const& _returnParameterTypes, Location _location = Location::INTERNAL): FunctionType(parseElementaryTypeVector(_parameterTypes), parseElementaryTypeVector(_returnParameterTypes), diff --git a/test/SolidityNameAndTypeResolution.cpp b/test/SolidityNameAndTypeResolution.cpp index 07f2d638a..88f761245 100644 --- a/test/SolidityNameAndTypeResolution.cpp +++ b/test/SolidityNameAndTypeResolution.cpp @@ -680,6 +680,49 @@ BOOST_AUTO_TEST_CASE(private_state_variable) BOOST_CHECK_MESSAGE(function == nullptr, "Accessor function of a private variable should not exist"); } +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() } diff --git a/test/SolidityParser.cpp b/test/SolidityParser.cpp index 7bfb4c0c8..745df98d4 100644 --- a/test/SolidityParser.cpp +++ b/test/SolidityParser.cpp @@ -586,6 +586,33 @@ BOOST_AUTO_TEST_CASE(modifier_invocation) 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() } From f57dc818896b438aefe8ac90fcf9b740dad31563 Mon Sep 17 00:00:00 2001 From: Christian Date: Thu, 29 Jan 2015 16:42:59 +0100 Subject: [PATCH 12/21] Code generation for events. --- libsolidity/AST.h | 1 - libsolidity/ExpressionCompiler.cpp | 87 ++++++++++++++++++++---------- libsolidity/ExpressionCompiler.h | 9 +++- test/SolidityEndToEndTest.cpp | 51 ++++++++++++++++++ test/solidityExecutionFramework.h | 1 - 5 files changed, 117 insertions(+), 32 deletions(-) diff --git a/libsolidity/AST.h b/libsolidity/AST.h index f0d6cb331..fb046f199 100755 --- a/libsolidity/AST.h +++ b/libsolidity/AST.h @@ -404,7 +404,6 @@ public: bool isStateVariable() const { return m_isStateVariable; } bool isIndexed() const { return m_isIndexed; } - private: ASTPointer m_typeName; ///< can be empty ("var") bool m_isPublic; ///< Whether there is an accessor for it or not diff --git a/libsolidity/ExpressionCompiler.cpp b/libsolidity/ExpressionCompiler.cpp index 5d44c86f3..2b2990117 100644 --- a/libsolidity/ExpressionCompiler.cpp +++ b/libsolidity/ExpressionCompiler.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -304,10 +305,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) m_context << eth::Instruction::SUICIDE; break; case Location::SHA3: - arguments.front()->accept(*this); - appendTypeConversion(*arguments.front()->getType(), *function.getParameterTypes().front(), true); - // @todo move this once we actually use memory - CompilerUtils(m_context).storeInMemory(0); + appendExpressionCopyToMemory(*function.getParameterTypes().front(), *arguments.front()); m_context << u256(32) << u256(0) << eth::Instruction::SHA3; break; case Location::LOG0: @@ -317,14 +315,41 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) case Location::LOG4: { unsigned logNumber = int(function.getLocation()) - int(Location::LOG0); - for (int arg = logNumber; arg >= 0; --arg) + for (unsigned arg = logNumber; arg > 0; --arg) { arguments[arg]->accept(*this); appendTypeConversion(*arguments[arg]->getType(), *function.getParameterTypes()[arg], true); } - // @todo move this once we actually use memory - CompilerUtils(m_context).storeInMemory(0); - m_context << u256(32) << u256(0) << eth::logInstruction(logNumber); + unsigned length = appendExpressionCopyToMemory(*function.getParameterTypes().front(), + *arguments.front()); + solAssert(length == 32, "Log data should have length 32."); + m_context << u256(length) << u256(0) << eth::logInstruction(logNumber); + break; + } + case Location::EVENT: + { + _functionCall.getExpression().accept(*this); + auto const& event = dynamic_cast(function.getDeclaration()); + // Copy all non-indexed arguments to memory (data) + unsigned numIndexed = 0; + unsigned memLength = 0; + for (unsigned arg = 0; arg < arguments.size(); ++arg) + if (!event.getParameters()[arg]->isIndexed()) + memLength += appendExpressionCopyToMemory(*function.getParameterTypes()[arg], + *arguments[arg], memLength); + // All indexed arguments go to the stack + for (unsigned arg = arguments.size(); arg > 0; --arg) + if (event.getParameters()[arg - 1]->isIndexed()) + { + ++numIndexed; + arguments[arg - 1]->accept(*this); + appendTypeConversion(*arguments[arg - 1]->getType(), + *function.getParameterTypes()[arg - 1], true); + } + m_context << u256(h256::Arith(dev::sha3(function.getCanonicalSignature(event.getName())))); + ++numIndexed; + solAssert(numIndexed <= 4, "Too many indexed arguments."); + m_context << u256(memLength) << u256(0) << eth::logInstruction(numIndexed); break; } case Location::BLOCKHASH: @@ -459,14 +484,13 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess) bool ExpressionCompiler::visit(IndexAccess const& _indexAccess) { _indexAccess.getBaseExpression().accept(*this); - _indexAccess.getIndexExpression().accept(*this); - appendTypeConversion(*_indexAccess.getIndexExpression().getType(), - *dynamic_cast(*_indexAccess.getBaseExpression().getType()).getKeyType(), - true); + + TypePointer const& keyType = dynamic_cast(*_indexAccess.getBaseExpression().getType()).getKeyType(); + unsigned length = appendExpressionCopyToMemory(*keyType, _indexAccess.getIndexExpression()); + solAssert(length == 32, "Mapping key length in memory has to be 32."); // @todo move this once we actually use memory - CompilerUtils(m_context).storeInMemory(0); - CompilerUtils(m_context).storeInMemory(32); - m_context << u256(64) << u256(0) << eth::Instruction::SHA3; + length += CompilerUtils(m_context).storeInMemory(length); + m_context << u256(length) << u256(0) << eth::Instruction::SHA3; m_currentLValue = LValue(m_context, LValue::STORAGE, *_indexAccess.getType()); m_currentLValue.retrieveValueIfLValueNotRequested(_indexAccess); @@ -495,6 +519,10 @@ void ExpressionCompiler::endVisit(Identifier const& _identifier) { // no-op } + else if (dynamic_cast(declaration)) + { + // no-op + } else { BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Identifier type not expected in expression context.")); @@ -791,22 +819,25 @@ unsigned ExpressionCompiler::appendArgumentCopyToMemory(TypePointers const& _typ { unsigned length = 0; for (unsigned i = 0; i < _arguments.size(); ++i) - { - _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); - } + length += appendExpressionCopyToMemory(*_types[i], *_arguments[i], _memoryOffset + 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 = _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) { m_currentLValue.fromStateVariable(_varDecl, _varDecl.getType()); diff --git a/libsolidity/ExpressionCompiler.h b/libsolidity/ExpressionCompiler.h index 748cc6c6f..18c3817ac 100644 --- a/libsolidity/ExpressionCompiler.h +++ b/libsolidity/ExpressionCompiler.h @@ -94,8 +94,13 @@ private: bool bare = false); /// Appends code that copies the given arguments to memory (with optional offset). /// @returns the number of bytes copied to memory - unsigned appendArgumentCopyToMemory(TypePointers const& _functionType, std::vector> const& _arguments, - unsigned _memoryOffset = 0); + unsigned appendArgumentCopyToMemory(TypePointers const& _types, + std::vector> const& _arguments, + unsigned _memoryOffset = 0); + /// Appends code that evaluates a single expression and copies it to memory (with optional offset). + /// @returns the number of bytes copied to memory + unsigned appendExpressionCopyToMemory(Type const& _expectedType, Expression const& _expression, + unsigned _memoryOffset = 0); /// Appends code for a State Variable accessor function void appendStateVariableAccessor(VariableDeclaration const& _varDecl); diff --git a/test/SolidityEndToEndTest.cpp b/test/SolidityEndToEndTest.cpp index f29767075..0df2d9146 100644 --- a/test/SolidityEndToEndTest.cpp +++ b/test/SolidityEndToEndTest.cpp @@ -1955,6 +1955,57 @@ BOOST_AUTO_TEST_CASE(super_in_constructor) BOOST_CHECK(callContractFunction("f()") == encodeArgs(1 | 2 | 4 | 8)); } +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, hash(msg.sender), _id); + } else + Deposit(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, _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() } diff --git a/test/solidityExecutionFramework.h b/test/solidityExecutionFramework.h index 7dad9ad44..5a6935365 100644 --- a/test/solidityExecutionFramework.h +++ b/test/solidityExecutionFramework.h @@ -67,7 +67,6 @@ public: bytes const& callContractFunctionWithValue(std::string _sig, u256 const& _value, Args const&... _arguments) { - FixedHash<4> hash(dev::sha3(_sig)); sendMessage(hash.asBytes() + encodeArgs(_arguments...), false, _value); return m_output; From c38dd9765b5f572eb47259a02327c9f0573b98fa Mon Sep 17 00:00:00 2001 From: Christian Date: Thu, 29 Jan 2015 19:32:32 +0100 Subject: [PATCH 13/21] Whitespace and error messages. --- libsolidity/AST.h | 6 +++--- libsolidity/ExpressionCompiler.cpp | 4 ++-- libsolidity/ExpressionCompiler.h | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/libsolidity/AST.h b/libsolidity/AST.h index fb046f199..ab9815f60 100755 --- a/libsolidity/AST.h +++ b/libsolidity/AST.h @@ -473,9 +473,9 @@ class EventDefinition: public Declaration, public Documented { public: EventDefinition(Location const& _location, - ASTPointer const& _name, - ASTPointer const& _documentation, - ASTPointer const& _parameters): + ASTPointer const& _name, + ASTPointer const& _documentation, + ASTPointer const& _parameters): Declaration(_location, _name), Documented(_documentation), m_parameters(_parameters) {} virtual void accept(ASTVisitor& _visitor) override; diff --git a/libsolidity/ExpressionCompiler.cpp b/libsolidity/ExpressionCompiler.cpp index 2b2990117..db71b51d4 100644 --- a/libsolidity/ExpressionCompiler.cpp +++ b/libsolidity/ExpressionCompiler.cpp @@ -322,7 +322,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) } unsigned length = appendExpressionCopyToMemory(*function.getParameterTypes().front(), *arguments.front()); - solAssert(length == 32, "Log data should have length 32."); + solAssert(length == 32, "Log data should be 32 bytes long (for now)."); m_context << u256(length) << u256(0) << eth::logInstruction(logNumber); break; } @@ -487,7 +487,7 @@ bool ExpressionCompiler::visit(IndexAccess const& _indexAccess) TypePointer const& keyType = dynamic_cast(*_indexAccess.getBaseExpression().getType()).getKeyType(); unsigned length = appendExpressionCopyToMemory(*keyType, _indexAccess.getIndexExpression()); - solAssert(length == 32, "Mapping key length in memory has to be 32."); + solAssert(length == 32, "Mapping key has to take 32 bytes in memory (for now)."); // @todo move this once we actually use memory length += CompilerUtils(m_context).storeInMemory(length); m_context << u256(length) << u256(0) << eth::Instruction::SHA3; diff --git a/libsolidity/ExpressionCompiler.h b/libsolidity/ExpressionCompiler.h index 18c3817ac..caecbfe8d 100644 --- a/libsolidity/ExpressionCompiler.h +++ b/libsolidity/ExpressionCompiler.h @@ -95,8 +95,8 @@ private: /// Appends code that copies the given arguments to memory (with optional offset). /// @returns the number of bytes copied to memory unsigned appendArgumentCopyToMemory(TypePointers const& _types, - std::vector> const& _arguments, - unsigned _memoryOffset = 0); + std::vector> const& _arguments, + unsigned _memoryOffset = 0); /// Appends code that evaluates a single expression and copies it to memory (with optional offset). /// @returns the number of bytes copied to memory unsigned appendExpressionCopyToMemory(Type const& _expectedType, Expression const& _expression, From 47f6c9a5def78391c9a748443f392cfc17d71d69 Mon Sep 17 00:00:00 2001 From: Christian Date: Thu, 29 Jan 2015 20:02:23 +0100 Subject: [PATCH 14/21] Padding fixes. --- libsolidity/ExpressionCompiler.cpp | 2 +- test/SolidityEndToEndTest.cpp | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/libsolidity/ExpressionCompiler.cpp b/libsolidity/ExpressionCompiler.cpp index db71b51d4..7d58ea2e2 100644 --- a/libsolidity/ExpressionCompiler.cpp +++ b/libsolidity/ExpressionCompiler.cpp @@ -828,7 +828,7 @@ unsigned ExpressionCompiler::appendExpressionCopyToMemory(Type const& _expectedT { _expression.accept(*this); appendTypeConversion(*_expression.getType(), _expectedType, true); - unsigned const c_numBytes = _expectedType.getCalldataEncodedSize(); + unsigned const c_numBytes = CompilerUtils::getPaddedSize(_expectedType.getCalldataEncodedSize()); if (c_numBytes == 0 || c_numBytes > 32) BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(_expression.getLocation()) diff --git a/test/SolidityEndToEndTest.cpp b/test/SolidityEndToEndTest.cpp index 0df2d9146..b51492e3e 100644 --- a/test/SolidityEndToEndTest.cpp +++ b/test/SolidityEndToEndTest.cpp @@ -1963,9 +1963,9 @@ BOOST_AUTO_TEST_CASE(event) function deposit(hash _id, bool _manually) { if (_manually) { hash s = 0x50cb9fe53daa9737b786ab3646f04d0150dc50ef4e75f59509d83667ad5adb20; - log3(msg.value, s, hash(msg.sender), _id); + log3(msg.value, s, hash32(msg.sender), _id); } else - Deposit(msg.sender, _id, msg.value); + Deposit(hash32(msg.sender), _id, msg.value); } } )"; @@ -1991,7 +1991,7 @@ BOOST_AUTO_TEST_CASE(event_lots_of_data) contract ClientReceipt { event Deposit(address _from, hash _id, uint _value, bool _flag); function deposit(hash _id) { - Deposit(msg.sender, _id, msg.value, true); + Deposit(msg.sender, hash32(_id), msg.value, true); } } )"; From edefb95aaedfad2467f98e07686cccf6566fe603 Mon Sep 17 00:00:00 2001 From: Christian Date: Thu, 29 Jan 2015 22:50:20 +0100 Subject: [PATCH 15/21] Fallback functions. --- libsolidity/AST.cpp | 18 ++++++++-- libsolidity/AST.h | 4 ++- libsolidity/Compiler.cpp | 14 ++++++-- libsolidity/Parser.cpp | 6 +++- libsolidity/Types.cpp | 4 +-- test/SolidityABIJSON.cpp | 9 +++++ test/SolidityCompiler.cpp | 10 +++--- test/SolidityEndToEndTest.cpp | 31 +++++++++++++++++ test/SolidityNameAndTypeResolution.cpp | 48 ++++++++++++++++++++++++++ test/SolidityParser.cpp | 8 +++++ 10 files changed, 138 insertions(+), 14 deletions(-) diff --git a/libsolidity/AST.cpp b/libsolidity/AST.cpp index bc6be6008..20c117c0f 100644 --- a/libsolidity/AST.cpp +++ b/libsolidity/AST.cpp @@ -56,7 +56,12 @@ void ContractDefinition::checkTypeRequirements() FunctionDefinition const* constructor = getConstructor(); if (constructor && !constructor->getReturnParameters().empty()) 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 const& modifier: getFunctionModifiers()) modifier->checkTypeRequirements(); @@ -99,6 +104,15 @@ FunctionDefinition const* ContractDefinition::getConstructor() const return nullptr; } +FunctionDefinition const* ContractDefinition::getFallbackFunction() const +{ + for (ContractDefinition const* contract: getLinearizedBaseContracts()) + for (ASTPointer const& f: contract->getDefinedFunctions()) + if (f->getName().empty()) + return f.get(); + return nullptr; +} + void ContractDefinition::checkIllegalOverrides() const { // TODO unify this at a later point. for this we need to put the constness and the access specifier @@ -147,7 +161,7 @@ vector, FunctionTypePointer>> const& ContractDefinition::getIn for (ContractDefinition const* contract: getLinearizedBaseContracts()) { for (ASTPointer const& f: contract->getDefinedFunctions()) - if (f->isPublic() && !f->isConstructor() && functionsSeen.count(f->getName()) == 0) + if (f->isPublic() && !f->isConstructor() && !f->getName().empty() && functionsSeen.count(f->getName()) == 0) { functionsSeen.insert(f->getName()); FixedHash<4> hash(dev::sha3(f->getCanonicalSignature())); diff --git a/libsolidity/AST.h b/libsolidity/AST.h index 6c207290c..3dc63d333 100755 --- a/libsolidity/AST.h +++ b/libsolidity/AST.h @@ -235,8 +235,10 @@ public: std::vector const& getLinearizedBaseContracts() const { return m_linearizedBaseContracts; } void setLinearizedBaseContracts(std::vector const& _bases) { m_linearizedBaseContracts = _bases; } - /// Returns the constructor or nullptr if no constructor was specified + /// Returns the constructor or nullptr if no constructor was specified. FunctionDefinition const* getConstructor() const; + /// Returns the fallback function or nullptr if no constructor was specified. + FunctionDefinition const* getFallbackFunction() const; private: void checkIllegalOverrides() const; diff --git a/libsolidity/Compiler.cpp b/libsolidity/Compiler.cpp index 1fa31ce7d..3c46d4552 100644 --- a/libsolidity/Compiler.cpp +++ b/libsolidity/Compiler.cpp @@ -146,8 +146,8 @@ void Compiler::appendFunctionSelector(ContractDefinition const& _contract) map, const eth::AssemblyItem> callDataUnpackerEntryPoints; // retrieve the function signature hash from the calldata - m_context << u256(1) << u256(0); - CompilerUtils(m_context).loadFromMemory(0, 4, false, true); + if (!interfaceFunctions.empty()) + CompilerUtils(m_context).loadFromMemory(0, 4, false, true); // stack now is: 1 0 for (auto const& it: interfaceFunctions) @@ -156,7 +156,15 @@ void Compiler::appendFunctionSelector(ContractDefinition const& _contract) m_context << eth::dupInstruction(1) << u256(FixedHash<4>::Arith(it.first)) << eth::Instruction::EQ; m_context.appendConditionalJumpTo(callDataUnpackerEntryPoints.at(it.first)); } - 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) { diff --git a/libsolidity/Parser.cpp b/libsolidity/Parser.cpp index 5cfc8f462..812b3263d 100644 --- a/libsolidity/Parser.cpp +++ b/libsolidity/Parser.cpp @@ -189,7 +189,11 @@ ASTPointer Parser::parseFunctionDefinition(bool _isPublic, A docstring = make_shared(m_scanner->getCurrentCommentLiteral()); expectToken(Token::FUNCTION); - ASTPointer name(expectIdentifierToken()); + ASTPointer name; + if (m_scanner->getCurrentToken() == Token::LPAREN) + name = make_shared(); // anonymous function + else + name = expectIdentifierToken(); ASTPointer parameters(parseParameterList()); bool isDeclaredConst = false; vector> modifiers; diff --git a/libsolidity/Types.cpp b/libsolidity/Types.cpp index bebb4be17..afc1c1dba 100644 --- a/libsolidity/Types.cpp +++ b/libsolidity/Types.cpp @@ -494,7 +494,7 @@ MemberList const& ContractType::getMembers() const { for (ContractDefinition const* base: m_contract.getLinearizedBaseContracts()) for (ASTPointer const& function: base->getDefinedFunctions()) - if (!function->isConstructor()) + if (!function->isConstructor() && !function->getName().empty()) members.insert(make_pair(function->getName(), make_shared(*function, true))); } else @@ -808,7 +808,7 @@ MemberList const& TypeType::getMembers() const // We are accessing the type of a base contract, so add all public and private // functions. Note that this does not add inherited functions on purpose. for (ASTPointer const& f: contract.getDefinedFunctions()) - if (!f->isConstructor()) + if (!f->isConstructor() && !f->getName().empty()) members[f->getName()] = make_shared(*f); } m_members.reset(new MemberList(members)); diff --git a/test/SolidityABIJSON.cpp b/test/SolidityABIJSON.cpp index 4a44ebb84..edafb1686 100644 --- a/test/SolidityABIJSON.cpp +++ b/test/SolidityABIJSON.cpp @@ -273,6 +273,15 @@ BOOST_AUTO_TEST_CASE(const_function) 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() } diff --git a/test/SolidityCompiler.cpp b/test/SolidityCompiler.cpp index 98397af79..17d9a7c07 100644 --- a/test/SolidityCompiler.cpp +++ b/test/SolidityCompiler.cpp @@ -96,7 +96,7 @@ BOOST_AUTO_TEST_CASE(smoke_test) "}\n"; bytes code = compileContract(sourceCode); - unsigned boilerplateSize = 73; + unsigned boilerplateSize = 69; bytes expectation({byte(Instruction::JUMPDEST), byte(Instruction::PUSH1), 0x0, // initialize local variable x 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; }" "}\n"; bytes code = compileContract(sourceCode); - unsigned shift = 60; - unsigned boilerplateSize = 73; + unsigned shift = 56; + unsigned boilerplateSize = 69; bytes expectation({byte(Instruction::JUMPDEST), byte(Instruction::PUSH1), 0x0, byte(Instruction::DUP1), @@ -155,8 +155,8 @@ BOOST_AUTO_TEST_CASE(loops) " function f() { while(true){1;break;2;continue;3;return;4;} }" "}\n"; bytes code = compileContract(sourceCode); - unsigned shift = 60; - unsigned boilerplateSize = 73; + unsigned shift = 56; + unsigned boilerplateSize = 69; bytes expectation({byte(Instruction::JUMPDEST), byte(Instruction::JUMPDEST), byte(Instruction::PUSH1), 0x1, diff --git a/test/SolidityEndToEndTest.cpp b/test/SolidityEndToEndTest.cpp index f29767075..576e0f917 100644 --- a/test/SolidityEndToEndTest.cpp +++ b/test/SolidityEndToEndTest.cpp @@ -1955,6 +1955,37 @@ BOOST_AUTO_TEST_CASE(super_in_constructor) 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_SUITE_END() } diff --git a/test/SolidityNameAndTypeResolution.cpp b/test/SolidityNameAndTypeResolution.cpp index 07f2d638a..d081916cd 100644 --- a/test/SolidityNameAndTypeResolution.cpp +++ b/test/SolidityNameAndTypeResolution.cpp @@ -680,6 +680,54 @@ BOOST_AUTO_TEST_CASE(private_state_variable) 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_SUITE_END() } diff --git a/test/SolidityParser.cpp b/test/SolidityParser.cpp index 7bfb4c0c8..0059ccf70 100644 --- a/test/SolidityParser.cpp +++ b/test/SolidityParser.cpp @@ -586,6 +586,14 @@ BOOST_AUTO_TEST_CASE(modifier_invocation) 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_SUITE_END() } From 5fd79ae914c574239efb15dd96ce775b62fcbf49 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Wed, 28 Jan 2015 15:11:09 -0800 Subject: [PATCH 16/21] First little commit - not workign yet. --- libdevcore/FixedHash.h | 3 +++ libweb3jsonrpc/WebThreeStubServerBase.cpp | 7 ++++--- libwhisper/Common.cpp | 9 +++++++++ libwhisper/Common.h | 4 ++++ libwhisper/Interface.h | 7 +++++-- libwhisper/Message.cpp | 17 ++++++++++++++--- libwhisper/Message.h | 12 ++++++------ 7 files changed, 45 insertions(+), 14 deletions(-) diff --git a/libdevcore/FixedHash.h b/libdevcore/FixedHash.h index 6c42aa501..561f2f405 100644 --- a/libdevcore/FixedHash.h +++ b/libdevcore/FixedHash.h @@ -67,6 +67,9 @@ public: /// Explicitly construct, copying from a byte array. explicit FixedHash(bytes const& _b) { if (_b.size() == N) memcpy(m_data.data(), _b.data(), std::min(_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(_b.size(), N)); } + /// Explicitly construct, copying from a bytes in memory with given pointer. explicit FixedHash(byte const* _bs, ConstructFromPointerType) { memcpy(m_data.data(), _bs, N); } diff --git a/libweb3jsonrpc/WebThreeStubServerBase.cpp b/libweb3jsonrpc/WebThreeStubServerBase.cpp index 0d2c07747..55812f34a 100644 --- a/libweb3jsonrpc/WebThreeStubServerBase.cpp +++ b/libweb3jsonrpc/WebThreeStubServerBase.cpp @@ -173,9 +173,9 @@ static shh::Envelope toSealed(Json::Value const& _json, shh::Message const& _m, return _m.seal(_from, bt, ttl, workToProve); } -static pair toWatch(Json::Value const& _json) +static pair toWatch(Json::Value const& _json) { - shh::BuildTopicMask bt; + shh::BuildTopic bt; Public to; if (_json["to"].isString()) @@ -190,7 +190,7 @@ static pair toWatch(Json::Value const& _json) if (i.isString()) bt.shift(jsToBytes(i.asString())); } - return make_pair(bt.toTopicMask(), to); + return make_pair(bt, to); } static Json::Value toJson(h256 const& _h, shh::Envelope const& _e, shh::Message const& _m) @@ -571,6 +571,7 @@ Json::Value WebThreeStubServerBase::shh_changed(int const& _id) if (!pub || m_ids.count(pub)) for (h256 const& h: face()->checkWatch(_id)) { + face()->watchFilter(_id).topics(); auto e = face()->envelope(h); shh::Message m; if (pub) diff --git a/libwhisper/Common.cpp b/libwhisper/Common.cpp index bbc7ecdf7..a057157d1 100644 --- a/libwhisper/Common.cpp +++ b/libwhisper/Common.cpp @@ -37,6 +37,15 @@ Topic BuildTopic::toTopic() const return ret; } +FullTopic BuildTopic::toFullTopic() const +{ + FullTopic ret; + ret.reserve(m_parts.size()); + for (auto const& h: m_parts) + ret.push_back(h); + return ret; +} + BuildTopic& BuildTopic::shiftBytes(bytes const& _b) { m_parts.push_back(dev::sha3(_b)); diff --git a/libwhisper/Common.h b/libwhisper/Common.h index 5ce7d3b1c..442e71830 100644 --- a/libwhisper/Common.h +++ b/libwhisper/Common.h @@ -62,6 +62,7 @@ enum WhisperPacket using TopicPart = FixedHash<4>; using Topic = std::vector; +using FullTopic = std::vector; class BuildTopic { @@ -75,7 +76,9 @@ public: BuildTopic& shiftRaw(h256 const& _part) { m_parts.push_back(_part); return *this; } operator Topic() const { return toTopic(); } + operator FullTopic() const { return toFullTopic(); } Topic toTopic() const; + FullTopic toFullTopic() const { return m_parts; } protected: BuildTopic& shiftBytes(bytes const& _b); @@ -108,6 +111,7 @@ public: bool matches(Envelope const& _m) const; private: + h256s m_parts; TopicMasks m_topicMasks; }; diff --git a/libwhisper/Interface.h b/libwhisper/Interface.h index 0b7b52cf7..fdac29cba 100644 --- a/libwhisper/Interface.h +++ b/libwhisper/Interface.h @@ -47,8 +47,9 @@ class Watch; struct InstalledFilter { - InstalledFilter(TopicFilter const& _f): filter(_f) {} + InstalledFilter(FullTopic const& _f): full(_f), filter(fullToFilter(_f)) {} + FullTopic full; TopicFilter filter; unsigned refCount = 1; }; @@ -70,7 +71,8 @@ public: virtual void inject(Envelope const& _m, WhisperPeer* _from = nullptr) = 0; unsigned installWatch(TopicMask const& _mask); - virtual unsigned installWatch(TopicFilter const& _filter) = 0; + virtual FullTopic getFilter(unsigned _id) const = 0; + virtual unsigned installWatch(FullTopic const& _filter) = 0; virtual unsigned installWatchOnId(h256 _filterId) = 0; virtual void uninstallWatch(unsigned _watchId) = 0; virtual h256s peekWatch(unsigned _watchId) const = 0; @@ -108,6 +110,7 @@ public: Watch(Interface& _c, TopicFilter const& _tf): m_c(&_c), m_id(_c.installWatch(_tf)) {} ~Watch() { if (m_c) m_c->uninstallWatch(m_id); } + FullTopic fullTopic() const { return m_c ? m_c->fullTopic(m_id) : FullTopic(); } h256s check() { return m_c ? m_c->checkWatch(m_id) : h256s(); } h256s peek() { return m_c ? m_c->peekWatch(m_id) : h256s(); } diff --git a/libwhisper/Message.cpp b/libwhisper/Message.cpp index 07620a7cf..5a10fc323 100644 --- a/libwhisper/Message.cpp +++ b/libwhisper/Message.cpp @@ -26,7 +26,7 @@ using namespace dev; using namespace dev::p2p; using namespace dev::shh; -Message::Message(Envelope const& _e, Secret const& _s) +Message::Message(Envelope const& _e, Secret const& _s, unsigned _topicIndex) { try { @@ -34,7 +34,18 @@ Message::Message(Envelope const& _e, Secret const& _s) if (_s) if (!decrypt(_s, &(_e.data()), b)) return; - if (populate(_s ? b : _e.data())) + else{} + else + { + // public - need to get the key through combining with the topic/topicIndex we know. + if (_e.data().size() < _e.topics().size() * 32) + return; + // get key from decrypted topic key: just xor + if (!decrypt(_s ^ h256(bytesConstRef(&(_e.data())).cropped(32 * _topicIndex, 32)), bytesConstRef(&(_e.data())).cropped(32 * _e.topics().size()), b)) + return; + } + + if (populate(b)) m_to = KeyPair(_s).pub(); } catch (...) // Invalid secret? TODO: replace ... with InvalidSecret @@ -63,7 +74,7 @@ bool Message::populate(bytes const& _data) return true; } -Envelope Message::seal(Secret _from, Topic const& _topic, unsigned _ttl, unsigned _workToProve) const +Envelope Message::seal(Secret _from, FullTopic const& _topic, unsigned _ttl, unsigned _workToProve) const { Envelope ret(time(0) + _ttl, _ttl, _topic); diff --git a/libwhisper/Message.h b/libwhisper/Message.h index 91765d33e..eeb7446eb 100644 --- a/libwhisper/Message.h +++ b/libwhisper/Message.h @@ -64,7 +64,7 @@ public: Topic const& topic() const { return m_topic; } bytes const& data() const { return m_data; } - Message open(Secret const& _s = Secret()) const; + Message open(Secret const& _s = Secret(), unsigned _topicIndex = (unsigned)-1) const; unsigned workProved() const; void proveWork(unsigned _ms); @@ -91,7 +91,7 @@ class Message { public: Message() {} - Message(Envelope const& _e, Secret const& _s = Secret()); + Message(Envelope const& _e, Secret const& _s = Secret(), unsigned _topicIndex = (unsigned)-1); Message(bytes const& _payload): m_payload(_payload) {} Message(bytesConstRef _payload): m_payload(_payload.toBytes()) {} Message(bytes&& _payload) { std::swap(_payload, m_payload); } @@ -108,11 +108,11 @@ public: operator bool() const { return !!m_payload.size() || m_from || m_to; } /// Turn this message into a ditributable Envelope. - Envelope seal(Secret _from, Topic const& _topic, unsigned _workToProve = 50, unsigned _ttl = 50) const; + Envelope seal(Secret _from, FullTopic const& _topic, unsigned _workToProve = 50, unsigned _ttl = 50) const; // Overloads for skipping _from or specifying _to. - Envelope seal(Topic const& _topic, unsigned _ttl = 50, unsigned _workToProve = 50) const { return seal(Secret(), _topic, _workToProve, _ttl); } - Envelope sealTo(Public _to, Topic const& _topic, unsigned _workToProve = 50, unsigned _ttl = 50) { m_to = _to; return seal(Secret(), _topic, _workToProve, _ttl); } - Envelope sealTo(Secret _from, Public _to, Topic const& _topic, unsigned _workToProve = 50, unsigned _ttl = 50) { m_to = _to; return seal(_from, _topic, _workToProve, _ttl); } + Envelope seal(FullTopic const& _topic, unsigned _ttl = 50, unsigned _workToProve = 50) const { return seal(Secret(), _topic, _workToProve, _ttl); } + Envelope sealTo(Public _to, FullTopic const& _topic, unsigned _workToProve = 50, unsigned _ttl = 50) { m_to = _to; return seal(Secret(), _topic, _workToProve, _ttl); } + Envelope sealTo(Secret _from, Public _to, FullTopic const& _topic, unsigned _workToProve = 50, unsigned _ttl = 50) { m_to = _to; return seal(_from, _topic, _workToProve, _ttl); } private: bool populate(bytes const& _data); From c921b25aef2cd1a6fc949befa43d0a25a428bde9 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Wed, 28 Jan 2015 19:03:23 -0800 Subject: [PATCH 17/21] Compiles. --- alethzero/MainWin.cpp | 2 +- libdevcrypto/Common.cpp | 12 +++++++++++ libdevcrypto/Common.h | 8 +++++++- libdevcrypto/CryptoPP.h | 2 +- libweb3jsonrpc/WebThreeStubServerBase.cpp | 9 +++++--- libwhisper/Common.cpp | 9 -------- libwhisper/Common.h | 6 ++++-- libwhisper/Interface.cpp | 3 +-- libwhisper/Interface.h | 20 ++++++++---------- libwhisper/Message.cpp | 25 ++++++++++++++++------- libwhisper/Message.h | 4 +++- libwhisper/WhisperHost.cpp | 7 ++++--- libwhisper/WhisperHost.h | 4 ++-- libwhisper/WhisperPeer.cpp | 2 +- libwhisper/WhisperPeer.h | 4 ++-- 15 files changed, 71 insertions(+), 46 deletions(-) diff --git a/alethzero/MainWin.cpp b/alethzero/MainWin.cpp index c58d6f931..b771d4836 100644 --- a/alethzero/MainWin.cpp +++ b/alethzero/MainWin.cpp @@ -1597,7 +1597,7 @@ void Main::on_destination_currentTextChanged() // updateFee(); } -static shh::Topic topicFromText(QString _s) +static shh::FullTopic topicFromText(QString _s) { shh::BuildTopic ret; while (_s.size()) diff --git a/libdevcrypto/Common.cpp b/libdevcrypto/Common.cpp index 2c13c40bf..0a94662c8 100644 --- a/libdevcrypto/Common.cpp +++ b/libdevcrypto/Common.cpp @@ -80,6 +80,18 @@ bool dev::decrypt(Secret const& _k, bytesConstRef _cipher, bytes& o_plaintext) return true; } +void dev::encryptSym(Secret const& _k, bytesConstRef _plain, bytes& o_cipher) +{ + // TOOD: @alex @subtly do this properly. + encrypt(KeyPair(_k).pub(), _plain, o_cipher); +} + +bool dev::decryptSym(Secret const& _k, bytesConstRef _cipher, bytes& o_plain) +{ + // TODO: @alex @subtly do this properly. + return decrypt(_k, _cipher, o_plain); +} + Public dev::recover(Signature const& _sig, h256 const& _message) { return s_secp256k1.recover(_sig, _message.ref()); diff --git a/libdevcrypto/Common.h b/libdevcrypto/Common.h index 2eea2b83c..e91df2526 100644 --- a/libdevcrypto/Common.h +++ b/libdevcrypto/Common.h @@ -86,7 +86,13 @@ void encrypt(Public const& _k, bytesConstRef _plain, bytes& o_cipher); /// Decrypts cipher using Secret key. bool decrypt(Secret const& _k, bytesConstRef _cipher, bytes& o_plaintext); - + +/// Symmetric encryption. +void encryptSym(Secret const& _k, bytesConstRef _plain, bytes& o_cipher); + +/// Symmetric decryption. +bool decryptSym(Secret const& _k, bytesConstRef _cipher, bytes& o_plaintext); + /// Recovers Public key from signed message hash. Public recover(Signature const& _sig, h256 const& _hash); diff --git a/libdevcrypto/CryptoPP.h b/libdevcrypto/CryptoPP.h index 7ec95c552..fa9d92aa1 100644 --- a/libdevcrypto/CryptoPP.h +++ b/libdevcrypto/CryptoPP.h @@ -62,7 +62,7 @@ using namespace CryptoPP; inline ECP::Point publicToPoint(Public const& _p) { Integer x(_p.data(), 32); Integer y(_p.data() + 32, 32); return std::move(ECP::Point(x,y)); } inline Integer secretToExponent(Secret const& _s) { return std::move(Integer(_s.data(), Secret::size)); } - + /** * CryptoPP secp256k1 algorithms. */ diff --git a/libweb3jsonrpc/WebThreeStubServerBase.cpp b/libweb3jsonrpc/WebThreeStubServerBase.cpp index 55812f34a..bd09ac9c6 100644 --- a/libweb3jsonrpc/WebThreeStubServerBase.cpp +++ b/libweb3jsonrpc/WebThreeStubServerBase.cpp @@ -571,18 +571,21 @@ Json::Value WebThreeStubServerBase::shh_changed(int const& _id) if (!pub || m_ids.count(pub)) for (h256 const& h: face()->checkWatch(_id)) { - face()->watchFilter(_id).topics(); auto e = face()->envelope(h); shh::Message m; if (pub) { cwarn << "Silently decrypting message from identity" << pub.abridged() << ": User validation hook goes here."; - m = e.open(m_ids[pub]); + m = e.open(m_ids[pub], shh::NotPublic); if (!m) continue; } else - m = e.open(); + { + unsigned i = 0; + for (; i < face()->getFilter(_id).size() && !face()->getFilter(_id)[i]; ++i) {} + m = e.open(face()->getFilter(_id)[i], i); + } ret.append(toJson(h, e, m)); } diff --git a/libwhisper/Common.cpp b/libwhisper/Common.cpp index a057157d1..bbc7ecdf7 100644 --- a/libwhisper/Common.cpp +++ b/libwhisper/Common.cpp @@ -37,15 +37,6 @@ Topic BuildTopic::toTopic() const return ret; } -FullTopic BuildTopic::toFullTopic() const -{ - FullTopic ret; - ret.reserve(m_parts.size()); - for (auto const& h: m_parts) - ret.push_back(h); - return ret; -} - BuildTopic& BuildTopic::shiftBytes(bytes const& _b) { m_parts.push_back(dev::sha3(_b)); diff --git a/libwhisper/Common.h b/libwhisper/Common.h index 442e71830..e19c65ea9 100644 --- a/libwhisper/Common.h +++ b/libwhisper/Common.h @@ -62,7 +62,7 @@ enum WhisperPacket using TopicPart = FixedHash<4>; using Topic = std::vector; -using FullTopic = std::vector; +using FullTopic = h256s; class BuildTopic { @@ -93,6 +93,7 @@ class TopicFilter { public: TopicFilter() {} + TopicFilter(FullTopic const& _m) { m_topicMasks.push_back(TopicMask()); for (auto const& h: _m) m_topicMasks.back().push_back(std::make_pair(TopicPart(h), h ? ~TopicPart() : TopicPart())); } TopicFilter(TopicMask const& _m): m_topicMasks(1, _m) {} TopicFilter(TopicMasks const& _m): m_topicMasks(_m) {} TopicFilter(RLP const& _r)//: m_topicMasks(_r.toVector>()) @@ -111,7 +112,6 @@ public: bool matches(Envelope const& _m) const; private: - h256s m_parts; TopicMasks m_topicMasks; }; @@ -127,7 +127,9 @@ public: template BuildTopicMask& operator()(T const& _t) { shift(_t); return *this; } operator TopicMask() const { return toTopicMask(); } + operator FullTopic() const { return toFullTopic(); } TopicMask toTopicMask() const; + FullTopic toFullTopic() const { return m_parts; } }; } diff --git a/libwhisper/Interface.cpp b/libwhisper/Interface.cpp index c00c3ebb2..72bca9785 100644 --- a/libwhisper/Interface.cpp +++ b/libwhisper/Interface.cpp @@ -34,7 +34,6 @@ using namespace dev::shh; #endif #define clogS(X) dev::LogOutputStream(false) << "| " << std::setw(2) << session()->socketId() << "] " -unsigned Interface::installWatch(TopicMask const& _mask) +Interface::~Interface() { - return installWatch(TopicFilter(_mask)); } diff --git a/libwhisper/Interface.h b/libwhisper/Interface.h index fdac29cba..1ee36c756 100644 --- a/libwhisper/Interface.h +++ b/libwhisper/Interface.h @@ -47,7 +47,7 @@ class Watch; struct InstalledFilter { - InstalledFilter(FullTopic const& _f): full(_f), filter(fullToFilter(_f)) {} + InstalledFilter(FullTopic const& _f): full(_f), filter(_f) {} FullTopic full; TopicFilter filter; @@ -66,13 +66,12 @@ struct ClientWatch class Interface { public: - virtual ~Interface() {} + virtual ~Interface(); virtual void inject(Envelope const& _m, WhisperPeer* _from = nullptr) = 0; - unsigned installWatch(TopicMask const& _mask); virtual FullTopic getFilter(unsigned _id) const = 0; - virtual unsigned installWatch(FullTopic const& _filter) = 0; + virtual unsigned installWatch(FullTopic const& _mask) = 0; virtual unsigned installWatchOnId(h256 _filterId) = 0; virtual void uninstallWatch(unsigned _watchId) = 0; virtual h256s peekWatch(unsigned _watchId) const = 0; @@ -81,10 +80,10 @@ public: virtual Envelope envelope(h256 _m) const = 0; - void post(bytes const& _payload, Topic _topic, unsigned _ttl = 50, unsigned _workToProve = 50) { inject(Message(_payload).seal(_topic, _ttl, _workToProve)); } - void post(Public _to, bytes const& _payload, Topic _topic, unsigned _ttl = 50, unsigned _workToProve = 50) { inject(Message(_payload).sealTo(_to, _topic, _ttl, _workToProve)); } - void post(Secret _from, bytes const& _payload, Topic _topic, unsigned _ttl = 50, unsigned _workToProve = 50) { inject(Message(_payload).seal(_from, _topic, _ttl, _workToProve)); } - void post(Secret _from, Public _to, bytes const& _payload, Topic _topic, unsigned _ttl = 50, unsigned _workToProve = 50) { inject(Message(_payload).sealTo(_from, _to, _topic, _ttl, _workToProve)); } + void post(bytes const& _payload, FullTopic _topic, unsigned _ttl = 50, unsigned _workToProve = 50) { inject(Message(_payload).seal(_topic, _ttl, _workToProve)); } + void post(Public _to, bytes const& _payload, FullTopic _topic, unsigned _ttl = 50, unsigned _workToProve = 50) { inject(Message(_payload).sealTo(_to, _topic, _ttl, _workToProve)); } + void post(Secret _from, bytes const& _payload, FullTopic _topic, unsigned _ttl = 50, unsigned _workToProve = 50) { inject(Message(_payload).seal(_from, _topic, _ttl, _workToProve)); } + void post(Secret _from, Public _to, bytes const& _payload, FullTopic _topic, unsigned _ttl = 50, unsigned _workToProve = 50) { inject(Message(_payload).sealTo(_from, _to, _topic, _ttl, _workToProve)); } }; struct WatshhChannel: public dev::LogChannel { static const char* name() { return "shh"; } static const int verbosity = 1; }; @@ -106,11 +105,10 @@ class Watch: public boost::noncopyable public: Watch() {} - Watch(Interface& _c, TopicMask const& _f): m_c(&_c), m_id(_c.installWatch(_f)) {} - Watch(Interface& _c, TopicFilter const& _tf): m_c(&_c), m_id(_c.installWatch(_tf)) {} + Watch(Interface& _c, FullTopic const& _f): m_c(&_c), m_id(_c.installWatch(_f)) {} ~Watch() { if (m_c) m_c->uninstallWatch(m_id); } - FullTopic fullTopic() const { return m_c ? m_c->fullTopic(m_id) : FullTopic(); } + FullTopic fullTopic() const { return m_c ? m_c->getFilter(m_id) : FullTopic(); } h256s check() { return m_c ? m_c->checkWatch(m_id) : h256s(); } h256s peek() { return m_c ? m_c->peekWatch(m_id) : h256s(); } diff --git a/libwhisper/Message.cpp b/libwhisper/Message.cpp index 5a10fc323..ed7faaea8 100644 --- a/libwhisper/Message.cpp +++ b/libwhisper/Message.cpp @@ -35,13 +35,13 @@ Message::Message(Envelope const& _e, Secret const& _s, unsigned _topicIndex) if (!decrypt(_s, &(_e.data()), b)) return; else{} - else + else if (_topicIndex != (unsigned)-1) { // public - need to get the key through combining with the topic/topicIndex we know. if (_e.data().size() < _e.topics().size() * 32) return; // get key from decrypted topic key: just xor - if (!decrypt(_s ^ h256(bytesConstRef(&(_e.data())).cropped(32 * _topicIndex, 32)), bytesConstRef(&(_e.data())).cropped(32 * _e.topics().size()), b)) + if (!decryptSym(_s ^ h256(bytesConstRef(&(_e.data())).cropped(32 * _topicIndex, 32)), bytesConstRef(&(_e.data())).cropped(32 * _e.topics().size()), b)) return; } @@ -74,9 +74,12 @@ bool Message::populate(bytes const& _data) return true; } -Envelope Message::seal(Secret _from, FullTopic 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); + Topic topic; + for (auto const& ft: _fullTopic) + topic.push_back(TopicPart(ft)); + Envelope ret(time(0) + _ttl, _ttl, topic); bytes input(1 + m_payload.size()); input[0] = 0; @@ -94,7 +97,15 @@ Envelope Message::seal(Secret _from, FullTopic const& _topic, unsigned _ttl, uns if (m_to) encrypt(m_to, &input, ret.m_data); else - swap(ret.m_data, input); + { + // create the shared secret and encrypt + Secret s = Secret::random(); + for (h256 const& t: _fullTopic) + ret.m_data += (t ^ s).asBytes(); + bytes d; + encryptSym(s, &input, d); + ret.m_data += d; + } ret.proveWork(_workToProve); return ret; @@ -109,9 +120,9 @@ Envelope::Envelope(RLP const& _m) m_nonce = _m[4].toInt(); } -Message Envelope::open(Secret const& _s) const +Message Envelope::open(Secret const& _s, unsigned _topicIndex) const { - return Message(*this, _s); + return Message(*this, _s, _topicIndex); } unsigned Envelope::workProved() const diff --git a/libwhisper/Message.h b/libwhisper/Message.h index eeb7446eb..b23e5d576 100644 --- a/libwhisper/Message.h +++ b/libwhisper/Message.h @@ -85,13 +85,15 @@ enum /*Message Flags*/ ContainsSignature = 1 }; +static const unsigned NotPublic = (unsigned)-1; + /// An (unencrypted) message, constructed from the combination of an Envelope, and, potentially, /// a Secret key to decrypt the Message. class Message { public: Message() {} - Message(Envelope const& _e, Secret const& _s = Secret(), unsigned _topicIndex = (unsigned)-1); + Message(Envelope const& _e, Secret const& _s, unsigned _topicIndex); Message(bytes const& _payload): m_payload(_payload) {} Message(bytesConstRef _payload): m_payload(_payload.toBytes()) {} Message(bytes&& _payload) { std::swap(_payload, m_payload); } diff --git a/libwhisper/WhisperHost.cpp b/libwhisper/WhisperHost.cpp index 7bf427aa6..609d5e8bf 100644 --- a/libwhisper/WhisperHost.cpp +++ b/libwhisper/WhisperHost.cpp @@ -104,14 +104,15 @@ unsigned WhisperHost::installWatchOnId(h256 _h) return ret; } -unsigned WhisperHost::installWatch(shh::TopicFilter const& _f) +unsigned WhisperHost::installWatch(shh::FullTopic const& _ft) { Guard l(m_filterLock); - h256 h = _f.sha3(); + InstalledFilter f(_ft); + h256 h = f.filter.sha3(); if (!m_filters.count(h)) - m_filters.insert(make_pair(h, _f)); + m_filters.insert(make_pair(h, f)); return installWatchOnId(h); } diff --git a/libwhisper/WhisperHost.h b/libwhisper/WhisperHost.h index 1a4ec2a71..07207989e 100644 --- a/libwhisper/WhisperHost.h +++ b/libwhisper/WhisperHost.h @@ -51,8 +51,8 @@ public: virtual void inject(Envelope const& _e, WhisperPeer* _from = nullptr) override; - using Interface::installWatch; - virtual unsigned installWatch(TopicFilter const& _filter) override; + virtual FullTopic getFilter(unsigned _id) const { try { return m_filters.at(m_watches.at(_id).id).full; } catch (...) { return FullTopic(); } } + virtual unsigned installWatch(FullTopic const& _filter) override; virtual unsigned installWatchOnId(h256 _filterId) override; virtual void uninstallWatch(unsigned _watchId) override; virtual h256s peekWatch(unsigned _watchId) const override { dev::Guard l(m_filterLock); try { return m_watches.at(_watchId).changes; } catch (...) { return h256s(); } } diff --git a/libwhisper/WhisperPeer.cpp b/libwhisper/WhisperPeer.cpp index dfa7ab628..7480a104e 100644 --- a/libwhisper/WhisperPeer.cpp +++ b/libwhisper/WhisperPeer.cpp @@ -106,7 +106,7 @@ void WhisperPeer::sendMessages() } } -void WhisperPeer::noteNewMessage(h256 _h, Message const& _m) +void WhisperPeer::noteNewMessage(h256 _h, Envelope const& _m) { Guard l(x_unseen); m_unseen.insert(make_pair(rating(_m), _h)); diff --git a/libwhisper/WhisperPeer.h b/libwhisper/WhisperPeer.h index d21f33725..5dd265e5a 100644 --- a/libwhisper/WhisperPeer.h +++ b/libwhisper/WhisperPeer.h @@ -63,8 +63,8 @@ private: void sendMessages(); - unsigned rating(Message const&) const { return 0; } // TODO - void noteNewMessage(h256 _h, Message const& _m); + unsigned rating(Envelope const&) const { return 0; } // TODO + void noteNewMessage(h256 _h, Envelope const& _m); mutable dev::Mutex x_unseen; std::multimap m_unseen; ///< Rated according to what they want. From 5f523e146d9a339d6b3ce3e7098fa7fddb8fc138 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Thu, 29 Jan 2015 10:54:34 -0800 Subject: [PATCH 18/21] Dev. --- libweb3jsonrpc/WebThreeStubServerBase.cpp | 3 +-- libwhisper/Interface.h | 10 +++++++++- libwhisper/Message.h | 6 +++--- libwhisper/WhisperHost.h | 2 +- test/whisperTopic.cpp | 2 +- 5 files changed, 15 insertions(+), 8 deletions(-) diff --git a/libweb3jsonrpc/WebThreeStubServerBase.cpp b/libweb3jsonrpc/WebThreeStubServerBase.cpp index bd09ac9c6..d6f00f7cb 100644 --- a/libweb3jsonrpc/WebThreeStubServerBase.cpp +++ b/libweb3jsonrpc/WebThreeStubServerBase.cpp @@ -583,8 +583,7 @@ Json::Value WebThreeStubServerBase::shh_changed(int const& _id) else { unsigned i = 0; - for (; i < face()->getFilter(_id).size() && !face()->getFilter(_id)[i]; ++i) {} - m = e.open(face()->getFilter(_id)[i], i); + m = e.open(face()->filterKey(_id)); } ret.append(toJson(h, e, m)); } diff --git a/libwhisper/Interface.h b/libwhisper/Interface.h index 1ee36c756..2a14b4a16 100644 --- a/libwhisper/Interface.h +++ b/libwhisper/Interface.h @@ -45,10 +45,18 @@ namespace shh class Watch; +struct FilterKey +{ + unsigned topicIndex = (unsigned)-1; + Secret key; +}; + struct InstalledFilter { InstalledFilter(FullTopic const& _f): full(_f), filter(_f) {} + FilterKey filterKey() const { unsigned i; for (i = 0; i < full.size() && !full[i]; ++i) {} return i < full.size() ? FilterKey{i, full[i]} : FilterKey(); } + FullTopic full; TopicFilter filter; unsigned refCount = 1; @@ -70,7 +78,7 @@ public: virtual void inject(Envelope const& _m, WhisperPeer* _from = nullptr) = 0; - virtual FullTopic getFilter(unsigned _id) const = 0; + virtual FilterKey filterKey(unsigned _id) const = 0; virtual unsigned installWatch(FullTopic const& _mask) = 0; virtual unsigned installWatchOnId(h256 _filterId) = 0; virtual void uninstallWatch(unsigned _watchId) = 0; diff --git a/libwhisper/Message.h b/libwhisper/Message.h index b23e5d576..0eef832eb 100644 --- a/libwhisper/Message.h +++ b/libwhisper/Message.h @@ -45,6 +45,8 @@ enum IncludeNonce WithNonce = 1 }; +static const unsigned NotPublic = (unsigned)-1; + class Envelope { friend class Message; @@ -64,7 +66,7 @@ public: Topic const& topic() const { return m_topic; } bytes const& data() const { return m_data; } - Message open(Secret const& _s = Secret(), unsigned _topicIndex = (unsigned)-1) const; + Message open(Secret const& _s, unsigned _topicIndex = NotPublic) const; unsigned workProved() const; void proveWork(unsigned _ms); @@ -85,8 +87,6 @@ enum /*Message Flags*/ ContainsSignature = 1 }; -static const unsigned NotPublic = (unsigned)-1; - /// An (unencrypted) message, constructed from the combination of an Envelope, and, potentially, /// a Secret key to decrypt the Message. class Message diff --git a/libwhisper/WhisperHost.h b/libwhisper/WhisperHost.h index 07207989e..425fe3108 100644 --- a/libwhisper/WhisperHost.h +++ b/libwhisper/WhisperHost.h @@ -51,7 +51,7 @@ public: virtual void inject(Envelope const& _e, WhisperPeer* _from = nullptr) override; - virtual FullTopic getFilter(unsigned _id) const { try { return m_filters.at(m_watches.at(_id).id).full; } catch (...) { return FullTopic(); } } + virtual FilterKey filterKey(unsigned _id) const { try { return m_filters.at(m_watches.at(_id).id).filterKey(); } catch (...) { return FilterKey(); } } virtual unsigned installWatch(FullTopic const& _filter) override; virtual unsigned installWatchOnId(h256 _filterId) override; virtual void uninstallWatch(unsigned _watchId) override; diff --git a/test/whisperTopic.cpp b/test/whisperTopic.cpp index 31cefdb8a..a47b0a574 100644 --- a/test/whisperTopic.cpp +++ b/test/whisperTopic.cpp @@ -55,7 +55,7 @@ BOOST_AUTO_TEST_CASE(topic) { for (auto i: wh->checkWatch(w)) { - Message msg = wh->envelope(i).open(); + Message msg = wh->envelope(i).open(wh->filterKey()); last = RLP(msg.payload()).toInt(); cnote << "New message from:" << msg.from().abridged() << RLP(msg.payload()).toInt(); result += last; From cec504d1a219ee92d1a137c38bce63988b1b37b5 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Thu, 29 Jan 2015 15:26:08 -0800 Subject: [PATCH 19/21] Various fixes. --- alethzero/MainWin.cpp | 4 ++-- libweb3jsonrpc/WebThreeStubServerBase.cpp | 5 +---- libwhisper/Interface.h | 9 +-------- libwhisper/Message.cpp | 17 +++++++++-------- libwhisper/Message.h | 16 ++++++++++++---- libwhisper/WhisperHost.cpp | 2 +- test/whisperTopic.cpp | 18 +++++++++--------- 7 files changed, 35 insertions(+), 36 deletions(-) diff --git a/alethzero/MainWin.cpp b/alethzero/MainWin.cpp index b771d4836..a608957a9 100644 --- a/alethzero/MainWin.cpp +++ b/alethzero/MainWin.cpp @@ -2414,10 +2414,10 @@ void Main::refreshWhispers() shh::Envelope const& e = w.second; shh::Message m; for (pair const& i: m_server->ids()) - if (!!(m = e.open(i.second))) + if (!!(m = e.open(shh::FilterKey(shh::Undefined, i.second)))) break; if (!m) - m = e.open(); + m = e.open(shh::FilterKey()); QString msg; if (m.from()) diff --git a/libweb3jsonrpc/WebThreeStubServerBase.cpp b/libweb3jsonrpc/WebThreeStubServerBase.cpp index d6f00f7cb..0fd9476a4 100644 --- a/libweb3jsonrpc/WebThreeStubServerBase.cpp +++ b/libweb3jsonrpc/WebThreeStubServerBase.cpp @@ -576,15 +576,12 @@ Json::Value WebThreeStubServerBase::shh_changed(int const& _id) if (pub) { cwarn << "Silently decrypting message from identity" << pub.abridged() << ": User validation hook goes here."; - m = e.open(m_ids[pub], shh::NotPublic); + m = e.open(shh::FilterKey(shh::Undefined, m_ids[pub])); if (!m) continue; } else - { - unsigned i = 0; m = e.open(face()->filterKey(_id)); - } ret.append(toJson(h, e, m)); } diff --git a/libwhisper/Interface.h b/libwhisper/Interface.h index 2a14b4a16..6e3eb1cd1 100644 --- a/libwhisper/Interface.h +++ b/libwhisper/Interface.h @@ -45,17 +45,11 @@ namespace shh class Watch; -struct FilterKey -{ - unsigned topicIndex = (unsigned)-1; - Secret key; -}; - struct InstalledFilter { InstalledFilter(FullTopic const& _f): full(_f), filter(_f) {} - FilterKey filterKey() const { unsigned i; for (i = 0; i < full.size() && !full[i]; ++i) {} return i < full.size() ? FilterKey{i, full[i]} : FilterKey(); } + FilterKey filterKey() const { unsigned i; for (i = 0; i < full.size() && !full[i]; ++i) {} return i < full.size() ? FilterKey(i, full[i]) : FilterKey(); } FullTopic full; TopicFilter filter; @@ -116,7 +110,6 @@ public: Watch(Interface& _c, FullTopic const& _f): m_c(&_c), m_id(_c.installWatch(_f)) {} ~Watch() { if (m_c) m_c->uninstallWatch(m_id); } - FullTopic fullTopic() const { return m_c ? m_c->getFilter(m_id) : FullTopic(); } h256s check() { return m_c ? m_c->checkWatch(m_id) : h256s(); } h256s peek() { return m_c ? m_c->peekWatch(m_id) : h256s(); } diff --git a/libwhisper/Message.cpp b/libwhisper/Message.cpp index ed7faaea8..b87ba0717 100644 --- a/libwhisper/Message.cpp +++ b/libwhisper/Message.cpp @@ -26,27 +26,28 @@ using namespace dev; using namespace dev::p2p; using namespace dev::shh; -Message::Message(Envelope const& _e, Secret const& _s, unsigned _topicIndex) +Message::Message(Envelope const& _e, FilterKey const& _fk) { try { bytes b; - if (_s) - if (!decrypt(_s, &(_e.data()), b)) + if (_fk.topicIndex == Undefined) + if (!_fk.key || !decrypt(_fk.key, &(_e.data()), b)) return; else{} - else if (_topicIndex != (unsigned)-1) + else { // public - need to get the key through combining with the topic/topicIndex we know. if (_e.data().size() < _e.topics().size() * 32) return; // get key from decrypted topic key: just xor - if (!decryptSym(_s ^ h256(bytesConstRef(&(_e.data())).cropped(32 * _topicIndex, 32)), bytesConstRef(&(_e.data())).cropped(32 * _e.topics().size()), b)) + if (!decryptSym(_fk.key ^ h256(bytesConstRef(&(_e.data())).cropped(32 * _fk.topicIndex, 32)), bytesConstRef(&(_e.data())).cropped(32 * _e.topics().size()), b)) return; } if (populate(b)) - m_to = KeyPair(_s).pub(); + if (_fk.key && _fk.topicIndex == Undefined) + m_to = KeyPair(_fk.key).pub(); } catch (...) // Invalid secret? TODO: replace ... with InvalidSecret { @@ -120,9 +121,9 @@ Envelope::Envelope(RLP const& _m) m_nonce = _m[4].toInt(); } -Message Envelope::open(Secret const& _s, unsigned _topicIndex) const +Message Envelope::open(FilterKey const& _filterKey) const { - return Message(*this, _s, _topicIndex); + return Message(*this, _filterKey); } unsigned Envelope::workProved() const diff --git a/libwhisper/Message.h b/libwhisper/Message.h index 0eef832eb..00e619461 100644 --- a/libwhisper/Message.h +++ b/libwhisper/Message.h @@ -39,14 +39,22 @@ namespace shh class Message; +static const unsigned Undefined = (unsigned)-1; + +struct FilterKey +{ + FilterKey() {} + FilterKey(unsigned _tI, Secret const& _k): topicIndex(_tI), key(_k) {} + unsigned topicIndex = Undefined; + Secret key; +}; + enum IncludeNonce { WithoutNonce = 0, WithNonce = 1 }; -static const unsigned NotPublic = (unsigned)-1; - class Envelope { friend class Message; @@ -66,7 +74,7 @@ public: Topic const& topic() const { return m_topic; } bytes const& data() const { return m_data; } - Message open(Secret const& _s, unsigned _topicIndex = NotPublic) const; + Message open(FilterKey const& _fk) const; unsigned workProved() const; void proveWork(unsigned _ms); @@ -93,7 +101,7 @@ class Message { public: Message() {} - Message(Envelope const& _e, Secret const& _s, unsigned _topicIndex); + Message(Envelope const& _e, FilterKey const& _fk); Message(bytes const& _payload): m_payload(_payload) {} Message(bytesConstRef _payload): m_payload(_payload.toBytes()) {} Message(bytes&& _payload) { std::swap(_payload, m_payload); } diff --git a/libwhisper/WhisperHost.cpp b/libwhisper/WhisperHost.cpp index 609d5e8bf..213134db9 100644 --- a/libwhisper/WhisperHost.cpp +++ b/libwhisper/WhisperHost.cpp @@ -56,7 +56,7 @@ void WhisperHost::streamMessage(h256 _m, RLPStream& _s) const void WhisperHost::inject(Envelope const& _m, WhisperPeer* _p) { - cnote << "inject: " << _m.expiry() << _m.ttl() << _m.topic() << toHex(_m.data()); + cnote << this << ": inject: " << _m.expiry() << _m.ttl() << _m.topic() << toHex(_m.data()); if (_m.expiry() <= time(0)) return; diff --git a/test/whisperTopic.cpp b/test/whisperTopic.cpp index a47b0a574..635defd75 100644 --- a/test/whisperTopic.cpp +++ b/test/whisperTopic.cpp @@ -34,7 +34,7 @@ BOOST_AUTO_TEST_CASE(topic) { cnote << "Testing Whisper..."; auto oldLogVerbosity = g_logVerbosity; - g_logVerbosity = 0; + g_logVerbosity = 4; bool started = false; unsigned result = 0; @@ -46,16 +46,16 @@ BOOST_AUTO_TEST_CASE(topic) auto wh = ph.registerCapability(new WhisperHost()); ph.start(); - started = true; - /// Only interested in odd packets 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)) { - Message msg = wh->envelope(i).open(wh->filterKey()); + Message msg = wh->envelope(i).open(wh->filterKey(w)); last = RLP(msg.payload()).toInt(); cnote << "New message from:" << msg.from().abridged() << RLP(msg.payload()).toInt(); result += last; @@ -116,7 +116,7 @@ BOOST_AUTO_TEST_CASE(forwarding) { for (auto i: wh->checkWatch(w)) { - Message msg = wh->envelope(i).open(); + Message msg = wh->envelope(i).open(wh->filterKey(w)); unsigned last = RLP(msg.payload()).toInt(); cnote << "New message from:" << msg.from().abridged() << RLP(msg.payload()).toInt(); result = last; @@ -152,7 +152,7 @@ BOOST_AUTO_TEST_CASE(forwarding) { for (auto i: wh->checkWatch(w)) { - Message msg = wh->envelope(i).open(); + Message msg = wh->envelope(i).open(wh->filterKey(w)); cnote << "New message from:" << msg.from().abridged() << RLP(msg.payload()).toInt(); } this_thread::sleep_for(chrono::milliseconds(50)); @@ -215,7 +215,7 @@ BOOST_AUTO_TEST_CASE(asyncforwarding) { for (auto i: wh->checkWatch(w)) { - Message msg = wh->envelope(i).open(); + Message msg = wh->envelope(i).open(wh->filterKey(w)); cnote << "New message from:" << msg.from().abridged() << RLP(msg.payload()).toInt(); } this_thread::sleep_for(chrono::milliseconds(50)); @@ -255,7 +255,7 @@ BOOST_AUTO_TEST_CASE(asyncforwarding) { for (auto i: wh->checkWatch(w)) { - Message msg = wh->envelope(i).open(); + Message msg = wh->envelope(i).open(wh->filterKey(w)); unsigned last = RLP(msg.payload()).toInt(); cnote << "New message from:" << msg.from().abridged() << RLP(msg.payload()).toInt(); result = last; From 7d504a275f1b34681956c078f489b7f3d9bda49c Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Thu, 29 Jan 2015 20:22:08 -0800 Subject: [PATCH 20/21] Whisper now 100% encrypted. --- alethzero/MainWin.cpp | 4 +- libweb3jsonrpc/WebThreeStubServerBase.cpp | 8 +-- libwhisper/Interface.h | 4 +- libwhisper/Message.cpp | 60 ++++++++++++++++++----- libwhisper/Message.h | 4 +- libwhisper/WhisperHost.h | 4 +- test/whisperTopic.cpp | 12 ++--- 7 files changed, 66 insertions(+), 30 deletions(-) diff --git a/alethzero/MainWin.cpp b/alethzero/MainWin.cpp index a608957a9..3c87f649a 100644 --- a/alethzero/MainWin.cpp +++ b/alethzero/MainWin.cpp @@ -2414,10 +2414,10 @@ void Main::refreshWhispers() shh::Envelope const& e = w.second; shh::Message m; for (pair const& i: m_server->ids()) - if (!!(m = e.open(shh::FilterKey(shh::Undefined, i.second)))) + if (!!(m = e.open(shh::FullTopic(), i.second))) break; if (!m) - m = e.open(shh::FilterKey()); + m = e.open(shh::FullTopic()); QString msg; if (m.from()) diff --git a/libweb3jsonrpc/WebThreeStubServerBase.cpp b/libweb3jsonrpc/WebThreeStubServerBase.cpp index 0fd9476a4..c8aff938f 100644 --- a/libweb3jsonrpc/WebThreeStubServerBase.cpp +++ b/libweb3jsonrpc/WebThreeStubServerBase.cpp @@ -576,12 +576,12 @@ Json::Value WebThreeStubServerBase::shh_changed(int const& _id) if (pub) { cwarn << "Silently decrypting message from identity" << pub.abridged() << ": User validation hook goes here."; - m = e.open(shh::FilterKey(shh::Undefined, m_ids[pub])); - if (!m) - continue; + m = e.open(face()->fullTopic(_id), m_ids[pub]); } else - m = e.open(face()->filterKey(_id)); + m = e.open(face()->fullTopic(_id)); + if (!m) + continue; ret.append(toJson(h, e, m)); } diff --git a/libwhisper/Interface.h b/libwhisper/Interface.h index 6e3eb1cd1..37651a826 100644 --- a/libwhisper/Interface.h +++ b/libwhisper/Interface.h @@ -49,8 +49,6 @@ struct InstalledFilter { InstalledFilter(FullTopic const& _f): full(_f), filter(_f) {} - FilterKey filterKey() const { unsigned i; for (i = 0; i < full.size() && !full[i]; ++i) {} return i < full.size() ? FilterKey(i, full[i]) : FilterKey(); } - FullTopic full; TopicFilter filter; unsigned refCount = 1; @@ -72,7 +70,7 @@ public: virtual void inject(Envelope const& _m, WhisperPeer* _from = nullptr) = 0; - virtual FilterKey filterKey(unsigned _id) const = 0; + virtual FullTopic const& fullTopic(unsigned _id) const = 0; virtual unsigned installWatch(FullTopic const& _mask) = 0; virtual unsigned installWatchOnId(h256 _filterId) = 0; virtual void uninstallWatch(unsigned _watchId) = 0; diff --git a/libwhisper/Message.cpp b/libwhisper/Message.cpp index b87ba0717..74ce9475d 100644 --- a/libwhisper/Message.cpp +++ b/libwhisper/Message.cpp @@ -26,28 +26,56 @@ using namespace dev; using namespace dev::p2p; using namespace dev::shh; -Message::Message(Envelope const& _e, FilterKey const& _fk) +Topic collapse(FullTopic const& _fullTopic) +{ + Topic ret; + ret.reserve(_fullTopic.size()); + for (auto const& ft: _fullTopic) + ret.push_back(TopicPart(ft)); + return ret; +} + +Message::Message(Envelope const& _e, FullTopic const& _fk, Secret const& _s) { try { bytes b; - if (_fk.topicIndex == Undefined) - if (!_fk.key || !decrypt(_fk.key, &(_e.data()), b)) + if (_s) + if (!decrypt(_s, &(_e.data()), b)) return; else{} else { // public - need to get the key through combining with the topic/topicIndex we know. - if (_e.data().size() < _e.topics().size() * 32) + unsigned topicIndex = 0; + Secret topicSecret; + + // determine topicSecret/topicIndex from knowledge of the collapsed topics (which give the order) and our full-size filter topic. + Topic 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 - if (!decryptSym(_fk.key ^ h256(bytesConstRef(&(_e.data())).cropped(32 * _fk.topicIndex, 32)), bytesConstRef(&(_e.data())).cropped(32 * _e.topics().size()), b)) + 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 (_fk.key && _fk.topicIndex == Undefined) - m_to = KeyPair(_fk.key).pub(); + if (_s) + m_to = KeyPair(_s).pub(); } catch (...) // Invalid secret? TODO: replace ... with InvalidSecret { @@ -77,9 +105,7 @@ bool Message::populate(bytes const& _data) Envelope Message::seal(Secret _from, FullTopic const& _fullTopic, unsigned _ttl, unsigned _workToProve) const { - Topic topic; - for (auto const& ft: _fullTopic) - topic.push_back(TopicPart(ft)); + Topic topic = collapse(_fullTopic); Envelope ret(time(0) + _ttl, _ttl, topic); bytes input(1 + m_payload.size()); @@ -106,6 +132,16 @@ Envelope Message::seal(Secret _from, FullTopic const& _fullTopic, unsigned _ttl, 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); @@ -121,9 +157,9 @@ Envelope::Envelope(RLP const& _m) m_nonce = _m[4].toInt(); } -Message Envelope::open(FilterKey const& _filterKey) const +Message Envelope::open(FullTopic const& _ft, Secret const& _s) const { - return Message(*this, _filterKey); + return Message(*this, _ft, _s); } unsigned Envelope::workProved() const diff --git a/libwhisper/Message.h b/libwhisper/Message.h index 00e619461..b4b88b472 100644 --- a/libwhisper/Message.h +++ b/libwhisper/Message.h @@ -74,7 +74,7 @@ public: Topic const& topic() const { return m_topic; } bytes const& data() const { return m_data; } - Message open(FilterKey const& _fk) const; + Message open(FullTopic const& _ft, Secret const& _s = Secret()) const; unsigned workProved() const; void proveWork(unsigned _ms); @@ -101,7 +101,7 @@ class Message { public: Message() {} - Message(Envelope const& _e, FilterKey const& _fk); + Message(Envelope const& _e, FullTopic const& _ft, Secret const& _s = Secret()); Message(bytes const& _payload): m_payload(_payload) {} Message(bytesConstRef _payload): m_payload(_payload.toBytes()) {} Message(bytes&& _payload) { std::swap(_payload, m_payload); } diff --git a/libwhisper/WhisperHost.h b/libwhisper/WhisperHost.h index 425fe3108..8111c6449 100644 --- a/libwhisper/WhisperHost.h +++ b/libwhisper/WhisperHost.h @@ -39,6 +39,8 @@ namespace dev namespace shh { +static const FullTopic EmptyFullTopic; + class WhisperHost: public HostCapability, public Interface, public Worker { friend class WhisperPeer; @@ -51,7 +53,7 @@ public: virtual void inject(Envelope const& _e, WhisperPeer* _from = nullptr) override; - virtual FilterKey filterKey(unsigned _id) const { try { return m_filters.at(m_watches.at(_id).id).filterKey(); } catch (...) { return FilterKey(); } } + virtual FullTopic const& fullTopic(unsigned _id) const { try { return m_filters.at(m_watches.at(_id).id).full; } catch (...) { return EmptyFullTopic; } } virtual unsigned installWatch(FullTopic const& _filter) override; virtual unsigned installWatchOnId(h256 _filterId) override; virtual void uninstallWatch(unsigned _watchId) override; diff --git a/test/whisperTopic.cpp b/test/whisperTopic.cpp index 635defd75..0162124b8 100644 --- a/test/whisperTopic.cpp +++ b/test/whisperTopic.cpp @@ -34,7 +34,7 @@ BOOST_AUTO_TEST_CASE(topic) { cnote << "Testing Whisper..."; auto oldLogVerbosity = g_logVerbosity; - g_logVerbosity = 4; + g_logVerbosity = 0; bool started = false; unsigned result = 0; @@ -55,7 +55,7 @@ BOOST_AUTO_TEST_CASE(topic) { for (auto i: wh->checkWatch(w)) { - Message msg = wh->envelope(i).open(wh->filterKey(w)); + Message msg = wh->envelope(i).open(wh->fullTopic(w)); last = RLP(msg.payload()).toInt(); cnote << "New message from:" << msg.from().abridged() << RLP(msg.payload()).toInt(); result += last; @@ -116,7 +116,7 @@ BOOST_AUTO_TEST_CASE(forwarding) { for (auto i: wh->checkWatch(w)) { - Message msg = wh->envelope(i).open(wh->filterKey(w)); + Message msg = wh->envelope(i).open(wh->fullTopic(w)); unsigned last = RLP(msg.payload()).toInt(); cnote << "New message from:" << msg.from().abridged() << RLP(msg.payload()).toInt(); result = last; @@ -152,7 +152,7 @@ BOOST_AUTO_TEST_CASE(forwarding) { for (auto i: wh->checkWatch(w)) { - Message msg = wh->envelope(i).open(wh->filterKey(w)); + Message msg = wh->envelope(i).open(wh->fullTopic(w)); cnote << "New message from:" << msg.from().abridged() << RLP(msg.payload()).toInt(); } this_thread::sleep_for(chrono::milliseconds(50)); @@ -215,7 +215,7 @@ BOOST_AUTO_TEST_CASE(asyncforwarding) { for (auto i: wh->checkWatch(w)) { - Message msg = wh->envelope(i).open(wh->filterKey(w)); + Message msg = wh->envelope(i).open(wh->fullTopic(w)); cnote << "New message from:" << msg.from().abridged() << RLP(msg.payload()).toInt(); } this_thread::sleep_for(chrono::milliseconds(50)); @@ -255,7 +255,7 @@ BOOST_AUTO_TEST_CASE(asyncforwarding) { for (auto i: wh->checkWatch(w)) { - Message msg = wh->envelope(i).open(wh->filterKey(w)); + Message msg = wh->envelope(i).open(wh->fullTopic(w)); unsigned last = RLP(msg.payload()).toInt(); cnote << "New message from:" << msg.from().abridged() << RLP(msg.payload()).toInt(); result = last; From ec10d674b2e61eabb22e2789921a96d73bf27fbd Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Thu, 29 Jan 2015 20:34:54 -0800 Subject: [PATCH 21/21] Double SHA-3 as good crypto practice. --- libwhisper/Common.cpp | 22 ++++++++++++++++++---- libwhisper/Common.h | 16 ++++++++++------ libwhisper/Message.cpp | 13 ++----------- libwhisper/Message.h | 6 +++--- libwhisper/WhisperHost.h | 2 +- 5 files changed, 34 insertions(+), 25 deletions(-) diff --git a/libwhisper/Common.cpp b/libwhisper/Common.cpp index bbc7ecdf7..f17ad638b 100644 --- a/libwhisper/Common.cpp +++ b/libwhisper/Common.cpp @@ -28,12 +28,26 @@ using namespace dev; using namespace dev::p2p; using namespace dev::shh; -Topic BuildTopic::toTopic() const +CollapsedTopicPart dev::shh::collapse(FullTopicPart const& _p) { - Topic ret; + return CollapsedTopicPart(sha3(_p)); +} + +CollapsedTopic dev::shh::collapse(FullTopic const& _fullTopic) +{ + CollapsedTopic ret; + ret.reserve(_fullTopic.size()); + for (auto const& ft: _fullTopic) + ret.push_back(collapse(ft)); + return ret; +} + +CollapsedTopic BuildTopic::toTopic() const +{ + CollapsedTopic ret; ret.reserve(m_parts.size()); for (auto const& h: m_parts) - ret.push_back(TopicPart(h)); + ret.push_back(collapse(h)); return ret; } @@ -75,7 +89,7 @@ TopicMask BuildTopicMask::toTopicMask() const TopicMask ret; ret.reserve(m_parts.size()); for (auto const& h: m_parts) - ret.push_back(make_pair(TopicPart(h), ~TopicPart())); + ret.push_back(make_pair(collapse(h), ~CollapsedTopicPart())); return ret; } diff --git a/libwhisper/Common.h b/libwhisper/Common.h index e19c65ea9..8180b0ec4 100644 --- a/libwhisper/Common.h +++ b/libwhisper/Common.h @@ -59,11 +59,15 @@ enum WhisperPacket PacketCount }; -using TopicPart = FixedHash<4>; +using CollapsedTopicPart = FixedHash<4>; +using FullTopicPart = h256; -using Topic = std::vector; +using CollapsedTopic = std::vector; using FullTopic = h256s; +CollapsedTopicPart collapse(FullTopicPart const& _fullTopicPart); +CollapsedTopic collapse(FullTopic const& _fullTopic); + class BuildTopic { public: @@ -75,9 +79,9 @@ public: BuildTopic& shiftRaw(h256 const& _part) { m_parts.push_back(_part); return *this; } - operator Topic() const { return toTopic(); } + operator CollapsedTopic() const { return toTopic(); } operator FullTopic() const { return toFullTopic(); } - Topic toTopic() const; + CollapsedTopic toTopic() const; FullTopic toFullTopic() const { return m_parts; } protected: @@ -86,14 +90,14 @@ protected: h256s m_parts; }; -using TopicMask = std::vector>; +using TopicMask = std::vector>; using TopicMasks = std::vector; class TopicFilter { public: TopicFilter() {} - TopicFilter(FullTopic const& _m) { m_topicMasks.push_back(TopicMask()); for (auto const& h: _m) m_topicMasks.back().push_back(std::make_pair(TopicPart(h), h ? ~TopicPart() : TopicPart())); } + TopicFilter(FullTopic const& _m) { m_topicMasks.push_back(TopicMask()); for (auto const& h: _m) m_topicMasks.back().push_back(std::make_pair(collapse(h), h ? ~CollapsedTopicPart() : CollapsedTopicPart())); } TopicFilter(TopicMask const& _m): m_topicMasks(1, _m) {} TopicFilter(TopicMasks const& _m): m_topicMasks(_m) {} TopicFilter(RLP const& _r)//: m_topicMasks(_r.toVector>()) diff --git a/libwhisper/Message.cpp b/libwhisper/Message.cpp index 74ce9475d..07bcea0c1 100644 --- a/libwhisper/Message.cpp +++ b/libwhisper/Message.cpp @@ -26,15 +26,6 @@ using namespace dev; using namespace dev::p2p; using namespace dev::shh; -Topic collapse(FullTopic const& _fullTopic) -{ - Topic ret; - ret.reserve(_fullTopic.size()); - for (auto const& ft: _fullTopic) - ret.push_back(TopicPart(ft)); - return ret; -} - Message::Message(Envelope const& _e, FullTopic const& _fk, Secret const& _s) { try @@ -51,7 +42,7 @@ Message::Message(Envelope const& _e, FullTopic const& _fk, Secret const& _s) Secret topicSecret; // determine topicSecret/topicIndex from knowledge of the collapsed topics (which give the order) and our full-size filter topic. - Topic knownTopic = collapse(_fk); + 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]) @@ -105,7 +96,7 @@ bool Message::populate(bytes const& _data) Envelope Message::seal(Secret _from, FullTopic const& _fullTopic, unsigned _ttl, unsigned _workToProve) const { - Topic topic = collapse(_fullTopic); + CollapsedTopic topic = collapse(_fullTopic); Envelope ret(time(0) + _ttl, _ttl, topic); bytes input(1 + m_payload.size()); diff --git a/libwhisper/Message.h b/libwhisper/Message.h index b4b88b472..7e5df5a95 100644 --- a/libwhisper/Message.h +++ b/libwhisper/Message.h @@ -71,7 +71,7 @@ public: unsigned sent() const { return m_expiry - m_ttl; } unsigned expiry() const { return m_expiry; } unsigned ttl() const { return m_ttl; } - Topic const& topic() const { return m_topic; } + CollapsedTopic const& topic() const { return m_topic; } bytes const& data() const { return m_data; } Message open(FullTopic const& _ft, Secret const& _s = Secret()) const; @@ -80,13 +80,13 @@ public: void proveWork(unsigned _ms); private: - Envelope(unsigned _exp, unsigned _ttl, Topic const& _topic): m_expiry(_exp), m_ttl(_ttl), m_topic(_topic) {} + Envelope(unsigned _exp, unsigned _ttl, CollapsedTopic const& _topic): m_expiry(_exp), m_ttl(_ttl), m_topic(_topic) {} unsigned m_expiry = 0; unsigned m_ttl = 0; u256 m_nonce; - Topic m_topic; + CollapsedTopic m_topic; bytes m_data; }; diff --git a/libwhisper/WhisperHost.h b/libwhisper/WhisperHost.h index 8111c6449..b6e683778 100644 --- a/libwhisper/WhisperHost.h +++ b/libwhisper/WhisperHost.h @@ -49,7 +49,7 @@ public: WhisperHost(); virtual ~WhisperHost(); - unsigned protocolVersion() const { return 1; } + unsigned protocolVersion() const { return 2; } virtual void inject(Envelope const& _e, WhisperPeer* _from = nullptr) override;