From b185a748a5fefa9d1d6485df116a13773b18de18 Mon Sep 17 00:00:00 2001 From: Christian Date: Mon, 24 Nov 2014 13:23:58 +0100 Subject: [PATCH] Access to blockchain data. --- libsolidity/ExpressionCompiler.cpp | 31 ++++++++++++++++-- libsolidity/GlobalContext.cpp | 18 ++++++----- libsolidity/Types.cpp | 50 ++++++++++++++++++++++++++++++ libsolidity/Types.h | 24 ++++++++++++++ test/solidityEndToEndTest.cpp | 13 ++++++++ 5 files changed, 125 insertions(+), 11 deletions(-) diff --git a/libsolidity/ExpressionCompiler.cpp b/libsolidity/ExpressionCompiler.cpp index b1a49457c..de8bc1d28 100644 --- a/libsolidity/ExpressionCompiler.cpp +++ b/libsolidity/ExpressionCompiler.cpp @@ -212,10 +212,11 @@ bool ExpressionCompiler::visit(FunctionCall& _functionCall) void ExpressionCompiler::endVisit(MemberAccess& _memberAccess) { + ASTString const& member = _memberAccess.getMemberName(); switch (_memberAccess.getExpression().getType()->getCategory()) { case Type::Category::INTEGER: - if (asserts(_memberAccess.getMemberName() == "balance")) + if (asserts(member == "balance")) BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Invalid member access to integer.")); m_context << eth::Instruction::BALANCE; break; @@ -224,12 +225,36 @@ void ExpressionCompiler::endVisit(MemberAccess& _memberAccess) BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Contract variables not yet implemented.")); break; case Type::Category::MAGIC: - BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Magic variables not yet implemented.")); + // we can ignore the kind of magic and only look at the name of the member + if (member == "coinbase") + m_context << eth::Instruction::COINBASE; + else if (member == "timestamp") + m_context << eth::Instruction::TIMESTAMP; + else if (member == "prevhash") + m_context << eth::Instruction::PREVHASH; + else if (member == "difficulty") + m_context << eth::Instruction::DIFFICULTY; + else if (member == "number") + m_context << eth::Instruction::NUMBER; + else if (member == "gaslimit") + m_context << eth::Instruction::GASLIMIT; + else if (member == "sender") + m_context << eth::Instruction::CALLER; + else if (member == "value") + m_context << eth::Instruction::CALLVALUE; + else if (member == "origin") + m_context << eth::Instruction::ORIGIN; + else if (member == "gas") + m_context << eth::Instruction::GAS; + else if (member == "gasprice") + m_context << eth::Instruction::GASPRICE; + else + BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown magic member.")); break; case Type::Category::STRUCT: { StructType const& type = dynamic_cast(*_memberAccess.getExpression().getType()); - m_context << type.getStorageOffsetOfMember(_memberAccess.getMemberName()) << eth::Instruction::ADD; + m_context << type.getStorageOffsetOfMember(member) << eth::Instruction::ADD; m_currentLValue = LValue(m_context, LValue::STORAGE); m_currentLValue.retrieveValueIfLValueNotRequested(_memberAccess); break; diff --git a/libsolidity/GlobalContext.cpp b/libsolidity/GlobalContext.cpp index f2e6d9ce9..4120dae5e 100644 --- a/libsolidity/GlobalContext.cpp +++ b/libsolidity/GlobalContext.cpp @@ -32,15 +32,17 @@ namespace dev namespace solidity { -GlobalContext::GlobalContext() +GlobalContext::GlobalContext(): + m_magicVariables{make_shared(MagicVariableDeclaration::VariableKind::BLOCK, + "block", + make_shared(MagicType::Kind::BLOCK)), + make_shared(MagicVariableDeclaration::VariableKind::MSG, + "msg", + make_shared(MagicType::Kind::MSG)), + make_shared(MagicVariableDeclaration::VariableKind::TX, + "tx", + make_shared(MagicType::Kind::TX))} { - // CurrentContract this; // @todo type depends on context -> switch prior to entering contract - // Message msg; - // Transaction tx; - // Block block; - - //@todo type will be a custom complex type, maybe the same type class for msg tx and block. - //addVariable("msg", ); } void GlobalContext::setCurrentContract(ContractDefinition const& _contract) diff --git a/libsolidity/Types.cpp b/libsolidity/Types.cpp index 8ded428f8..616c91831 100644 --- a/libsolidity/Types.cpp +++ b/libsolidity/Types.cpp @@ -333,5 +333,55 @@ bool TypeType::operator==(Type const& _other) const return *getActualType() == *other.getActualType(); } +MagicType::MagicType(MagicType::Kind _kind): + m_kind(_kind) +{ + switch (m_kind) + { + case Kind::BLOCK: + m_members = MemberList({{"coinbase", make_shared(0, IntegerType::Modifier::ADDRESS)}, + {"timestamp", make_shared(256)}, + {"prevhash", make_shared(256, IntegerType::Modifier::HASH)}, + {"difficulty", make_shared(256)}, + {"number", make_shared(256)}, + {"gaslimit", make_shared(256)}}); + break; + case Kind::MSG: + m_members = MemberList({{"sender", make_shared(0, IntegerType::Modifier::ADDRESS)}, + {"value", make_shared(256)}}); + break; + case Kind::TX: + m_members = MemberList({{"origin", make_shared(0, IntegerType::Modifier::ADDRESS)}, + {"gas", make_shared(256)}, + {"gasprice", make_shared(256)}}); + break; + default: + BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown kind of magic.")); + } +} + +bool MagicType::operator==(Type const& _other) const +{ + if (_other.getCategory() != getCategory()) + return false; + MagicType const& other = dynamic_cast(_other); + return other.m_kind == m_kind; +} + +string MagicType::toString() const +{ + switch (m_kind) + { + case Kind::BLOCK: + return "block"; + case Kind::MSG: + return "msg"; + case Kind::TX: + return "tx"; + default: + BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown kind of magic.")); + } +} + } } diff --git a/libsolidity/Types.h b/libsolidity/Types.h index a8caf7154..2dd4af64f 100644 --- a/libsolidity/Types.h +++ b/libsolidity/Types.h @@ -327,5 +327,29 @@ private: }; +/** + * Special type for magic variables (block, msg, tx), similar to a struct but without any reference + * (it always references a global singleton by name). + */ +class MagicType: public Type +{ +public: + enum class Kind { BLOCK, MSG, TX }; //@todo should be unified with MagicVariableDeclaration::VariableKind; + virtual Category getCategory() const override { return Category::MAGIC; } + + MagicType(Kind _kind); + virtual bool operator==(Type const& _other) const; + virtual bool canBeStored() const override { return false; } + virtual bool canLiveOutsideStorage() const override { return true; } + virtual MemberList const& getMembers() const override { return m_members; } + + virtual std::string toString() const override; + +private: + Kind m_kind; + + MemberList m_members; +}; + } } diff --git a/test/solidityEndToEndTest.cpp b/test/solidityEndToEndTest.cpp index 11535d4f6..fc542f141 100644 --- a/test/solidityEndToEndTest.cpp +++ b/test/solidityEndToEndTest.cpp @@ -761,6 +761,19 @@ BOOST_AUTO_TEST_CASE(balance) BOOST_CHECK(callContractFunction(0) == toBigEndian(u256(23))); } +BOOST_AUTO_TEST_CASE(blockchain) +{ + char const* sourceCode = "contract test {\n" + " function someInfo() returns (uint256 value, address coinbase, uint256 blockNumber) {\n" + " value = msg.value;\n" + " coinbase = block.coinbase;\n" + " blockNumber = block.number;\n" + " }\n" + "}\n"; + compileAndRun(sourceCode, 27); + BOOST_CHECK(callContractFunction(0, bytes{0}, u256(28)) == toBigEndian(u256(28)) + bytes(20, 0) + toBigEndian(u256(1))); +} + BOOST_AUTO_TEST_SUITE_END() }