-
Notifications
You must be signed in to change notification settings - Fork 77
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add a tool to convert Zil API state JSON to Scilla format (#988)
* Add a tool to convert Zil API state JSON to Scilla format * Minor grammer fixes to README * Mention that init JSON must be provided to scilla-checker
- Loading branch information
1 parent
869c869
commit ce0a248
Showing
6 changed files
with
1,187 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
convert.exe : ScillaStateJsonConvert.cpp | ||
g++ -o convert.exe ScillaStateJsonConvert.cpp -ljsoncpp -lboost_program_options | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
# Convert State JSON from Zilliqa API Format to Scilla Format | ||
|
||
The [Zilliqa API]((https://dev.zilliqa.com/docs/apis/api-contract-get-smartcontract-state)) | ||
to fetch the state of a contract produces a JSON in the format | ||
|
||
``` | ||
{ | ||
"_balance": "0", | ||
"admins": { | ||
"0xdfa89866ae86632b36361d53b76c1373448c28fa": { | ||
"argtypes": [], | ||
"arguments": [], | ||
"constructor": "True" | ||
} | ||
} | ||
} | ||
``` | ||
|
||
This program converts it into a format that Scilla understands. Since | ||
there is some loss of information in the API output (the type of a field, | ||
for example), this program also requires the `-contractinfo` output of | ||
scilla-checker as an additional input to find this missing information. | ||
The init JSON of the contract must be provided as input to `scilla-checker` | ||
(via the `-init` option) so that names are disambiguated correctly in the | ||
output contract info JSON of `scilla-checker`. | ||
|
||
The output JSON for the above snippet would look something like this | ||
|
||
``` | ||
[ | ||
{ | ||
"vname" : "_balance" | ||
"type" : "Uint128", | ||
"value" : "0" | ||
}, | ||
{ | ||
"vname" : "admins", | ||
"type" : "Map ByStr20 Bool", | ||
"value" : [ | ||
{ | ||
"key" : "0xdfa89866ae86632b36361d53b76c1373448c28fa", | ||
"val" : { | ||
"argtypes": [], | ||
"arguments": [], | ||
"constructor": "True" | ||
} | ||
} | ||
] | ||
} | ||
] | ||
``` | ||
|
||
## Build | ||
Install `libjsoncpp-dev`and `libboost-program-options-dev` system packages. | ||
The command `make` in this directory should now succeed in building the | ||
executable `convert.exe`. | ||
|
||
## Use | ||
Using the example inputs provided in `tests`, here's how the tool can be used: | ||
|
||
```./convert.exe -s tests/ZilSwap/state.json -c tests/ZilSwap/contractinfo.json``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,163 @@ | ||
#include <iostream> | ||
#include <fstream> | ||
#include <memory> | ||
#include <unordered_map> | ||
#include <jsoncpp/json/reader.h> | ||
#include <jsoncpp/json/value.h> | ||
|
||
#include <boost/program_options.hpp> | ||
|
||
namespace po = boost::program_options; | ||
|
||
void exitMsg(const std::string &Msg) { | ||
std::cerr << Msg; | ||
exit(EXIT_FAILURE); | ||
} | ||
|
||
void parseCLIArgs(int argc, char *argv[], po::variables_map &VM) { | ||
auto UsageString = | ||
"Usage: " + std::string(argv[0]) + | ||
" -c contract_info.json -s state.json" | ||
"\nSupported options"; | ||
po::options_description Desc(UsageString); | ||
|
||
// clang-format off | ||
Desc.add_options() | ||
("state,s", po::value<std::string>(), "Specify the JSON to use as initial state") | ||
("contract-info,c", po::value<std::string>(), "Specify the contract info JSON from checker") | ||
("help,h", "Print help message") | ||
("version,v", "Print version") | ||
; | ||
// clang-format on | ||
|
||
try { | ||
po::store(po::command_line_parser(argc, argv).options(Desc).run(), VM); | ||
po::notify(VM); | ||
} catch (std::exception &e) { | ||
std::cerr << e.what() << "\n"; | ||
std::cerr << Desc << "\n"; | ||
exit(EXIT_FAILURE); | ||
} | ||
|
||
if (VM.count("help")) { | ||
std::cerr << Desc << "\n"; | ||
exit(EXIT_SUCCESS); | ||
} | ||
|
||
// Ensure that an input file is provided. | ||
if (!VM.count("state") || !VM.count("contract-info")) { | ||
std::cerr << "Missing mandatory command line arguments\n" << Desc << "\n"; | ||
exit(EXIT_FAILURE); | ||
} | ||
} | ||
|
||
std::string readFile(const std::string &Filename) { | ||
std::ifstream IfsFile(Filename); | ||
std::string FileStr((std::istreambuf_iterator<char>(IfsFile)), | ||
(std::istreambuf_iterator<char>())); | ||
return FileStr; | ||
} | ||
|
||
Json::Value parseJSONString(const std::string &JS) { | ||
Json::Value Ret; | ||
Json::CharReaderBuilder ReadBuilder; | ||
std::unique_ptr<Json::CharReader> Reader(ReadBuilder.newCharReader()); | ||
std::string Error; | ||
try { | ||
Reader->parse(JS.c_str(), JS.c_str() + JS.length(), &Ret, &Error); | ||
} catch (const std::exception &e) { | ||
exitMsg(std::string(e.what()) + ": " + Error); | ||
} | ||
|
||
return Ret; | ||
} | ||
|
||
Json::Value parseJSONFile(const std::string &Filename) { | ||
|
||
return parseJSONString(readFile(Filename)); | ||
} | ||
|
||
Json::Value convertValue(const Json::Value &IJ, int Depth) { | ||
if (Depth == 0) { | ||
return IJ; | ||
} | ||
|
||
if (IJ.isNull()) { | ||
return Json::arrayValue; | ||
} | ||
|
||
if (!IJ.isObject()) { | ||
exitMsg("Expected JSON object at depth " + std::to_string(Depth)); | ||
} | ||
|
||
Json::Value OJ = Json::arrayValue; | ||
for (Json::Value::const_iterator Itr = IJ.begin(); | ||
Itr != IJ.end(); Itr++) | ||
{ | ||
Json::Value J = Json::objectValue; | ||
J["key"] = Itr.name(); | ||
J["val"] = convertValue(*Itr, Depth - 1); | ||
OJ.append(J); | ||
} | ||
|
||
return OJ; | ||
} | ||
|
||
int main(int argc, char *argv[]) | ||
{ | ||
po::variables_map VM; | ||
parseCLIArgs(argc, argv, VM); | ||
|
||
// Process the contract info JSON first. | ||
std::string ContrInfoFilename = VM["contract-info"].as<std::string>(); | ||
Json::Value ContrInfoJ = parseJSONFile(ContrInfoFilename); | ||
|
||
Json::Value &Fields = ContrInfoJ["contract_info"]["fields"]; | ||
if (Fields.isNull()) { | ||
exitMsg("fields not found in " + ContrInfoFilename); | ||
} | ||
|
||
std::unordered_map<std::string, int> DepthMap; | ||
std::unordered_map<std::string, std::string> TypeMap; | ||
DepthMap["_balance"] = 0; | ||
TypeMap["_balance"] = "Uint128"; | ||
|
||
for (const auto &Field : Fields) { | ||
if (!Field["vname"].isString() || !Field["type"].isString() | ||
|| !Field["depth"].isInt()) | ||
{ | ||
exitMsg("Incorrect field entry.\n" + Field.toStyledString()); | ||
} | ||
TypeMap[Field["vname"].asString()] = Field["type"].asString(); | ||
DepthMap[Field["vname"].asString()] = Field["depth"].asInt(); | ||
} | ||
|
||
std::string InputStateFilename = VM["state"].as<std::string>(); | ||
Json::Value InputState = parseJSONFile(InputStateFilename); | ||
|
||
Json::Value OutputState = Json::arrayValue; | ||
if (!InputState.isObject()) { | ||
exitMsg("Expected input state to be a JSON object"); | ||
} | ||
|
||
for (Json::Value::const_iterator Itr = InputState.begin(); | ||
Itr != InputState.end(); Itr++) | ||
{ | ||
const std::string &VName = Itr.name(); | ||
const Json::Value &ValJ = *Itr; | ||
const auto &Type = TypeMap.find(VName); | ||
const auto &Depth = DepthMap.find(VName); | ||
if (Type == TypeMap.end() || Depth == DepthMap.end()) { | ||
exitMsg("Not found: type or depth for " + VName); | ||
} | ||
Json::Value OJ; | ||
OJ["vname"] = VName; | ||
OJ["type"] = Type->second; | ||
OJ["value"] = convertValue(ValJ, Depth->second); | ||
OutputState.append(OJ); | ||
} | ||
|
||
std::cout << OutputState.toStyledString(); | ||
|
||
return 0; | ||
} |
Oops, something went wrong.