mirror of
				https://github.com/ethereum/solidity
				synced 2023-10-03 13:03:40 +00:00 
			
		
		
		
	Merge pull request #6093 from ethereum/mapping-string-keys
Allow dynamic types in public mappings
This commit is contained in:
		
						commit
						ebec54aacb
					
				| @ -1,6 +1,7 @@ | |||||||
| ### 0.5.5 (unreleased) | ### 0.5.5 (unreleased) | ||||||
| 
 | 
 | ||||||
| Language Features: | Language Features: | ||||||
|  |  * Add support for accessors for mappings with string or byte key types | ||||||
|  * Meta programming: Provide access to the name of contracts via ``type(C).name``. |  * Meta programming: Provide access to the name of contracts via ``type(C).name``. | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -511,14 +511,6 @@ bool TypeChecker::visit(VariableDeclaration const& _variable) | |||||||
| 			) | 			) | ||||||
| 				m_errorReporter.typeError(_variable.location(), "Array is too large to be encoded."); | 				m_errorReporter.typeError(_variable.location(), "Array is too large to be encoded."); | ||||||
| 		break; | 		break; | ||||||
| 	case Type::Category::Mapping: |  | ||||||
| 		if (auto mappingType = dynamic_cast<MappingType const*>(varType.get())) |  | ||||||
| 			if ( |  | ||||||
| 				mappingType->keyType()->isDynamicallySized() && |  | ||||||
| 				_variable.visibility() == Declaration::Visibility::Public |  | ||||||
| 			) |  | ||||||
| 				m_errorReporter.typeError(_variable.location(), "Dynamically-sized keys for public mappings are not supported."); |  | ||||||
| 		break; |  | ||||||
| 	default: | 	default: | ||||||
| 		break; | 		break; | ||||||
| 	} | 	} | ||||||
|  | |||||||
| @ -106,18 +106,49 @@ void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const& | |||||||
| 		if (auto mappingType = dynamic_cast<MappingType const*>(returnType.get())) | 		if (auto mappingType = dynamic_cast<MappingType const*>(returnType.get())) | ||||||
| 		{ | 		{ | ||||||
| 			solAssert(CompilerUtils::freeMemoryPointer >= 0x40, ""); | 			solAssert(CompilerUtils::freeMemoryPointer >= 0x40, ""); | ||||||
| 			solUnimplementedAssert( | 
 | ||||||
| 				!paramTypes[i]->isDynamicallySized(), |  | ||||||
| 				"Accessors for mapping with dynamically-sized keys not yet implemented." |  | ||||||
| 			); |  | ||||||
| 			// pop offset
 | 			// pop offset
 | ||||||
| 			m_context << Instruction::POP; | 			m_context << Instruction::POP; | ||||||
| 			// move storage offset to memory.
 | 			if (paramTypes[i]->isDynamicallySized()) | ||||||
| 			utils().storeInMemory(32); | 			{ | ||||||
| 			// move key to memory.
 | 				solAssert( | ||||||
| 			utils().copyToStackTop(paramTypes.size() - i, 1); | 					dynamic_cast<ArrayType const&>(*paramTypes[i]).isByteArray(), | ||||||
| 			utils().storeInMemory(0); | 					"Expected string or byte array for mapping key type" | ||||||
| 			m_context << u256(64) << u256(0) << Instruction::KECCAK256; | 				); | ||||||
|  | 
 | ||||||
|  | 				// stack: <keys..> <slot position>
 | ||||||
|  | 
 | ||||||
|  | 				// copy key[i] to top.
 | ||||||
|  | 				utils().copyToStackTop(paramTypes.size() - i + 1, 1); | ||||||
|  | 
 | ||||||
|  | 				m_context.appendInlineAssembly(R"({ | ||||||
|  | 					let key_len := mload(key_ptr) | ||||||
|  | 					// Temp. use the memory after the array data for the slot
 | ||||||
|  | 					// position
 | ||||||
|  | 					let post_data_ptr := add(key_ptr, add(key_len, 0x20)) | ||||||
|  | 					let orig_data := mload(post_data_ptr) | ||||||
|  | 					mstore(post_data_ptr, slot_pos) | ||||||
|  | 					let hash := keccak256(add(key_ptr, 0x20), add(key_len, 0x20)) | ||||||
|  | 					mstore(post_data_ptr, orig_data) | ||||||
|  | 					slot_pos := hash | ||||||
|  | 				})", {"slot_pos", "key_ptr"}); | ||||||
|  | 
 | ||||||
|  | 				m_context << Instruction::POP; | ||||||
|  | 			} | ||||||
|  | 			else | ||||||
|  | 			{ | ||||||
|  | 				solAssert(paramTypes[i]->isValueType(), "Expected value type for mapping key"); | ||||||
|  | 
 | ||||||
|  | 				// move storage offset to memory.
 | ||||||
|  | 				utils().storeInMemory(32); | ||||||
|  | 
 | ||||||
|  | 				// move key to memory.
 | ||||||
|  | 				utils().copyToStackTop(paramTypes.size() - i, 1); | ||||||
|  | 				utils().storeInMemory(0); | ||||||
|  | 				m_context << u256(64) << u256(0); | ||||||
|  | 				m_context << Instruction::KECCAK256; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
| 			// push offset
 | 			// push offset
 | ||||||
| 			m_context << u256(0); | 			m_context << u256(0); | ||||||
| 			returnType = mappingType->valueType(); | 			returnType = mappingType->valueType(); | ||||||
|  | |||||||
| @ -51,6 +51,11 @@ namespace test | |||||||
| 
 | 
 | ||||||
| BOOST_FIXTURE_TEST_SUITE(SolidityEndToEndTest, SolidityExecutionFramework) | BOOST_FIXTURE_TEST_SUITE(SolidityEndToEndTest, SolidityExecutionFramework) | ||||||
| 
 | 
 | ||||||
|  | int constexpr roundTo32(int _num) | ||||||
|  | { | ||||||
|  | 	return (_num + 31) / 32 * 32; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| BOOST_AUTO_TEST_CASE(transaction_status) | BOOST_AUTO_TEST_CASE(transaction_status) | ||||||
| { | { | ||||||
| 	char const* sourceCode = R"( | 	char const* sourceCode = R"( | ||||||
| @ -8435,6 +8440,144 @@ BOOST_AUTO_TEST_CASE(string_as_mapping_key) | |||||||
| 		), encodeArgs(u256(7 + i))); | 		), encodeArgs(u256(7 + i))); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | BOOST_AUTO_TEST_CASE(string_as_public_mapping_key) | ||||||
|  | { | ||||||
|  | 	char const* sourceCode = R"( | ||||||
|  | 		contract Test { | ||||||
|  | 			mapping(string => uint) public data; | ||||||
|  | 			function set(string memory _s, uint _v) public { data[_s] = _v; } | ||||||
|  | 		} | ||||||
|  | 	)"; | ||||||
|  | 	compileAndRun(sourceCode, 0, "Test"); | ||||||
|  | 	vector<string> strings{ | ||||||
|  | 		"Hello, World!", | ||||||
|  | 		"Hello,                            World!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!1111", | ||||||
|  | 		"", | ||||||
|  | 		"1" | ||||||
|  | 	}; | ||||||
|  | 	for (unsigned i = 0; i < strings.size(); i++) | ||||||
|  | 		ABI_CHECK(callContractFunction( | ||||||
|  | 			"set(string,uint256)", | ||||||
|  | 			u256(0x40), | ||||||
|  | 			u256(7 + i), | ||||||
|  | 			u256(strings[i].size()), | ||||||
|  | 			strings[i] | ||||||
|  | 		), encodeArgs()); | ||||||
|  | 	for (unsigned i = 0; i < strings.size(); i++) | ||||||
|  | 		ABI_CHECK(callContractFunction( | ||||||
|  | 			"data(string)", | ||||||
|  | 			u256(0x20), | ||||||
|  | 			u256(strings[i].size()), | ||||||
|  | 			strings[i] | ||||||
|  | 		), encodeArgs(u256(7 + i))); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | BOOST_AUTO_TEST_CASE(nested_string_as_public_mapping_key) | ||||||
|  | { | ||||||
|  | 	char const* sourceCode = R"( | ||||||
|  | 		contract Test { | ||||||
|  | 			mapping(string => mapping(string => uint)) public data; | ||||||
|  | 			function set(string memory _s, string memory _s2, uint _v) public { | ||||||
|  | 				data[_s][_s2] = _v; } | ||||||
|  | 		} | ||||||
|  | 	)"; | ||||||
|  | 	compileAndRun(sourceCode, 0, "Test"); | ||||||
|  | 	vector<string> strings{ | ||||||
|  | 		"Hello, World!", | ||||||
|  | 		"Hello,                            World!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!1111", | ||||||
|  | 		"", | ||||||
|  | 		"1", | ||||||
|  | 		"last one" | ||||||
|  | 	}; | ||||||
|  | 	for (unsigned i = 0; i + 1 < strings.size(); i++) | ||||||
|  | 		ABI_CHECK(callContractFunction( | ||||||
|  | 			"set(string,string,uint256)", | ||||||
|  | 			u256(0x60), | ||||||
|  | 			u256(roundTo32(0x80 + strings[i].size())), | ||||||
|  | 			u256(7 + i), | ||||||
|  | 			u256(strings[i].size()), | ||||||
|  | 			strings[i], | ||||||
|  | 			u256(strings[i+1].size()), | ||||||
|  | 			strings[i+1] | ||||||
|  | 		), encodeArgs()); | ||||||
|  | 	for (unsigned i = 0; i + 1 < strings.size(); i++) | ||||||
|  | 		ABI_CHECK(callContractFunction( | ||||||
|  | 			"data(string,string)", | ||||||
|  | 			u256(0x40), | ||||||
|  | 			u256(roundTo32(0x60 + strings[i].size())), | ||||||
|  | 			u256(strings[i].size()), | ||||||
|  | 			strings[i], | ||||||
|  | 			u256(strings[i+1].size()), | ||||||
|  | 			strings[i+1] | ||||||
|  | 		), encodeArgs(u256(7 + i))); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | BOOST_AUTO_TEST_CASE(nested_mixed_string_as_public_mapping_key) | ||||||
|  | { | ||||||
|  | 	char const* sourceCode = R"( | ||||||
|  | 		contract Test { | ||||||
|  | 			mapping(string => | ||||||
|  | 				mapping(int => | ||||||
|  | 					mapping(address => | ||||||
|  | 						mapping(bytes => int)))) public data; | ||||||
|  | 
 | ||||||
|  | 			function set( | ||||||
|  | 				string memory _s1, | ||||||
|  | 				int _s2, | ||||||
|  | 				address _s3, | ||||||
|  | 				bytes memory _s4, | ||||||
|  | 				int _value | ||||||
|  | 			) public | ||||||
|  | 			{ | ||||||
|  | 				data[_s1][_s2][_s3][_s4] = _value; | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	)"; | ||||||
|  | 	compileAndRun(sourceCode, 0, "Test"); | ||||||
|  | 
 | ||||||
|  | 	struct Index | ||||||
|  | 	{ | ||||||
|  | 		string s1; | ||||||
|  | 		int s2; | ||||||
|  | 		int s3; | ||||||
|  | 		string s4; | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  | 	vector<Index> data{ | ||||||
|  | 		{ "aabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcbc", 4, 23, "efg" }, | ||||||
|  | 		{ "tiaron", 456, 63245, "908apzapzapzapzapzapzapzapzapzapzapzapzapzapzapzapzapzapzapzapzapzapzapzapzapzapzapzapzapzapzapzapzapzapzapzapzapzapzapzapzapzapzapzapz" }, | ||||||
|  | 		{ "", 2345, 12934, "665i65i65i65i65i65i65i65i65i65i65i65i65i65i65i65i65i65i5iart" }, | ||||||
|  | 		{ "¡¿…", 9781, 8148, "" }, | ||||||
|  | 		{ "ρν♀♀ω₂₃♀", 929608, 303030, "" } | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  | 	for (size_t i = 0; i + 1 < data.size(); i++) | ||||||
|  | 		ABI_CHECK(callContractFunction( | ||||||
|  | 			"set(string,int256,address,bytes,int256)", | ||||||
|  | 			u256(0xA0), | ||||||
|  | 			u256(data[i].s2), | ||||||
|  | 			u256(data[i].s3), | ||||||
|  | 			u256(roundTo32(0xC0 + data[i].s1.size())), | ||||||
|  | 			u256(i - 3), | ||||||
|  | 			u256(data[i].s1.size()), | ||||||
|  | 			data[i].s1, | ||||||
|  | 			u256(data[i].s4.size()), | ||||||
|  | 			data[i].s4 | ||||||
|  | 		), encodeArgs()); | ||||||
|  | 	for (size_t i = 0; i + 1 < data.size(); i++) | ||||||
|  | 		ABI_CHECK(callContractFunction( | ||||||
|  | 			"data(string,int256,address,bytes)", | ||||||
|  | 			u256(0x80), | ||||||
|  | 			u256(data[i].s2), | ||||||
|  | 			u256(data[i].s3), | ||||||
|  | 			u256(roundTo32(0xA0 + data[i].s1.size())), | ||||||
|  | 			u256(data[i].s1.size()), | ||||||
|  | 			data[i].s1, | ||||||
|  | 			u256(data[i].s4.size()), | ||||||
|  | 			data[i].s4 | ||||||
|  | 		), encodeArgs(u256(i - 3))); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| BOOST_AUTO_TEST_CASE(accessor_for_state_variable) | BOOST_AUTO_TEST_CASE(accessor_for_state_variable) | ||||||
| { | { | ||||||
| 	char const* sourceCode = R"( | 	char const* sourceCode = R"( | ||||||
|  | |||||||
| @ -2,4 +2,3 @@ contract c { | |||||||
| 	mapping(string => uint) public data; | 	mapping(string => uint) public data; | ||||||
| } | } | ||||||
| // ---- | // ---- | ||||||
| // TypeError: (14-49): Dynamically-sized keys for public mappings are not supported. |  | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user