From 664b53549e8e655fc5440e7e3716c36cc06aa75e Mon Sep 17 00:00:00 2001 From: Jacob Johannsen Date: Tue, 17 Jan 2023 15:29:12 +0100 Subject: [PATCH 1/3] Updated ScillaMessage.proto, and added pseudocode for ext reads of init data --- src/libPersistence/ContractStorage.cpp | 108 ++++++++++++++++++------- src/libPersistence/ScillaMessage.proto | 7 +- 2 files changed, 83 insertions(+), 32 deletions(-) diff --git a/src/libPersistence/ContractStorage.cpp b/src/libPersistence/ContractStorage.cpp index 02f305d7f9..a6181a4baf 100644 --- a/src/libPersistence/ContractStorage.cpp +++ b/src/libPersistence/ContractStorage.cpp @@ -448,37 +448,87 @@ 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); + //TODO + // 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); + + // TODO + // } + // else { //!query.is_mutable + /** TODO: Fetch the relevate data from the init file. + //Fetch init data + init_data = target.GetInitData() //(Using Account::GetInitData()) + //Possibly also need to use Account::ParseInitDataJson(), though I'm not sure + + if (init_data == null) { // 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; - } - query.set_mapdepth(map_depth_val); - fetchType = true; - } + entry = init_data.find("vname", query.name()) (entry = the data entry matching the variable name) + if (entry == null) { // This is possible if the parameter name is not declared, and should not cause an error + foundVal = false; + return true; + } + + res_type = entry.find("type") //This is the type we return - if this is a map lookup, then we parse the type on the OCaml side. - // get value - return FetchStateValue(target, query, dst, d_offset, foundVal, fetchType, - type); + if (query.ignoreval()) { //Only produce type information + foundVal = true; + type = res_type; + return true; + } + else { //See if there is a value to return + ProtoScillaVal value; + zbytes cur_val = entry.find("value"); // Find the value of the parameter + for (const auto& index : query.indices()) { //If this is a map lookup, traverse the indices + //This logic is similar to FetchStateValue, line 202 and forward. + //The difference is that we are are looking up the value in json data rather than in levelDB + next_entry = cur_val.find("key", index); // See if the key exists in the map. + if (next_entry == null) { // 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.find("val"); //Value of the map entry + } + value.set_bval(cur_val.data(), cur_val.size()); //I'm not entirely sure this is correct, but it looks like that is what happens in FetchStateValue + foundVal = true; + type = res_type; + return SerializeToArray(value, dst, 0); + } + */ + //TODO + // } } 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; } From 0bc9af3db07966c2a44e4ccbceaabd6e46d697f8 Mon Sep 17 00:00:00 2001 From: Jacob Johannsen Date: Tue, 17 Jan 2023 15:39:02 +0100 Subject: [PATCH 2/3] Add is_mutable=true to EVM instances of ProtoScillaQuery --- evm-ds/src/scillabackend.rs | 2 ++ 1 file changed, 2 insertions(+) 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(); From f8ea22a0cea243065f04037069df86ca18c5a48d Mon Sep 17 00:00:00 2001 From: Georgiy Komarov Date: Mon, 27 Feb 2023 17:19:34 +0700 Subject: [PATCH 3/3] Write code for ext reads of init data --- src/libPersistence/ContractStorage.cpp | 109 +++++++++++++++---------- 1 file changed, 65 insertions(+), 44 deletions(-) diff --git a/src/libPersistence/ContractStorage.cpp b/src/libPersistence/ContractStorage.cpp index a6181a4baf..a4abe216fd 100644 --- a/src/libPersistence/ContractStorage.cpp +++ b/src/libPersistence/ContractStorage.cpp @@ -448,8 +448,7 @@ bool ContractStorage::FetchExternalStateValue( return true; } - //TODO - // if (query.is_mutable()) { + 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") { @@ -481,54 +480,76 @@ bool ContractStorage::FetchExternalStateValue( // get value return FetchStateValue(target, query, dst, d_offset, foundVal, fetchType, type); - - // TODO - // } - // else { //!query.is_mutable - /** TODO: Fetch the relevate data from the init file. - //Fetch init data - init_data = target.GetInitData() //(Using Account::GetInitData()) - //Possibly also need to use Account::ParseInitDataJson(), though I'm not sure - - if (init_data == null) { // This is possible if no contract exists at the address, and should not cause an error - foundVal = false; - return true; - } + } 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; + } - entry = init_data.find("vname", query.name()) (entry = the data entry matching the variable name) - if (entry == null) { // This is possible if the parameter name is not declared, and should not cause an error - foundVal = false; - return true; - } + // 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 + } + + // 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; + } - res_type = entry.find("type") //This is the type we return - if this is a map lookup, then we parse the type on the OCaml side. + // 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; + 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; } - else { //See if there is a value to return - ProtoScillaVal value; - zbytes cur_val = entry.find("value"); // Find the value of the parameter - for (const auto& index : query.indices()) { //If this is a map lookup, traverse the indices - //This logic is similar to FetchStateValue, line 202 and forward. - //The difference is that we are are looking up the value in json data rather than in levelDB - next_entry = cur_val.find("key", index); // See if the key exists in the map. - if (next_entry == null) { // 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.find("val"); //Value of the map entry - } - value.set_bval(cur_val.data(), cur_val.size()); //I'm not entirely sure this is correct, but it looks like that is what happens in FetchStateValue - foundVal = true; - type = res_type; - return SerializeToArray(value, dst, 0); + cur_val = next_entry["val"]; // Value of the map entry + if (!cur_val) { + return false; // We have a malformed init data } - */ - //TODO - // } + } + + 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) {