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) | ||||
| 
 | ||||
| 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``. | ||||
| 
 | ||||
| 
 | ||||
|  | ||||
| @ -511,14 +511,6 @@ bool TypeChecker::visit(VariableDeclaration const& _variable) | ||||
| 			) | ||||
| 				m_errorReporter.typeError(_variable.location(), "Array is too large to be encoded."); | ||||
| 		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: | ||||
| 		break; | ||||
| 	} | ||||
|  | ||||
| @ -106,18 +106,49 @@ void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const& | ||||
| 		if (auto mappingType = dynamic_cast<MappingType const*>(returnType.get())) | ||||
| 		{ | ||||
| 			solAssert(CompilerUtils::freeMemoryPointer >= 0x40, ""); | ||||
| 			solUnimplementedAssert( | ||||
| 				!paramTypes[i]->isDynamicallySized(), | ||||
| 				"Accessors for mapping with dynamically-sized keys not yet implemented." | ||||
| 			); | ||||
| 
 | ||||
| 			// pop offset
 | ||||
| 			m_context << Instruction::POP; | ||||
| 			if (paramTypes[i]->isDynamicallySized()) | ||||
| 			{ | ||||
| 				solAssert( | ||||
| 					dynamic_cast<ArrayType const&>(*paramTypes[i]).isByteArray(), | ||||
| 					"Expected string or byte array for mapping key type" | ||||
| 				); | ||||
| 
 | ||||
| 				// 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) << Instruction::KECCAK256; | ||||
| 				m_context << u256(64) << u256(0); | ||||
| 				m_context << Instruction::KECCAK256; | ||||
| 			} | ||||
| 
 | ||||
| 			// push offset
 | ||||
| 			m_context << u256(0); | ||||
| 			returnType = mappingType->valueType(); | ||||
|  | ||||
| @ -51,6 +51,11 @@ namespace test | ||||
| 
 | ||||
| BOOST_FIXTURE_TEST_SUITE(SolidityEndToEndTest, SolidityExecutionFramework) | ||||
| 
 | ||||
| int constexpr roundTo32(int _num) | ||||
| { | ||||
| 	return (_num + 31) / 32 * 32; | ||||
| } | ||||
| 
 | ||||
| BOOST_AUTO_TEST_CASE(transaction_status) | ||||
| { | ||||
| 	char const* sourceCode = R"( | ||||
| @ -8435,6 +8440,144 @@ BOOST_AUTO_TEST_CASE(string_as_mapping_key) | ||||
| 		), 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) | ||||
| { | ||||
| 	char const* sourceCode = R"( | ||||
|  | ||||
| @ -2,4 +2,3 @@ contract c { | ||||
| 	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