aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlex Beregszaszi <alex@rtfs.hu>2017-01-26 22:42:34 +0800
committerGitHub <noreply@github.com>2017-01-26 22:42:34 +0800
commit102fd7ee5daeb7d7a7bb1254cf0ce71a23ad1220 (patch)
tree0ad00aad0ed6e5dfb382b4a7443772319ec15d34
parent024061b8280d51a11d91dc3347603c9281224705 (diff)
parentf610ba77a493f8a6f270c72222d34eedfb183722 (diff)
downloaddexon-solidity-102fd7ee5daeb7d7a7bb1254cf0ce71a23ad1220.tar
dexon-solidity-102fd7ee5daeb7d7a7bb1254cf0ce71a23ad1220.tar.gz
dexon-solidity-102fd7ee5daeb7d7a7bb1254cf0ce71a23ad1220.tar.bz2
dexon-solidity-102fd7ee5daeb7d7a7bb1254cf0ce71a23ad1220.tar.lz
dexon-solidity-102fd7ee5daeb7d7a7bb1254cf0ce71a23ad1220.tar.xz
dexon-solidity-102fd7ee5daeb7d7a7bb1254cf0ce71a23ad1220.tar.zst
dexon-solidity-102fd7ee5daeb7d7a7bb1254cf0ce71a23ad1220.zip
Merge pull request #1604 from ethereum/checksums
Warn about invalid checksums of addresses.
-rw-r--r--Changelog.md1
-rw-r--r--docs/types.rst13
-rw-r--r--libdevcore/CommonData.cpp40
-rw-r--r--libdevcore/CommonData.h5
-rw-r--r--libsolidity/analysis/TypeChecker.cpp10
-rw-r--r--libsolidity/ast/AST.cpp23
-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
-rwxr-xr-xscripts/tests.sh16
-rw-r--r--solc/CommandLineInterface.cpp5
-rwxr-xr-xtest/cmdlineTests.sh51
-rw-r--r--test/libdevcore/Checksum.cpp83
-rw-r--r--test/libsolidity/SolidityNameAndTypeResolution.cpp49
15 files changed, 294 insertions, 18 deletions
diff --git a/Changelog.md b/Changelog.md
index a8b66abc..71a1e096 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.
* Metadata: Add option to store sources as literal content.
* Code generator: Extract array utils into low-level functions.
diff --git a/docs/types.rst b/docs/types.rst
index 6b67e684..3fb1db95 100644
--- a/docs/types.rst
+++ b/docs/types.rst
@@ -171,6 +171,19 @@ Fixed Point Numbers
**COMING SOON...**
+.. index:: address, literal;address
+
+.. _address_literals:
+
+Address Literals
+----------------
+
+Hexadecimal literals that pass the address checksum test, for example
+``0xdCad3a6d3569DF655070DEd06cb7A1b2Ccd1D3AF`` are of ``address`` type.
+Hexadecimal literals that are between 39 and 41 digits
+long and do not pass the checksum test produce
+a warning and are treated as regular rational number literals.
+
.. index:: literal, literal;rational
.. _rational_literals:
diff --git a/libdevcore/CommonData.cpp b/libdevcore/CommonData.cpp
index 062d1b29..a53d9628 100644
--- a/libdevcore/CommonData.cpp
+++ b/libdevcore/CommonData.cpp
@@ -19,8 +19,12 @@
* @date 2014
*/
-#include "CommonData.h"
-#include "Exceptions.h"
+#include <libdevcore/CommonData.h>
+#include <libdevcore/Exceptions.h>
+#include <libdevcore/SHA3.h>
+
+#include <boost/algorithm/string.hpp>
+
using namespace std;
using namespace dev;
@@ -95,3 +99,35 @@ bytes dev::fromHex(std::string const& _s, WhenError _throw)
}
return ret;
}
+
+
+bool dev::passesAddressChecksum(string const& _str, bool _strict)
+{
+ string s = _str.substr(0, 2) == "0x" ? _str.substr(2) : _str;
+
+ if (s.length() != 40)
+ return false;
+
+ if (!_strict && (
+ _str.find_first_of("abcdef") == string::npos ||
+ _str.find_first_of("ABCDEF") == string::npos
+ ))
+ return true;
+
+ h256 hash = keccak256(boost::algorithm::to_lower_copy(s, std::locale::classic()));
+ for (size_t i = 0; i < 40; ++i)
+ {
+ char addressCharacter = s[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/libdevcore/CommonData.h b/libdevcore/CommonData.h
index 5ffcdcca..e0a6d221 100644
--- a/libdevcore/CommonData.h
+++ b/libdevcore/CommonData.h
@@ -179,4 +179,9 @@ bool contains(T const& _t, V const& _v)
return std::end(_t) != std::find(std::begin(_t), std::end(_t), _v);
}
+/// @returns true iff @a _str passess the hex address checksum test.
+/// @param _strict if false, hex strings with only uppercase or only lowercase letters
+/// are considered valid.
+bool passesAddressChecksum(std::string const& _str, bool _strict);
+
}
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..616de54e 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,19 @@ 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");
+ return dev::passesAddressChecksum(lit, 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/scripts/tests.sh b/scripts/tests.sh
index dfbda734..cf18cb26 100755
--- a/scripts/tests.sh
+++ b/scripts/tests.sh
@@ -30,20 +30,8 @@ set -e
REPO_ROOT="$(dirname "$0")"/..
- # Compile all files in std and examples.
-
-for f in "$REPO_ROOT"/std/*.sol
-do
- echo "Compiling $f..."
- set +e
- output=$("$REPO_ROOT"/build/solc/solc "$f" 2>&1)
- failed=$?
- # Remove the pre-release warning from the compiler output
- output=$(echo "$output" | grep -v 'pre-release')
- echo "$output"
- set -e
- test -z "$output" -a "$failed" -eq 0
-done
+echo "Running commandline tests..."
+"$REPO_ROOT/test/cmdlineTests.sh"
# This conditional is only needed because we don't have a working Homebrew
# install for `eth` at the time of writing, so we unzip the ZIP file locally
diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp
index 3e3053ce..2c1f0644 100644
--- a/solc/CommandLineInterface.cpp
+++ b/solc/CommandLineInterface.cpp
@@ -436,6 +436,11 @@ bool CommandLineInterface::parseLibraryOption(string const& _input)
string addrString(lib.begin() + colon + 1, lib.end());
boost::trim(libName);
boost::trim(addrString);
+ if (!passesAddressChecksum(addrString, false))
+ {
+ cerr << "Invalid checksum on library address \"" << libName << "\": " << addrString << endl;
+ return false;
+ }
bytes binAddr = fromHex(addrString);
h160 address(binAddr, h160::AlignRight);
if (binAddr.size() > 20 || address == h160())
diff --git a/test/cmdlineTests.sh b/test/cmdlineTests.sh
new file mode 100755
index 00000000..fc48654a
--- /dev/null
+++ b/test/cmdlineTests.sh
@@ -0,0 +1,51 @@
+#!/usr/bin/env bash
+
+#------------------------------------------------------------------------------
+# Bash script to run commandline Solidity tests.
+#
+# The documentation for solidity is hosted at:
+#
+# https://solidity.readthedocs.org
+#
+# ------------------------------------------------------------------------------
+# This file is part of solidity.
+#
+# solidity is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# solidity 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with solidity. If not, see <http://www.gnu.org/licenses/>
+#
+# (c) 2016 solidity contributors.
+#------------------------------------------------------------------------------
+
+set -e
+
+REPO_ROOT="$(dirname "$0")"/..
+SOLC="$REPO_ROOT/build/solc/solc"
+
+ # Compile all files in std and examples.
+
+for f in "$REPO_ROOT"/std/*.sol
+do
+ echo "Compiling $f..."
+ set +e
+ output=$("$SOLC" "$f" 2>&1)
+ failed=$?
+ # Remove the pre-release warning from the compiler output
+ output=$(echo "$output" | grep -v 'pre-release')
+ echo "$output"
+ set -e
+ test -z "$output" -a "$failed" -eq 0
+done
+
+# Test library checksum
+echo 'contact C {}' | "$SOLC" --link --libraries a:0x90f20564390eAe531E810af625A22f51385Cd222
+! echo 'contract C {}' | "$SOLC" --link --libraries a:0x80f20564390eAe531E810af625A22f51385Cd222 2>/dev/null
diff --git a/test/libdevcore/Checksum.cpp b/test/libdevcore/Checksum.cpp
new file mode 100644
index 00000000..32260888
--- /dev/null
+++ b/test/libdevcore/Checksum.cpp
@@ -0,0 +1,83 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ cpp-ethereum 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * Unit tests for the address checksum.
+ */
+
+#include <libdevcore/CommonData.h>
+
+#include "../TestHelper.h"
+
+using namespace std;
+
+namespace dev
+{
+namespace test
+{
+
+BOOST_AUTO_TEST_SUITE(Checksum)
+
+BOOST_AUTO_TEST_CASE(regular)
+{
+ BOOST_CHECK(passesAddressChecksum("0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed", true));
+ BOOST_CHECK(passesAddressChecksum("0xfB6916095ca1df60bB79Ce92cE3Ea74c37c5d359", true));
+ BOOST_CHECK(passesAddressChecksum("0xdbF03B407c01E7cD3CBea99509d93f8DDDC8C6FB", true));
+ BOOST_CHECK(passesAddressChecksum("0xD1220A0cf47c7B9Be7A2E6BA89F429762e7b9aDb", true));
+}
+
+BOOST_AUTO_TEST_CASE(regular_negative)
+{
+ BOOST_CHECK(!passesAddressChecksum("0x6aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed", true));
+ BOOST_CHECK(!passesAddressChecksum("0xeB6916095ca1df60bB79Ce92cE3Ea74c37c5d359", true));
+ BOOST_CHECK(!passesAddressChecksum("0xebF03B407c01E7cD3CBea99509d93f8DDDC8C6FB", true));
+ BOOST_CHECK(!passesAddressChecksum("0xE1220A0cf47c7B9Be7A2E6BA89F429762e7b9aDb", true));
+}
+
+BOOST_AUTO_TEST_CASE(regular_invalid_length)
+{
+ BOOST_CHECK(passesAddressChecksum("0x9426cbfc57389778d313268E7F85F1CDc2fdad60", true));
+ BOOST_CHECK(!passesAddressChecksum("0x9426cbfc57389778d313268E7F85F1CDc2fdad6", true));
+ BOOST_CHECK(passesAddressChecksum("0x08A61851FFa4637dE289D630Ae8c5dFb0ff9171F", true));
+ BOOST_CHECK(!passesAddressChecksum("0x8A61851FFa4637dE289D630Ae8c5dFb0ff9171F", true));
+ BOOST_CHECK(passesAddressChecksum("0x00c40cC30cb4675673c9ee382de805c19734986A", true));
+ BOOST_CHECK(!passesAddressChecksum("0xc40cC30cb4675673c9ee382de805c19734986A", true));
+ BOOST_CHECK(passesAddressChecksum("0xC40CC30cb4675673C9ee382dE805c19734986a00", true));
+ BOOST_CHECK(!passesAddressChecksum("0xC40CC30cb4675673C9ee382dE805c19734986a", true));
+}
+
+BOOST_AUTO_TEST_CASE(homocaps_valid)
+{
+ BOOST_CHECK(passesAddressChecksum("0x52908400098527886E0F7030069857D2E4169EE7", true));
+ BOOST_CHECK(passesAddressChecksum("0x8617E340B3D01FA5F11F306F4090FD50E238070D", true));
+ BOOST_CHECK(passesAddressChecksum("0xde709f2102306220921060314715629080e2fb77", true));
+ BOOST_CHECK(passesAddressChecksum("0x27b1fdb04752bbc536007a920d24acb045561c26", true));
+}
+
+BOOST_AUTO_TEST_CASE(homocaps_invalid)
+{
+ string upper = "0x00AA0000000012400000000DDEEFF000000000BB";
+ BOOST_CHECK(passesAddressChecksum(upper, false));
+ BOOST_CHECK(!passesAddressChecksum(upper, true));
+ string lower = "0x11aa000000000000000d00cc00000000000000bb";
+ BOOST_CHECK(passesAddressChecksum(lower, false));
+ BOOST_CHECK(!passesAddressChecksum(lower, true));
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+}
+}
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()
}