diff --git a/evm-ds/src/scillabackend.rs b/evm-ds/src/scillabackend.rs index 9ea3850d0e..6baef9cf99 100644 --- a/evm-ds/src/scillabackend.rs +++ b/evm-ds/src/scillabackend.rs @@ -127,6 +127,7 @@ impl ScillaBackend { address, query_name, key, use_default ); let mut query = ScillaMessage::ProtoScillaQuery::new(); + query.set_is_mutable(true); query.set_name(query_name.into()); if let Some(key) = key { query.set_indices(vec![bytes::Bytes::from(format!("{:X}", key))]); @@ -198,6 +199,7 @@ impl ScillaBackend { pub(crate) fn encode_storage(&self, key: H256, value: H256) -> (Bytes, Bytes) { let mut query = ScillaMessage::ProtoScillaQuery::new(); query.set_name("_evm_storage".into()); + query.set_is_mutable(true); query.set_indices(vec![bytes::Bytes::from(format!("{:X}", key))]); query.set_mapdepth(1); let mut val = ScillaMessage::ProtoScillaVal::new(); diff --git a/src/libPersistence/ContractStorage.cpp b/src/libPersistence/ContractStorage.cpp index 02f305d7f9..a4abe216fd 100644 --- a/src/libPersistence/ContractStorage.cpp +++ b/src/libPersistence/ContractStorage.cpp @@ -448,37 +448,108 @@ bool ContractStorage::FetchExternalStateValue( return true; } - // For _evm_storage, we know the type, so we don't have to query it. - bool fetchType; - if (query.name() == "_evm_storage") { - type = "ByStr32"; - fetchType = false; - } else { - fetchType = true; - // External state queries don't have map depth set. Get it from the - // database. - map map_depth; - string map_depth_key = - GenerateStorageKey(target, MAP_DEPTH_INDICATOR, {query.name()}); - FetchStateDataForKey(map_depth, map_depth_key, true); + if (query.is_mutable()) { + // For _evm_storage, we know the type, so we don't have to query it. + bool fetchType; + if (query.name() == "_evm_storage") { + type = "ByStr32"; + fetchType = false; + } else { + fetchType = true; + // External state queries don't have map depth set. Get it from the + // database. + map map_depth; + string map_depth_key = + GenerateStorageKey(target, MAP_DEPTH_INDICATOR, {query.name()}); + FetchStateDataForKey(map_depth, map_depth_key, true); + + int map_depth_val; + try { + map_depth_val = !map_depth.empty() + ? std::stoi(DataConversion::CharArrayToString( + map_depth[map_depth_key])) + : -1; + } catch (const std::exception& e) { + LOG_GENERAL(WARNING, "invalid map depth: " << e.what()); + return false; + } + query.set_mapdepth(map_depth_val); + fetchType = true; + } + + // get value + return FetchStateValue(target, query, dst, d_offset, foundVal, fetchType, + type); + } else { //! query.is_mutable + // Fetch init data + zbytes init_data = GetInitData(target); + if (init_data.empty()) { // This is possible if no contract exists at the + // address, and should not cause an error + foundVal = false; + return true; + } - int map_depth_val; - try { - map_depth_val = !map_depth.empty() - ? std::stoi(DataConversion::CharArrayToString( - map_depth[map_depth_key])) - : -1; - } catch (const std::exception& e) { - LOG_GENERAL(WARNING, "invalid map depth: " << e.what()); - return false; + // Convert init data to JSON + string init_data_str = DataConversion::CharArrayToString(init_data); + Json::Value init_data_json; + if (!JSONUtils::GetInstance().convertStrtoJson(init_data_str, + init_data_json)) { + return false; // We have a malformed init data } - query.set_mapdepth(map_depth_val); - fetchType = true; - } - // get value - return FetchStateValue(target, query, dst, d_offset, foundVal, fetchType, - type); + // Entry is the data entry matching the variable name + Json::Value entry; + for (auto it = init_data_json.begin(); it != init_data_json.end(); it++) { + if ((*it)["vname"] == query.name()) { + entry = *it; + } + } + if (!entry) { // This is possible if the parameter name is not + // declared, and should not cause an error + foundVal = false; + return true; + } + + // This is the type we return - if this is a map lookup, then we parse the + // type on the OCaml side. + Json::Value res_type = entry["type"]; + if (!res_type) { + return false; // We have a malformed init data + } + + if (query.ignoreval()) { // Only produce type information + foundVal = true; + type = res_type.asString(); + return true; + } else { // See if there is a value to return + ProtoScillaVal value; + Json::Value cur_val = entry["value"]; // Find the value of the parameter + if (!cur_val) { + return false; // We have a malformed init data + } + // If this is a map lookup, traverse the indices + for (const auto& index : query.indices()) { + // See if the key exists in the map + Json::Value next_entry = cur_val["key"]; + if (!next_entry) { // This is possible if the key does not exist + // in the map, and should not cause an error + foundVal = false; + return true; + } + cur_val = next_entry["val"]; // Value of the map entry + if (!cur_val) { + return false; // We have a malformed init data + } + } + + string cur_val_str = JSONUtils::GetInstance().convertJsontoStr(cur_val); + zbytes cur_val_bval = zbytes(cur_val_str.begin(), cur_val_str.end()); + value.set_bval(cur_val_bval.data(), cur_val_bval.size()); + foundVal = true; + type = res_type.asString(); + return SerializeToArray(value, dst, 0); + } + } } void ContractStorage::DeleteByPrefix(const string& prefix) { diff --git a/src/libPersistence/ScillaMessage.proto b/src/libPersistence/ScillaMessage.proto index dfaf5a7f30..d707bf2b81 100644 --- a/src/libPersistence/ScillaMessage.proto +++ b/src/libPersistence/ScillaMessage.proto @@ -17,7 +17,8 @@ message ProtoScillaVal message ProtoScillaQuery { string name = 1; - uint32 mapdepth = 2; - repeated bytes indices = 3; - bool ignoreval = 4; + bool is_mutable = 2; + uint32 mapdepth = 3; + repeated bytes indices = 4; + bool ignoreval = 5; }