aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Changelog.md1
-rw-r--r--libsolidity/analysis/TypeChecker.cpp10
-rw-r--r--libsolidity/ast/AST.cpp43
-rw-r--r--libsolidity/ast/AST.h5
-rw-r--r--libsolidity/ast/Types.cpp8
-rw-r--r--libsolidity/ast/Types.h2
-rw-r--r--libsolidity/codegen/ExpressionCompiler.cpp1
-rw-r--r--test/libsolidity/SolidityNameAndTypeResolution.cpp49
8 files changed, 117 insertions, 2 deletions
diff --git a/Changelog.md b/Changelog.md
index 7dd607d8..a14e9ec8 100644
--- a/Changelog.md
+++ b/Changelog.md
@@ -5,6 +5,7 @@ Features:
* Compiler interface: Report source location for "stack too deep" errors.
* AST: Use deterministic node identifiers.
* Type system: Introduce type identifier strings.
+ * Type checker: Warn about invalid checksum for addresses and deduce type from valid ones.
* Metadata: Do not include platform in the version number.
* Code generator: Extract array utils into low-level functions.
diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp
index adb3d54f..533c787b 100644
--- a/libsolidity/analysis/TypeChecker.cpp
+++ b/libsolidity/analysis/TypeChecker.cpp
@@ -1565,6 +1565,16 @@ void TypeChecker::endVisit(ElementaryTypeNameExpression const& _expr)
void TypeChecker::endVisit(Literal const& _literal)
{
+ if (_literal.looksLikeAddress())
+ {
+ if (_literal.passesAddressChecksum())
+ {
+ _literal.annotation().type = make_shared<IntegerType>(0, IntegerType::Modifier::Address);
+ return;
+ }
+ else
+ warning(_literal.location(), "This looks like an address but has an invalid checksum.");
+ }
_literal.annotation().type = Type::forLiteral(_literal);
if (!_literal.annotation().type)
fatalTypeError(_literal.location(), "Invalid literal value.");
diff --git a/libsolidity/ast/AST.cpp b/libsolidity/ast/AST.cpp
index 8a43c3f7..e2b50dd7 100644
--- a/libsolidity/ast/AST.cpp
+++ b/libsolidity/ast/AST.cpp
@@ -20,8 +20,6 @@
* Solidity abstract syntax tree.
*/
-#include <algorithm>
-#include <functional>
#include <libsolidity/interface/Utils.h>
#include <libsolidity/ast/AST.h>
#include <libsolidity/ast/ASTVisitor.h>
@@ -30,6 +28,11 @@
#include <libdevcore/SHA3.h>
+#include <boost/algorithm/string.hpp>
+
+#include <algorithm>
+#include <functional>
+
using namespace std;
using namespace dev;
using namespace dev::solidity;
@@ -522,3 +525,39 @@ IdentifierAnnotation& Identifier::annotation() const
m_annotation = new IdentifierAnnotation();
return static_cast<IdentifierAnnotation&>(*m_annotation);
}
+
+bool Literal::looksLikeAddress() const
+{
+ if (subDenomination() != SubDenomination::None)
+ return false;
+
+ string lit = value();
+ return lit.substr(0, 2) == "0x" && abs(int(lit.length()) - 42) <= 1;
+}
+
+bool Literal::passesAddressChecksum() const
+{
+ string lit = value();
+ solAssert(lit.substr(0, 2) == "0x", "Expected hex prefix");
+ lit = lit.substr(2);
+
+ if (lit.length() != 40)
+ return false;
+
+ h256 hash = keccak256(boost::algorithm::to_lower_copy(lit, std::locale::classic()));
+ for (size_t i = 0; i < 40; ++i)
+ {
+ char addressCharacter = lit[i];
+ bool lowerCase;
+ if ('a' <= addressCharacter && addressCharacter <= 'f')
+ lowerCase = true;
+ else if ('A' <= addressCharacter && addressCharacter <= 'F')
+ lowerCase = false;
+ else
+ continue;
+ unsigned nibble = (unsigned(hash[i / 2]) >> (4 * (1 - (i % 2)))) & 0xf;
+ if ((nibble >= 8) == lowerCase)
+ return false;
+ }
+ return true;
+}
diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h
index 4c75f7ea..743fdaa1 100644
--- a/libsolidity/ast/AST.h
+++ b/libsolidity/ast/AST.h
@@ -1584,6 +1584,11 @@ public:
SubDenomination subDenomination() const { return m_subDenomination; }
+ /// @returns true if this looks like a checksummed address.
+ bool looksLikeAddress() const;
+ /// @returns true if it passes the address checksum test.
+ bool passesAddressChecksum() const;
+
private:
Token::Value m_token;
ASTPointer<ASTString> m_value;
diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp
index cefd0603..971e1f18 100644
--- a/libsolidity/ast/Types.cpp
+++ b/libsolidity/ast/Types.cpp
@@ -405,6 +405,14 @@ string IntegerType::toString(bool) const
return prefix + dev::toString(m_bits);
}
+u256 IntegerType::literalValue(Literal const* _literal) const
+{
+ solAssert(m_modifier == Modifier::Address, "");
+ solAssert(_literal, "");
+ solAssert(_literal->value().substr(0, 2) == "0x", "");
+ return u256(_literal->value());
+}
+
TypePointer IntegerType::binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const
{
if (
diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h
index 1e94631e..770cbb30 100644
--- a/libsolidity/ast/Types.h
+++ b/libsolidity/ast/Types.h
@@ -314,6 +314,8 @@ public:
virtual std::string toString(bool _short) const override;
+ virtual u256 literalValue(Literal const* _literal) const override;
+
virtual TypePointer encodingType() const override { return shared_from_this(); }
virtual TypePointer interfaceType(bool) const override { return shared_from_this(); }
diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp
index fe0eeb1c..bda4e04d 100644
--- a/libsolidity/codegen/ExpressionCompiler.cpp
+++ b/libsolidity/codegen/ExpressionCompiler.cpp
@@ -1308,6 +1308,7 @@ void ExpressionCompiler::endVisit(Literal const& _literal)
{
case Type::Category::RationalNumber:
case Type::Category::Bool:
+ case Type::Category::Integer:
m_context << type->literalValue(&_literal);
break;
case Type::Category::StringLiteral:
diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp
index ce241c78..b6067ea5 100644
--- a/test/libsolidity/SolidityNameAndTypeResolution.cpp
+++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp
@@ -4983,6 +4983,55 @@ BOOST_AUTO_TEST_CASE(constructible_internal_constructor)
success(text);
}
+BOOST_AUTO_TEST_CASE(address_checksum_type_deduction)
+{
+ char const* text = R"(
+ contract C {
+ function f() {
+ var x = 0xfA0bFc97E48458494Ccd857e1A85DC91F7F0046E;
+ x.send(2);
+ }
+ }
+ )";
+ success(text);
+}
+
+BOOST_AUTO_TEST_CASE(invalid_address_checksum)
+{
+ char const* text = R"(
+ contract C {
+ function f() {
+ var x = 0xFA0bFc97E48458494Ccd857e1A85DC91F7F0046E;
+ }
+ }
+ )";
+ CHECK_WARNING(text, "checksum");
+}
+
+BOOST_AUTO_TEST_CASE(invalid_address_no_checksum)
+{
+ char const* text = R"(
+ contract C {
+ function f() {
+ var x = 0xfa0bfc97e48458494ccd857e1a85dc91f7f0046e;
+ }
+ }
+ )";
+ CHECK_WARNING(text, "checksum");
+}
+
+BOOST_AUTO_TEST_CASE(invalid_address_length)
+{
+ char const* text = R"(
+ contract C {
+ function f() {
+ var x = 0xA0bFc97E48458494Ccd857e1A85DC91F7F0046E;
+ }
+ }
+ )";
+ CHECK_WARNING(text, "checksum");
+}
+
BOOST_AUTO_TEST_SUITE_END()
}