From d56acb68abdda19d0e5a684cdf8d40c3d7ce277e Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 1 Aug 2017 00:55:13 +0100 Subject: [PATCH 1/5] Add abi.encode, abi.encodePacked, abi.encodeWithSelector and abi.encodeWithSignature. --- libsolidity/analysis/GlobalContext.cpp | 1 + libsolidity/analysis/ViewPureChecker.cpp | 7 +- libsolidity/ast/Types.cpp | 49 +++++++- libsolidity/ast/Types.h | 8 +- libsolidity/codegen/ExpressionCompiler.cpp | 102 +++++++++++++++++ test/libsolidity/SolidityEndToEndTest.cpp | 108 ++++++++++++++++++ .../SolidityNameAndTypeResolution.cpp | 16 +++ 7 files changed, 287 insertions(+), 4 deletions(-) diff --git a/libsolidity/analysis/GlobalContext.cpp b/libsolidity/analysis/GlobalContext.cpp index 6a858d36d..ef1a59fe3 100644 --- a/libsolidity/analysis/GlobalContext.cpp +++ b/libsolidity/analysis/GlobalContext.cpp @@ -35,6 +35,7 @@ namespace solidity GlobalContext::GlobalContext(): m_magicVariables(vector>{ + make_shared("abi", make_shared(MagicType::Kind::ABI)), make_shared("addmod", make_shared(strings{"uint256", "uint256", "uint256"}, strings{"uint256"}, FunctionType::Kind::AddMod, false, StateMutability::Pure)), make_shared("assert", make_shared(strings{"bool"}, strings{}, FunctionType::Kind::Assert, false, StateMutability::Pure)), make_shared("block", make_shared(MagicType::Kind::Block)), diff --git a/libsolidity/analysis/ViewPureChecker.cpp b/libsolidity/analysis/ViewPureChecker.cpp index 13c3ab68e..d98430120 100644 --- a/libsolidity/analysis/ViewPureChecker.cpp +++ b/libsolidity/analysis/ViewPureChecker.cpp @@ -305,10 +305,15 @@ void ViewPureChecker::endVisit(MemberAccess const& _memberAccess) mutability = StateMutability::View; break; case Type::Category::Magic: + { // we can ignore the kind of magic and only look at the name of the member - if (member != "data" && member != "sig" && member != "blockhash") + set static const pureMembers{ + "encode", "encodePacked", "encodeWithSelector", "encodeWithSignature", "data", "sig", "blockhash" + }; + if (!pureMembers.count(member)) mutability = StateMutability::View; break; + } case Type::Category::Struct: { if (_memberAccess.expression().annotation().type->dataStoredIn(DataLocation::Storage)) diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index 21353080b..68b127770 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -2375,7 +2375,11 @@ string FunctionType::richIdentifier() const case Kind::ByteArrayPush: id += "bytearraypush"; break; case Kind::ObjectCreation: id += "objectcreation"; break; case Kind::Assert: id += "assert"; break; - case Kind::Require: id += "require";break; + case Kind::Require: id += "require"; break; + case Kind::ABIEncode: id += "abiencode"; break; + case Kind::ABIEncodePacked: id += "abiencodepacked"; break; + case Kind::ABIEncodeWithSelector: id += "abiencodewithselector"; break; + case Kind::ABIEncodeWithSignature: id += "abiencodewithsignature"; break; default: solAssert(false, "Unknown function location."); break; } id += "_" + stateMutabilityToString(m_stateMutability); @@ -2996,6 +3000,8 @@ string MagicType::richIdentifier() const return "t_magic_message"; case Kind::Transaction: return "t_magic_transaction"; + case Kind::ABI: + return "t_magic_abi"; default: solAssert(false, "Unknown kind of magic"); } @@ -3036,6 +3042,45 @@ MemberList::MemberMap MagicType::nativeMembers(ContractDefinition const*) const {"origin", make_shared(160, IntegerType::Modifier::Address)}, {"gasprice", make_shared(256)} }); + case Kind::ABI: + return MemberList::MemberMap({ + {"encode", make_shared( + TypePointers(), + TypePointers{make_shared(DataLocation::Memory)}, + strings{}, + strings{}, + FunctionType::Kind::ABIEncode, + true, + StateMutability::Pure + )}, + {"encodePacked", make_shared( + TypePointers(), + TypePointers{make_shared(DataLocation::Memory)}, + strings{}, + strings{}, + FunctionType::Kind::ABIEncodePacked, + true, + StateMutability::Pure + )}, + {"encodeWithSelector", make_shared( + TypePointers{make_shared(4)}, + TypePointers{make_shared(DataLocation::Memory)}, + strings{}, + strings{}, + FunctionType::Kind::ABIEncodeWithSelector, + true, + StateMutability::Pure + )}, + {"encodeWithSignature", make_shared( + TypePointers{make_shared(DataLocation::Memory, true)}, + TypePointers{make_shared(DataLocation::Memory)}, + strings{}, + strings{}, + FunctionType::Kind::ABIEncodeWithSignature, + true, + StateMutability::Pure + )} + }); default: solAssert(false, "Unknown kind of magic."); } @@ -3051,6 +3096,8 @@ string MagicType::toString(bool) const return "msg"; case Kind::Transaction: return "tx"; + case Kind::ABI: + return "abi"; default: solAssert(false, "Unknown kind of magic."); } diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h index 05f506f1c..47556c4d7 100644 --- a/libsolidity/ast/Types.h +++ b/libsolidity/ast/Types.h @@ -914,6 +914,10 @@ public: ObjectCreation, ///< array creation using new Assert, ///< assert() Require, ///< require() + ABIEncode, + ABIEncodePacked, + ABIEncodeWithSelector, + ABIEncodeWithSignature, GasLeft ///< gasleft() }; @@ -1049,7 +1053,7 @@ public: ASTPointer documentation() const; /// true iff arguments are to be padded to multiples of 32 bytes for external calls - bool padArguments() const { return !(m_kind == Kind::SHA3 || m_kind == Kind::SHA256 || m_kind == Kind::RIPEMD160); } + bool padArguments() const { return !(m_kind == Kind::SHA3 || m_kind == Kind::SHA256 || m_kind == Kind::RIPEMD160 || m_kind == Kind::ABIEncodePacked); } bool takesArbitraryParameters() const { return m_arbitraryParameters; } bool gasSet() const { return m_gasSet; } bool valueSet() const { return m_valueSet; } @@ -1207,7 +1211,7 @@ private: class MagicType: public Type { public: - enum class Kind { Block, Message, Transaction }; + enum class Kind { Block, Message, Transaction, ABI }; virtual Category category() const override { return Category::Magic; } explicit MagicType(Kind _kind): m_kind(_kind) {} diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index 57d49ac66..051db5362 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -33,6 +33,8 @@ #include #include +#include + using namespace std; namespace dev @@ -912,6 +914,106 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) m_context << success; break; } + case FunctionType::Kind::ABIEncode: + case FunctionType::Kind::ABIEncodePacked: + case FunctionType::Kind::ABIEncodeWithSelector: + case FunctionType::Kind::ABIEncodeWithSignature: + { + bool const isPacked = function.kind() == FunctionType::Kind::ABIEncodePacked; + bool const hasSelectorOrSignature = + function.kind() == FunctionType::Kind::ABIEncodeWithSelector || + function.kind() == FunctionType::Kind::ABIEncodeWithSignature; + + TypePointers argumentTypes; + TypePointers targetTypes; + for (unsigned i = 0; i < arguments.size(); ++i) + { + arguments[i]->accept(*this); + // Do not keep the selector as part of the ABI encoded args + if (!hasSelectorOrSignature || i > 0) + argumentTypes.push_back(arguments[i]->annotation().type); + } + utils().fetchFreeMemoryPointer(); + // stack now: [] .. + + // adjust by 32(+4) bytes to accommodate the length(+selector) + m_context << u256(32 + (hasSelectorOrSignature ? 4 : 0)) << Instruction::ADD; + // stack now: [] .. + + if (isPacked) + { + solAssert(!function.padArguments(), ""); + utils().packedEncode(argumentTypes, TypePointers()); + } + else + { + solAssert(function.padArguments(), ""); + utils().abiEncode(argumentTypes, TypePointers()); + } + utils().fetchFreeMemoryPointer(); + // stack: [] + + // size is end minus start minus length slot + m_context.appendInlineAssembly(R"({ + mstore(mem_ptr, sub(sub(mem_end, mem_ptr), 0x20)) + })", {"mem_end", "mem_ptr"}); + m_context << Instruction::SWAP1; + utils().storeFreeMemoryPointer(); + // stack: [] + + if (hasSelectorOrSignature) + { + // stack: + solAssert(arguments.size() >= 1, ""); + TypePointer const& selectorType = arguments[0]->annotation().type; + utils().moveIntoStack(selectorType->sizeOnStack()); + TypePointer dataOnStack = selectorType; + // stack: + if (function.kind() == FunctionType::Kind::ABIEncodeWithSignature) + { + // hash the signature + if (auto const* stringType = dynamic_cast(selectorType.get())) + { + FixedHash<4> hash(dev::keccak256(stringType->value())); + m_context << (u256(FixedHash<4>::Arith(hash)) << (256 - 32)); + dataOnStack = make_shared(4); + } + else + { + utils().fetchFreeMemoryPointer(); + // stack: + utils().packedEncode(TypePointers{selectorType}, TypePointers()); + utils().toSizeAfterFreeMemoryPointer(); + m_context << Instruction::KECCAK256; + // stack: + + dataOnStack = make_shared(32); + } + } + else + { + solAssert(function.kind() == FunctionType::Kind::ABIEncodeWithSelector, ""); + } + + // Cleanup actually does not clean on shrinking the type. + utils().convertType(*dataOnStack, FixedBytesType(4), true); + + // stack: + + // load current memory, mask and combine the selector + string mask = formatNumber((u256(-1) >> 32)); + m_context.appendInlineAssembly(R"({ + let data_start := add(mem_ptr, 0x20) + let data := mload(data_start) + let mask := )" + mask + R"( + mstore(data_start, or(and(data, mask), and(selector, not(mask)))) + })", {"mem_ptr", "selector"}); + m_context << Instruction::POP; + } + + // stack now: + break; + } case FunctionType::Kind::GasLeft: m_context << Instruction::GAS; break; diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index 39f4b03e9..a5b990ace 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -3557,6 +3557,45 @@ BOOST_AUTO_TEST_CASE(empty_name_return_parameter) ABI_CHECK(callContractFunction("f(uint256)", 9), encodeArgs(9)); } +BOOST_AUTO_TEST_CASE(sha256_empty) +{ + char const* sourceCode = R"( + contract C { + function f() returns (bytes32) { + return sha256(); + } + } + )"; + compileAndRun(sourceCode); + ABI_CHECK(callContractFunction("f()"), fromHex("0xe3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855")); +} + +BOOST_AUTO_TEST_CASE(ripemd160_empty) +{ + char const* sourceCode = R"( + contract C { + function f() returns (bytes20) { + return ripemd160(); + } + } + )"; + compileAndRun(sourceCode); + ABI_CHECK(callContractFunction("f()"), fromHex("0x9c1185a5c5e9fc54612808977ee8f548b2258d31000000000000000000000000")); +} + +BOOST_AUTO_TEST_CASE(keccak256_empty) +{ + char const* sourceCode = R"( + contract C { + function f() returns (bytes32) { + return keccak256(); + } + } + )"; + compileAndRun(sourceCode); + ABI_CHECK(callContractFunction("f()"), fromHex("0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470")); +} + BOOST_AUTO_TEST_CASE(keccak256_multiple_arguments) { char const* sourceCode = R"( @@ -11052,6 +11091,75 @@ BOOST_AUTO_TEST_CASE(snark) BOOST_CHECK(callContractFunction("verifyTx()") == encodeArgs(true)); } +BOOST_AUTO_TEST_CASE(abi_encode) +{ + char const* sourceCode = R"( + contract C { + function f() returns (bytes) { + return abi.encode(1, 2); + } + function g() returns (bytes) { + return abi.encodePacked(uint256(1), uint256(2)); + } + } + )"; + compileAndRun(sourceCode, 0, "C"); + ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(0x20), u256(0x40), u256(1), u256(2))); + ABI_CHECK(callContractFunction("g()"), encodeArgs(u256(0x20), u256(0x40), u256(1), u256(2))); +} + +BOOST_AUTO_TEST_CASE(abi_encode_complex_call) +{ + char const* sourceCode = R"T( + contract C { + function f(uint8 a, string b, string c) { + require(a == 42); + require(bytes(b).length == 2); + require(bytes(b)[0] == 72); // 'H' + require(bytes(b)[1] == 101); // 'e' + require(keccak256(b) == keccak256(c)); + } + function g(uint8 a, string b) returns (bool) { + bytes request = abi.encode( + bytes4(keccak256(abi.encodePacked("f(uint8,string)"))), + a, + b, + "He" + ); + return this.call(request); + } + } + )T"; + compileAndRun(sourceCode, 0, "C"); + ABI_CHECK(callContractFunction("g(uint8,string)", u256(42), u256(0x40), u256(2), string("He")), encodeArgs(u256(1))); +} + +BOOST_AUTO_TEST_CASE(abi_encodewithselector_complex_call) +{ + char const* sourceCode = R"T( + contract C { + function f(uint8 a, string b, string c) { + require(a == 42); + require(bytes(b).length == 2); + require(bytes(b)[0] == 72); // 'H' + require(bytes(b)[1] == 101); // 'e' + require(keccak256(b) == keccak256(c)); + } + function g(uint8 a, string b) returns (bool) { + bytes request = abi.encodeWithSignature( + "f(uint8,string)", + a, + b, + "He" + ); + return this.call(request); + } + } + )T"; + compileAndRun(sourceCode, 0, "C"); + ABI_CHECK(callContractFunction("g(uint8,string)", u256(42), u256(0x40), u256(2), string("He")), encodeArgs(u256(1))); +} + BOOST_AUTO_TEST_CASE(staticcall_for_view_and_pure) { char const* sourceCode = R"( diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 18a414e00..7678bf4a9 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -7120,6 +7120,22 @@ BOOST_AUTO_TEST_CASE(tight_packing_literals) } )"; CHECK_WARNING(text, "The type of \"int_const 1\" was inferred as uint8."); + text = R"( + contract C { + function f() pure public returns (bytes) { + return abi.encode(1); + } + } + )"; + CHECK_SUCCESS_NO_WARNINGS(text); + text = R"( + contract C { + function f() pure public returns (bytes) { + return abi.encodePacked(1); + } + } + )"; + CHECK_WARNING(text, "The type of \"int_const 1\" was inferred as uint8."); } BOOST_AUTO_TEST_CASE(non_external_fallback) From 44c0d7ca5e1d97027e4f1e24fb02fd805eda578d Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 21 Mar 2018 14:45:32 +0100 Subject: [PATCH 2/5] Tests. --- test/libsolidity/SolidityEndToEndTest.cpp | 217 +++++++++++++++--- .../SolidityNameAndTypeResolution.cpp | 63 ----- .../syntaxTests/tight_packing_literals.sol | 25 ++ .../tight_packing_literals_fine.sol | 11 + 4 files changed, 216 insertions(+), 100 deletions(-) create mode 100644 test/libsolidity/syntaxTests/tight_packing_literals.sol create mode 100644 test/libsolidity/syntaxTests/tight_packing_literals_fine.sol diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index a5b990ace..8b0e3f83f 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -11095,69 +11095,212 @@ BOOST_AUTO_TEST_CASE(abi_encode) { char const* sourceCode = R"( contract C { - function f() returns (bytes) { + function f0() returns (bytes) { + return abi.encode(); + } + function f1() returns (bytes) { return abi.encode(1, 2); } - function g() returns (bytes) { - return abi.encodePacked(uint256(1), uint256(2)); + function f2() returns (bytes) { + string memory x = "abc"; + return abi.encode(1, x, 2); + } + function f3() returns (bytes r) { + // test that memory is properly allocated + string memory x = "abc"; + r = abi.encode(1, x, 2); + bytes memory y = "def"; + require(y[0] == "d"); + y[0] = "e"; + require(y[0] == "e"); } } )"; compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(0x20), u256(0x40), u256(1), u256(2))); - ABI_CHECK(callContractFunction("g()"), encodeArgs(u256(0x20), u256(0x40), u256(1), u256(2))); + ABI_CHECK(callContractFunction("f0()"), encodeArgs(0x20, 0)); + ABI_CHECK(callContractFunction("f1()"), encodeArgs(0x20, 0x40, 1, 2)); + ABI_CHECK(callContractFunction("f2()"), encodeArgs(0x20, 0xa0, 1, 0x60, 2, 3, "abc")); + ABI_CHECK(callContractFunction("f3()"), encodeArgs(0x20, 0xa0, 1, 0x60, 2, 3, "abc")); } -BOOST_AUTO_TEST_CASE(abi_encode_complex_call) +BOOST_AUTO_TEST_CASE(abi_encode_v2) +{ + char const* sourceCode = R"( + pragma experimental ABIEncoderV2; + contract C { + struct S { uint a; uint[] b; } + function f0() public pure returns (bytes) { + return abi.encode(); + } + function f1() public pure returns (bytes) { + return abi.encode(1, 2); + } + function f2() public pure returns (bytes) { + string memory x = "abc"; + return abi.encode(1, x, 2); + } + function f3() public pure returns (bytes r) { + // test that memory is properly allocated + string memory x = "abc"; + r = abi.encode(1, x, 2); + bytes memory y = "def"; + require(y[0] == "d"); + y[0] = "e"; + require(y[0] == "e"); + } + S s; + function f4() public view returns (bytes r) { + string memory x = "abc"; + s.a = 7; + s.b.push(2); + s.b.push(3); + r = abi.encode(1, x, s, 2); + bytes memory y = "def"; + require(y[0] == "d"); + y[0] = "e"; + require(y[0] == "e"); + } + } + )"; + compileAndRun(sourceCode, 0, "C"); + ABI_CHECK(callContractFunction("f0()"), encodeArgs(0x20, 0)); + ABI_CHECK(callContractFunction("f1()"), encodeArgs(0x20, 0x40, 1, 2)); + ABI_CHECK(callContractFunction("f2()"), encodeArgs(0x20, 0xa0, 1, 0x60, 2, 3, "abc")); + ABI_CHECK(callContractFunction("f3()"), encodeArgs(0x20, 0xa0, 1, 0x60, 2, 3, "abc")); + ABI_CHECK(callContractFunction("f4()"), encodeArgs(0x20, 0x160, 1, 0x80, 0xc0, 2, 3, "abc", 7, 0x40, 2, 2, 3)); +} + + +BOOST_AUTO_TEST_CASE(abi_encodePacked) +{ + char const* sourceCode = R"( + contract C { + function f0() public pure returns (bytes) { + return abi.encodePacked(); + } + function f1() public pure returns (bytes) { + return abi.encodePacked(uint8(1), uint8(2)); + } + function f2() public pure returns (bytes) { + string memory x = "abc"; + return abi.encodePacked(uint8(1), x, uint8(2)); + } + function f3() public pure returns (bytes r) { + // test that memory is properly allocated + string memory x = "abc"; + r = abi.encodePacked(uint8(1), x, uint8(2)); + bytes memory y = "def"; + require(y[0] == "d"); + y[0] = "e"; + require(y[0] == "e"); + } + } + )"; + compileAndRun(sourceCode, 0, "C"); + ABI_CHECK(callContractFunction("f0()"), encodeArgs(0x20, 0)); + ABI_CHECK(callContractFunction("f1()"), encodeArgs(0x20, 2, "\x01\x02")); + ABI_CHECK(callContractFunction("f2()"), encodeArgs(0x20, 5, "\x01" "abc" "\x02")); + ABI_CHECK(callContractFunction("f3()"), encodeArgs(0x20, 5, "\x01" "abc" "\x02")); +} + +BOOST_AUTO_TEST_CASE(abi_encode_with_selector) +{ + char const* sourceCode = R"( + contract C { + function f0() public pure returns (bytes) { + return abi.encodeWithSelector(0x12345678); + } + function f1() public pure returns (bytes) { + return abi.encodeWithSelector(0x12345678, "abc"); + } + function f2() public pure returns (bytes) { + bytes4 x = 0x12345678; + return abi.encodeWithSelector(x, "abc"); + } + function f3() public pure returns (bytes) { + bytes4 x = 0x12345678; + return abi.encodeWithSelector(x, uint(-1)); + } + } + )"; + compileAndRun(sourceCode, 0, "C"); + ABI_CHECK(callContractFunction("f0()"), encodeArgs(0x20, 4, "\x12\x34\x56\x78")); + bytes expectation; + expectation = encodeArgs(0x20, 4 + 0x60) + bytes{0x12, 0x34, 0x56, 0x78} + encodeArgs(0x20, 3, "abc") + bytes(0x20 - 4); + ABI_CHECK(callContractFunction("f1()"), expectation); + expectation = encodeArgs(0x20, 4 + 0x60) + bytes{0x12, 0x34, 0x56, 0x78} + encodeArgs(0x20, 3, "abc") + bytes(0x20 - 4); + ABI_CHECK(callContractFunction("f2()"), expectation); + expectation = encodeArgs(0x20, 4 + 0x20) + bytes{0x12, 0x34, 0x56, 0x78} + encodeArgs(u256(-1)) + bytes(0x20 - 4); + ABI_CHECK(callContractFunction("f3()"), expectation); +} + +BOOST_AUTO_TEST_CASE(abi_encode_with_signature) { char const* sourceCode = R"T( contract C { - function f(uint8 a, string b, string c) { - require(a == 42); - require(bytes(b).length == 2); - require(bytes(b)[0] == 72); // 'H' - require(bytes(b)[1] == 101); // 'e' - require(keccak256(b) == keccak256(c)); + function f0() public pure returns (bytes) { + return abi.encodeWithSignature("f(uint256)"); } - function g(uint8 a, string b) returns (bool) { - bytes request = abi.encode( - bytes4(keccak256(abi.encodePacked("f(uint8,string)"))), - a, - b, - "He" - ); - return this.call(request); + function f1() public pure returns (bytes) { + string memory x = "f(uint256)"; + return abi.encodeWithSignature(x, "abc"); + } + string xstor; + function f1s() public returns (bytes) { + xstor = "f(uint256)"; + return abi.encodeWithSignature(xstor, "abc"); + } + function f2() public pure returns (bytes r, uint[] ar) { + string memory x = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."; + uint[] memory y = new uint[](4); + y[0] = uint(-1); + y[1] = uint(-2); + y[2] = uint(-3); + y[3] = uint(-4); + r = abi.encodeWithSignature(x, y); + // The hash uses temporary memory. This allocation re-uses the memory + // and should initialize it properly. + ar = new uint[](2); } } )T"; compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("g(uint8,string)", u256(42), u256(0x40), u256(2), string("He")), encodeArgs(u256(1))); + ABI_CHECK(callContractFunction("f0()"), encodeArgs(0x20, 4, "\xb3\xde\x64\x8b")); + bytes expectation; + expectation = encodeArgs(0x20, 4 + 0x60) + bytes{0xb3, 0xde, 0x64, 0x8b} + encodeArgs(0x20, 3, "abc") + bytes(0x20 - 4); + ABI_CHECK(callContractFunction("f1()"), expectation); + ABI_CHECK(callContractFunction("f1s()"), expectation); + expectation = + encodeArgs(0x40, 0x140, 4 + 0xc0) + + (bytes{0xe9, 0xc9, 0x21, 0xcd} + encodeArgs(0x20, 4, u256(-1), u256(-2), u256(-3), u256(-4)) + bytes(0x20 - 4)) + + encodeArgs(2, 0, 0); + ABI_CHECK(callContractFunction("f2()"), expectation); } -BOOST_AUTO_TEST_CASE(abi_encodewithselector_complex_call) +BOOST_AUTO_TEST_CASE(abi_encode_call) { char const* sourceCode = R"T( contract C { - function f(uint8 a, string b, string c) { - require(a == 42); - require(bytes(b).length == 2); - require(bytes(b)[0] == 72); // 'H' - require(bytes(b)[1] == 101); // 'e' - require(keccak256(b) == keccak256(c)); + bool x; + function c(uint a, uint[] b) public { + require(a == 5); + require(b.length == 2); + require(b[0] == 6); + require(b[1] == 7); + x = true; } - function g(uint8 a, string b) returns (bool) { - bytes request = abi.encodeWithSignature( - "f(uint8,string)", - a, - b, - "He" - ); - return this.call(request); + function f() public returns (bool) { + uint a = 5; + uint[] memory b = new uint[](2); + b[0] = 6; + b[1] = 7; + require(this.call(abi.encodeWithSignature("c(uint256,uint256[])", a, b))); + return x; } } )T"; compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("g(uint8,string)", u256(42), u256(0x40), u256(2), string("He")), encodeArgs(u256(1))); + ABI_CHECK(callContractFunction("f()"), encodeArgs(true)); } BOOST_AUTO_TEST_CASE(staticcall_for_view_and_pure) diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 7678bf4a9..a301e6d2e 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -7075,69 +7075,6 @@ BOOST_AUTO_TEST_CASE(reject_interface_constructors) CHECK_ERROR(text, TypeError, "Wrong argument count for constructor call: 1 arguments given but expected 0."); } -BOOST_AUTO_TEST_CASE(tight_packing_literals) -{ - char const* text = R"( - contract C { - function f() pure public returns (bytes32) { - return keccak256(1); - } - } - )"; - CHECK_WARNING(text, "The type of \"int_const 1\" was inferred as uint8."); - text = R"( - contract C { - function f() pure public returns (bytes32) { - return keccak256(uint8(1)); - } - } - )"; - CHECK_SUCCESS_NO_WARNINGS(text); - text = R"( - contract C { - function f() pure public returns (bytes32) { - return sha3(1); - } - } - )"; - CHECK_WARNING_ALLOW_MULTI(text, (std::vector{ - "The type of \"int_const 1\" was inferred as uint8.", - "\"sha3\" has been deprecated in favour of \"keccak256\"" - })); - text = R"( - contract C { - function f() pure public returns (bytes32) { - return sha256(1); - } - } - )"; - CHECK_WARNING(text, "The type of \"int_const 1\" was inferred as uint8."); - text = R"( - contract C { - function f() pure public returns (bytes32) { - return ripemd160(1); - } - } - )"; - CHECK_WARNING(text, "The type of \"int_const 1\" was inferred as uint8."); - text = R"( - contract C { - function f() pure public returns (bytes) { - return abi.encode(1); - } - } - )"; - CHECK_SUCCESS_NO_WARNINGS(text); - text = R"( - contract C { - function f() pure public returns (bytes) { - return abi.encodePacked(1); - } - } - )"; - CHECK_WARNING(text, "The type of \"int_const 1\" was inferred as uint8."); -} - BOOST_AUTO_TEST_CASE(non_external_fallback) { char const* text = R"( diff --git a/test/libsolidity/syntaxTests/tight_packing_literals.sol b/test/libsolidity/syntaxTests/tight_packing_literals.sol new file mode 100644 index 000000000..8258a8a6c --- /dev/null +++ b/test/libsolidity/syntaxTests/tight_packing_literals.sol @@ -0,0 +1,25 @@ +contract C { + function f() pure public returns (bytes32) { + return keccak256(1); + } + function g() pure public returns (bytes32) { + return sha3(1); + } + function h() pure public returns (bytes32) { + return sha256(1); + } + function j() pure public returns (bytes32) { + return ripemd160(1); + } + function k() pure public returns (bytes) { + return abi.encodePacked(1); + } +} + +// ---- +// Warning: (87-88): The type of "int_const 1" was inferred as uint8. This is probably not desired. Use an explicit type to silence this warning. +// Warning: (161-168): "sha3" has been deprecated in favour of "keccak256" +// Warning: (166-167): The type of "int_const 1" was inferred as uint8. This is probably not desired. Use an explicit type to silence this warning. +// Warning: (247-248): The type of "int_const 1" was inferred as uint8. This is probably not desired. Use an explicit type to silence this warning. +// Warning: (331-332): The type of "int_const 1" was inferred as uint8. This is probably not desired. Use an explicit type to silence this warning. +// Warning: (420-421): The type of "int_const 1" was inferred as uint8. This is probably not desired. Use an explicit type to silence this warning. diff --git a/test/libsolidity/syntaxTests/tight_packing_literals_fine.sol b/test/libsolidity/syntaxTests/tight_packing_literals_fine.sol new file mode 100644 index 000000000..46407f718 --- /dev/null +++ b/test/libsolidity/syntaxTests/tight_packing_literals_fine.sol @@ -0,0 +1,11 @@ +contract C { + function f() pure public returns (bytes32) { + return keccak256(uint8(1)); + } + function g() pure public returns (bytes) { + return abi.encode(1); + } + function h() pure public returns (bytes) { + return abi.encodePacked(uint8(1)); + } +} From c4a6a63f362f478414ac886ed6e5e159038460aa Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 21 Mar 2018 18:38:06 +0100 Subject: [PATCH 3/5] Tests for view and pure. --- .../viewPure/view_pure_abi_encode.sol | 8 +++++ .../view_pure_abi_encode_arguments.sol | 36 +++++++++++++++++++ 2 files changed, 44 insertions(+) create mode 100644 test/libsolidity/syntaxTests/viewPure/view_pure_abi_encode.sol create mode 100644 test/libsolidity/syntaxTests/viewPure/view_pure_abi_encode_arguments.sol diff --git a/test/libsolidity/syntaxTests/viewPure/view_pure_abi_encode.sol b/test/libsolidity/syntaxTests/viewPure/view_pure_abi_encode.sol new file mode 100644 index 000000000..ca7db42e4 --- /dev/null +++ b/test/libsolidity/syntaxTests/viewPure/view_pure_abi_encode.sol @@ -0,0 +1,8 @@ +contract C { + function f() pure public returns (bytes r) { + r = abi.encode(1, 2); + r = abi.encodePacked(f()); + r = abi.encodeWithSelector(0x12345678, 1); + r = abi.encodeWithSignature("f(uint256)", 4); + } +} diff --git a/test/libsolidity/syntaxTests/viewPure/view_pure_abi_encode_arguments.sol b/test/libsolidity/syntaxTests/viewPure/view_pure_abi_encode_arguments.sol new file mode 100644 index 000000000..547362c3f --- /dev/null +++ b/test/libsolidity/syntaxTests/viewPure/view_pure_abi_encode_arguments.sol @@ -0,0 +1,36 @@ +contract C { + uint x; + function gView() public view returns (uint) { return x; } + function gNonPayable() public returns (uint) { x = 4; return 0; } + + function f1() view public returns (bytes) { + return abi.encode(gView()); + } + function f2() view public returns (bytes) { + return abi.encodePacked(gView()); + } + function f3() view public returns (bytes) { + return abi.encodeWithSelector(0x12345678, gView()); + } + function f4() view public returns (bytes) { + return abi.encodeWithSignature("f(uint256)", gView()); + } + function g1() public returns (bytes) { + return abi.encode(gNonPayable()); + } + function g2() public returns (bytes) { + return abi.encodePacked(gNonPayable()); + } + function g3() public returns (bytes) { + return abi.encodeWithSelector(0x12345678, gNonPayable()); + } + function g4() public returns (bytes) { + return abi.encodeWithSignature("f(uint256)", gNonPayable()); + } + // This will generate the only warning. + function check() public returns (bytes) { + return abi.encode(2); + } +} +// ---- +// Warning: (1044-1121): Function state mutability can be restricted to pure From 7343c4028365d3fbc9fa46fca6078971a2bed426 Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 21 Mar 2018 19:07:15 +0100 Subject: [PATCH 4/5] Check partial function parameters if rest is arbitrary. --- libsolidity/analysis/TypeChecker.cpp | 17 ++++++++++++++--- ...ary_parameters_but_restricted_first_type.sol | 13 +++++++++++++ 2 files changed, 27 insertions(+), 3 deletions(-) create mode 100644 test/libsolidity/syntaxTests/functionCalls/arbitrary_parameters_but_restricted_first_type.sol diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 8b57fc158..df08598c9 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -1688,7 +1688,19 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) } } - if (!functionType->takesArbitraryParameters() && parameterTypes.size() != arguments.size()) + if (functionType->takesArbitraryParameters() && arguments.size() < parameterTypes.size()) + { + solAssert(_functionCall.annotation().kind == FunctionCallKind::FunctionCall, ""); + m_errorReporter.typeError( + _functionCall.location(), + "Need at least " + + toString(parameterTypes.size()) + + " arguments for function call, but provided only " + + toString(arguments.size()) + + "." + ); + } + else if (!functionType->takesArbitraryParameters() && parameterTypes.size() != arguments.size()) { bool isStructConstructorCall = _functionCall.annotation().kind == FunctionCallKind::StructConstructorCall; @@ -1711,11 +1723,10 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) } else if (isPositionalCall) { - // call by positional arguments for (size_t i = 0; i < arguments.size(); ++i) { auto const& argType = type(*arguments[i]); - if (functionType->takesArbitraryParameters()) + if (functionType->takesArbitraryParameters() && i >= parameterTypes.size()) { bool errored = false; if (auto t = dynamic_cast(argType.get())) diff --git a/test/libsolidity/syntaxTests/functionCalls/arbitrary_parameters_but_restricted_first_type.sol b/test/libsolidity/syntaxTests/functionCalls/arbitrary_parameters_but_restricted_first_type.sol new file mode 100644 index 000000000..94da5881d --- /dev/null +++ b/test/libsolidity/syntaxTests/functionCalls/arbitrary_parameters_but_restricted_first_type.sol @@ -0,0 +1,13 @@ +contract C { + function f() pure public { + abi.encodeWithSelector(); + abi.encodeWithSignature(); + abi.encodeWithSelector(uint(2), 2); + abi.encodeWithSignature(uint(2), 2); + } +} +// ---- +// TypeError: (52-76): Need at least 1 arguments for function call, but provided only 0. +// TypeError: (86-111): Need at least 1 arguments for function call, but provided only 0. +// TypeError: (144-151): Invalid type for argument in function call. Invalid implicit conversion from uint256 to bytes4 requested. +// TypeError: (189-196): Invalid type for argument in function call. Invalid implicit conversion from uint256 to string memory requested. From 75b88286667690ffb4a5e079665ed8b70bcaeb87 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 5 Apr 2018 15:52:25 +0200 Subject: [PATCH 5/5] Allow struct encoding with new encoder. --- libsolidity/analysis/TypeChecker.cpp | 25 +++-- test/libsolidity/SolidityEndToEndTest.cpp | 106 ++++++++++++++++++ .../specialFunctions/abi_encode_structs.sol | 17 +++ .../abi_encode_structs_abiv2.sol | 18 +++ ...ypes_with_unspecified_encoding_structs.sol | 9 +- ...ith_unspecified_encoding_structs_abiv2.sol | 16 +++ 6 files changed, 179 insertions(+), 12 deletions(-) create mode 100644 test/libsolidity/syntaxTests/specialFunctions/abi_encode_structs.sol create mode 100644 test/libsolidity/syntaxTests/specialFunctions/abi_encode_structs_abiv2.sol create mode 100644 test/libsolidity/syntaxTests/specialFunctions/types_with_unspecified_encoding_structs_abiv2.sol diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index df08598c9..b95fee38a 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -1723,6 +1723,8 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) } else if (isPositionalCall) { + bool const abiEncodeV2 = m_scope->sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::ABIEncoderV2); + for (size_t i = 0; i < arguments.size(); ++i) { auto const& argType = type(*arguments[i]); @@ -1735,13 +1737,22 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) m_errorReporter.typeError(arguments[i]->location(), "Invalid rational number (too large or division by zero)."); errored = true; } - if (!errored && !( - argType->mobileType() && - argType->mobileType()->interfaceType(false) && - argType->mobileType()->interfaceType(false)->encodingType() && - !(dynamic_cast(argType->mobileType()->interfaceType(false)->encodingType().get())) - )) - m_errorReporter.typeError(arguments[i]->location(), "This type cannot be encoded."); + if (!errored) + { + TypePointer encodingType; + if ( + argType->mobileType() && + argType->mobileType()->interfaceType(false) && + argType->mobileType()->interfaceType(false)->encodingType() + ) + encodingType = argType->mobileType()->interfaceType(false)->encodingType(); + // Structs are fine as long as ABIV2 is activated and we do not do packed encoding. + if (!encodingType || ( + dynamic_cast(encodingType.get()) && + !(abiEncodeV2 && functionType->padArguments()) + )) + m_errorReporter.typeError(arguments[i]->location(), "This type cannot be encoded."); + } } else if (!type(*arguments[i])->isImplicitlyConvertibleTo(*parameterTypes[i])) m_errorReporter.typeError( diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index 8b0e3f83f..49cb5d4d3 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -11234,6 +11234,53 @@ BOOST_AUTO_TEST_CASE(abi_encode_with_selector) ABI_CHECK(callContractFunction("f3()"), expectation); } +BOOST_AUTO_TEST_CASE(abi_encode_with_selectorv2) +{ + char const* sourceCode = R"( + pragma experimental ABIEncoderV2; + contract C { + function f0() public pure returns (bytes) { + return abi.encodeWithSelector(0x12345678); + } + function f1() public pure returns (bytes) { + return abi.encodeWithSelector(0x12345678, "abc"); + } + function f2() public pure returns (bytes) { + bytes4 x = 0x12345678; + return abi.encodeWithSelector(x, "abc"); + } + function f3() public pure returns (bytes) { + bytes4 x = 0x12345678; + return abi.encodeWithSelector(x, uint(-1)); + } + struct S { uint a; string b; uint16 c; } + function f4() public pure returns (bytes) { + bytes4 x = 0x12345678; + S memory s; + s.a = 0x1234567; + s.b = "Lorem ipsum dolor sit ethereum........"; + s.c = 0x1234; + return abi.encodeWithSelector(x, uint(-1), s, uint(3)); + } + } + )"; + compileAndRun(sourceCode, 0, "C"); + ABI_CHECK(callContractFunction("f0()"), encodeArgs(0x20, 4, "\x12\x34\x56\x78")); + bytes expectation; + expectation = encodeArgs(0x20, 4 + 0x60) + bytes{0x12, 0x34, 0x56, 0x78} + encodeArgs(0x20, 3, "abc") + bytes(0x20 - 4); + ABI_CHECK(callContractFunction("f1()"), expectation); + expectation = encodeArgs(0x20, 4 + 0x60) + bytes{0x12, 0x34, 0x56, 0x78} + encodeArgs(0x20, 3, "abc") + bytes(0x20 - 4); + ABI_CHECK(callContractFunction("f2()"), expectation); + expectation = encodeArgs(0x20, 4 + 0x20) + bytes{0x12, 0x34, 0x56, 0x78} + encodeArgs(u256(-1)) + bytes(0x20 - 4); + ABI_CHECK(callContractFunction("f3()"), expectation); + expectation = + encodeArgs(0x20, 4 + 0x120) + + bytes{0x12, 0x34, 0x56, 0x78} + + encodeArgs(u256(-1), 0x60, u256(3), 0x1234567, 0x60, 0x1234, 38, "Lorem ipsum dolor sit ethereum........") + + bytes(0x20 - 4); + ABI_CHECK(callContractFunction("f4()"), expectation); +} + BOOST_AUTO_TEST_CASE(abi_encode_with_signature) { char const* sourceCode = R"T( @@ -11277,6 +11324,65 @@ BOOST_AUTO_TEST_CASE(abi_encode_with_signature) ABI_CHECK(callContractFunction("f2()"), expectation); } +BOOST_AUTO_TEST_CASE(abi_encode_with_signaturev2) +{ + char const* sourceCode = R"T( + pragma experimental ABIEncoderV2; + contract C { + function f0() public pure returns (bytes) { + return abi.encodeWithSignature("f(uint256)"); + } + function f1() public pure returns (bytes) { + string memory x = "f(uint256)"; + return abi.encodeWithSignature(x, "abc"); + } + string xstor; + function f1s() public returns (bytes) { + xstor = "f(uint256)"; + return abi.encodeWithSignature(xstor, "abc"); + } + function f2() public pure returns (bytes r, uint[] ar) { + string memory x = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."; + uint[] memory y = new uint[](4); + y[0] = uint(-1); + y[1] = uint(-2); + y[2] = uint(-3); + y[3] = uint(-4); + r = abi.encodeWithSignature(x, y); + // The hash uses temporary memory. This allocation re-uses the memory + // and should initialize it properly. + ar = new uint[](2); + } + struct S { uint a; string b; uint16 c; } + function f4() public pure returns (bytes) { + bytes4 x = 0x12345678; + S memory s; + s.a = 0x1234567; + s.b = "Lorem ipsum dolor sit ethereum........"; + s.c = 0x1234; + return abi.encodeWithSignature(s.b, uint(-1), s, uint(3)); + } + } + )T"; + compileAndRun(sourceCode, 0, "C"); + ABI_CHECK(callContractFunction("f0()"), encodeArgs(0x20, 4, "\xb3\xde\x64\x8b")); + bytes expectation; + expectation = encodeArgs(0x20, 4 + 0x60) + bytes{0xb3, 0xde, 0x64, 0x8b} + encodeArgs(0x20, 3, "abc") + bytes(0x20 - 4); + ABI_CHECK(callContractFunction("f1()"), expectation); + ABI_CHECK(callContractFunction("f1s()"), expectation); + expectation = + encodeArgs(0x40, 0x140, 4 + 0xc0) + + (bytes{0xe9, 0xc9, 0x21, 0xcd} + encodeArgs(0x20, 4, u256(-1), u256(-2), u256(-3), u256(-4)) + bytes(0x20 - 4)) + + encodeArgs(2, 0, 0); + ABI_CHECK(callContractFunction("f2()"), expectation); + expectation = + encodeArgs(0x20, 4 + 0x120) + + bytes{0x7c, 0x79, 0x30, 0x02} + + encodeArgs(u256(-1), 0x60, u256(3), 0x1234567, 0x60, 0x1234, 38, "Lorem ipsum dolor sit ethereum........") + + bytes(0x20 - 4); + ABI_CHECK(callContractFunction("f4()"), expectation); +} + BOOST_AUTO_TEST_CASE(abi_encode_call) { char const* sourceCode = R"T( diff --git a/test/libsolidity/syntaxTests/specialFunctions/abi_encode_structs.sol b/test/libsolidity/syntaxTests/specialFunctions/abi_encode_structs.sol new file mode 100644 index 000000000..d9eebee42 --- /dev/null +++ b/test/libsolidity/syntaxTests/specialFunctions/abi_encode_structs.sol @@ -0,0 +1,17 @@ +contract C { + struct S { uint x; } + S s; + struct T { uint y; } + T t; + function f() public view { + abi.encode(s, t); + } + function g() public view { + abi.encodePacked(s, t); + } +} +// ---- +// TypeError: (131-132): This type cannot be encoded. +// TypeError: (134-135): This type cannot be encoded. +// TypeError: (200-201): This type cannot be encoded. +// TypeError: (203-204): This type cannot be encoded. diff --git a/test/libsolidity/syntaxTests/specialFunctions/abi_encode_structs_abiv2.sol b/test/libsolidity/syntaxTests/specialFunctions/abi_encode_structs_abiv2.sol new file mode 100644 index 000000000..d6cf60e47 --- /dev/null +++ b/test/libsolidity/syntaxTests/specialFunctions/abi_encode_structs_abiv2.sol @@ -0,0 +1,18 @@ +pragma experimental ABIEncoderV2; + +contract C { + struct S { uint x; } + S s; + struct T { uint y; } + T t; + function f() public view { + abi.encode(s, t); + } + function g() public view { + abi.encodePacked(s, t); + } +} +// ---- +// Warning: (0-33): Experimental features are turned on. Do not use experimental features on live deployments. +// TypeError: (235-236): This type cannot be encoded. +// TypeError: (238-239): This type cannot be encoded. diff --git a/test/libsolidity/syntaxTests/specialFunctions/types_with_unspecified_encoding_structs.sol b/test/libsolidity/syntaxTests/specialFunctions/types_with_unspecified_encoding_structs.sol index cc354819f..fa9102607 100644 --- a/test/libsolidity/syntaxTests/specialFunctions/types_with_unspecified_encoding_structs.sol +++ b/test/libsolidity/syntaxTests/specialFunctions/types_with_unspecified_encoding_structs.sol @@ -1,14 +1,13 @@ contract C { struct S { uint x; } S s; - struct T { } + struct T { uint y; } T t; - function f() public pure { + function f() public view { bytes32 a = sha256(s, t); a; } } // ---- -// Warning: (51-63): Defining empty structs is deprecated. -// TypeError: (131-132): This type cannot be encoded. -// TypeError: (134-135): This type cannot be encoded. +// TypeError: (139-140): This type cannot be encoded. +// TypeError: (142-143): This type cannot be encoded. diff --git a/test/libsolidity/syntaxTests/specialFunctions/types_with_unspecified_encoding_structs_abiv2.sol b/test/libsolidity/syntaxTests/specialFunctions/types_with_unspecified_encoding_structs_abiv2.sol new file mode 100644 index 000000000..1187ce4a7 --- /dev/null +++ b/test/libsolidity/syntaxTests/specialFunctions/types_with_unspecified_encoding_structs_abiv2.sol @@ -0,0 +1,16 @@ +pragma experimental ABIEncoderV2; + +contract C { + struct S { uint x; } + S s; + struct T { uint y; } + T t; + function f() public view { + bytes32 a = sha256(s, t); + a; + } +} +// ---- +// Warning: (0-33): Experimental features are turned on. Do not use experimental features on live deployments. +// TypeError: (174-175): This type cannot be encoded. +// TypeError: (177-178): This type cannot be encoded.