diff options
author | chriseth <chris@ethereum.org> | 2018-04-13 02:55:03 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-04-13 02:55:03 +0800 |
commit | 44416d1ac65b2cfae4bb15d39bc84b1a78211baa (patch) | |
tree | 7b8ee302e1c4f0604f29d2fe21a2d0393cc60d11 /libsolidity | |
parent | 7453ff0f3a94ce4ddce55cdbb77146dd75a01e1c (diff) | |
parent | 75b88286667690ffb4a5e079665ed8b70bcaeb87 (diff) | |
download | dexon-solidity-44416d1ac65b2cfae4bb15d39bc84b1a78211baa.tar.gz dexon-solidity-44416d1ac65b2cfae4bb15d39bc84b1a78211baa.tar.zst dexon-solidity-44416d1ac65b2cfae4bb15d39bc84b1a78211baa.zip |
Merge pull request #2980 from ethereum/abi-api
Add abi.encode and abi.encodePacked
Diffstat (limited to 'libsolidity')
-rw-r--r-- | libsolidity/analysis/GlobalContext.cpp | 1 | ||||
-rw-r--r-- | libsolidity/analysis/TypeChecker.cpp | 42 | ||||
-rw-r--r-- | libsolidity/analysis/ViewPureChecker.cpp | 7 | ||||
-rw-r--r-- | libsolidity/ast/Types.cpp | 49 | ||||
-rw-r--r-- | libsolidity/ast/Types.h | 8 | ||||
-rw-r--r-- | libsolidity/codegen/ExpressionCompiler.cpp | 102 |
6 files changed, 195 insertions, 14 deletions
diff --git a/libsolidity/analysis/GlobalContext.cpp b/libsolidity/analysis/GlobalContext.cpp index 6a858d36..ef1a59fe 100644 --- a/libsolidity/analysis/GlobalContext.cpp +++ b/libsolidity/analysis/GlobalContext.cpp @@ -35,6 +35,7 @@ namespace solidity GlobalContext::GlobalContext(): m_magicVariables(vector<shared_ptr<MagicVariableDeclaration const>>{ + make_shared<MagicVariableDeclaration>("abi", make_shared<MagicType>(MagicType::Kind::ABI)), make_shared<MagicVariableDeclaration>("addmod", make_shared<FunctionType>(strings{"uint256", "uint256", "uint256"}, strings{"uint256"}, FunctionType::Kind::AddMod, false, StateMutability::Pure)), make_shared<MagicVariableDeclaration>("assert", make_shared<FunctionType>(strings{"bool"}, strings{}, FunctionType::Kind::Assert, false, StateMutability::Pure)), make_shared<MagicVariableDeclaration>("block", make_shared<MagicType>(MagicType::Kind::Block)), diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 8b57fc15..b95fee38 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,12 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) } else if (isPositionalCall) { - // call by positional arguments + 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]); - if (functionType->takesArbitraryParameters()) + if (functionType->takesArbitraryParameters() && i >= parameterTypes.size()) { bool errored = false; if (auto t = dynamic_cast<RationalNumberType const*>(argType.get())) @@ -1724,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<StructType const*>(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<StructType const*>(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/libsolidity/analysis/ViewPureChecker.cpp b/libsolidity/analysis/ViewPureChecker.cpp index 13c3ab68..d9843012 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<string> 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 21353080..68b12777 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<IntegerType>(160, IntegerType::Modifier::Address)}, {"gasprice", make_shared<IntegerType>(256)} }); + case Kind::ABI: + return MemberList::MemberMap({ + {"encode", make_shared<FunctionType>( + TypePointers(), + TypePointers{make_shared<ArrayType>(DataLocation::Memory)}, + strings{}, + strings{}, + FunctionType::Kind::ABIEncode, + true, + StateMutability::Pure + )}, + {"encodePacked", make_shared<FunctionType>( + TypePointers(), + TypePointers{make_shared<ArrayType>(DataLocation::Memory)}, + strings{}, + strings{}, + FunctionType::Kind::ABIEncodePacked, + true, + StateMutability::Pure + )}, + {"encodeWithSelector", make_shared<FunctionType>( + TypePointers{make_shared<FixedBytesType>(4)}, + TypePointers{make_shared<ArrayType>(DataLocation::Memory)}, + strings{}, + strings{}, + FunctionType::Kind::ABIEncodeWithSelector, + true, + StateMutability::Pure + )}, + {"encodeWithSignature", make_shared<FunctionType>( + TypePointers{make_shared<ArrayType>(DataLocation::Memory, true)}, + TypePointers{make_shared<ArrayType>(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 ecfc2333..345f84a1 100644 --- a/libsolidity/ast/Types.h +++ b/libsolidity/ast/Types.h @@ -917,6 +917,10 @@ public: ObjectCreation, ///< array creation using new Assert, ///< assert() Require, ///< require() + ABIEncode, + ABIEncodePacked, + ABIEncodeWithSelector, + ABIEncodeWithSignature, GasLeft ///< gasleft() }; @@ -1052,7 +1056,7 @@ public: ASTPointer<ASTString> 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; } @@ -1210,7 +1214,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 57d49ac6..051db536 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -33,6 +33,8 @@ #include <libsolidity/codegen/LValue.h> #include <libevmasm/GasMeter.h> +#include <libdevcore/Whiskers.h> + 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: [<selector>] <arg1> .. <argN> <free_mem> + + // adjust by 32(+4) bytes to accommodate the length(+selector) + m_context << u256(32 + (hasSelectorOrSignature ? 4 : 0)) << Instruction::ADD; + // stack now: [<selector>] <arg1> .. <argN> <data_encoding_area_start> + + if (isPacked) + { + solAssert(!function.padArguments(), ""); + utils().packedEncode(argumentTypes, TypePointers()); + } + else + { + solAssert(function.padArguments(), ""); + utils().abiEncode(argumentTypes, TypePointers()); + } + utils().fetchFreeMemoryPointer(); + // stack: [<selector>] <data_encoding_area_end> <bytes_memory_ptr> + + // 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: [<selector>] <memory ptr> + + if (hasSelectorOrSignature) + { + // stack: <selector> <memory pointer> + solAssert(arguments.size() >= 1, ""); + TypePointer const& selectorType = arguments[0]->annotation().type; + utils().moveIntoStack(selectorType->sizeOnStack()); + TypePointer dataOnStack = selectorType; + // stack: <memory pointer> <selector> + if (function.kind() == FunctionType::Kind::ABIEncodeWithSignature) + { + // hash the signature + if (auto const* stringType = dynamic_cast<StringLiteralType const*>(selectorType.get())) + { + FixedHash<4> hash(dev::keccak256(stringType->value())); + m_context << (u256(FixedHash<4>::Arith(hash)) << (256 - 32)); + dataOnStack = make_shared<FixedBytesType>(4); + } + else + { + utils().fetchFreeMemoryPointer(); + // stack: <memory pointer> <selector> <free mem ptr> + utils().packedEncode(TypePointers{selectorType}, TypePointers()); + utils().toSizeAfterFreeMemoryPointer(); + m_context << Instruction::KECCAK256; + // stack: <memory pointer> <hash> + + dataOnStack = make_shared<FixedBytesType>(32); + } + } + else + { + solAssert(function.kind() == FunctionType::Kind::ABIEncodeWithSelector, ""); + } + + // Cleanup actually does not clean on shrinking the type. + utils().convertType(*dataOnStack, FixedBytesType(4), true); + + // stack: <memory pointer> <selector> + + // 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: <memory pointer> + break; + } case FunctionType::Kind::GasLeft: m_context << Instruction::GAS; break; |