diff --git a/tee-worker/litentry/core/assertion-build/src/dynamic/contracts/libraries/Json.sol b/tee-worker/litentry/core/assertion-build/src/dynamic/contracts/libraries/Json.sol
new file mode 100644
index 0000000000..9c74eaefc9
--- /dev/null
+++ b/tee-worker/litentry/core/assertion-build/src/dynamic/contracts/libraries/Json.sol
@@ -0,0 +1,153 @@
+// Copyright 2020-2024 Trust Computing GmbH.
+// This file is part of Litentry.
+//
+// Litentry is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Litentry is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Litentry. If not, see .
+
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+pragma solidity ^0.8.8;
+
+library Json {
+ function getString(string memory json, string memory pointer)
+ internal
+ returns (bool, string memory)
+ {
+ bool success;
+ string memory value;
+ bytes memory encoded_params = abi.encode(json, pointer);
+
+ uint256 encoded_params_len = encoded_params.length;
+
+ assembly {
+ let memPtr := mload(0x40)
+ if iszero(
+ call(
+ not(0),
+ 0x044C,
+ 0,
+ add(encoded_params, 0x20),
+ encoded_params_len,
+ memPtr,
+ 0x1000
+ )
+ ) {
+ revert(0, 0)
+ }
+ success := mload(memPtr)
+ value := add(memPtr, 0x40)
+ mstore(0x40, add(memPtr, 0x1000))
+ }
+
+ return (success, value);
+ }
+
+ function getI64(string memory json, string memory pointer)
+ internal
+ returns (bool, int64)
+ {
+ bool success;
+ int64 value;
+
+ bytes memory encoded_params = abi.encode(json, pointer);
+ uint256 encoded_params_len = encoded_params.length;
+
+ assembly {
+ let memPtr := mload(0x40)
+ if iszero(
+ call(
+ not(0),
+ 0x044D,
+ 0,
+ add(encoded_params, 0x20),
+ encoded_params_len,
+ memPtr,
+ 0x40
+ )
+ ) {
+ revert(0, 0)
+ }
+ success := mload(memPtr)
+ value := mload(add(memPtr, 0x20))
+ mstore(0x40, add(memPtr, 0x40))
+ }
+
+ return (success, value);
+ }
+
+ function getBool(string memory json, string memory pointer)
+ internal
+ returns (bool, bool)
+ {
+ bool success;
+ bool value;
+
+ bytes memory encoded_params = abi.encode(json, pointer);
+ uint256 encoded_params_len = encoded_params.length;
+
+ assembly {
+ let memPtr := mload(0x40)
+ if iszero(
+ call(
+ not(0),
+ 0x044E,
+ 0,
+ add(encoded_params, 0x20),
+ encoded_params_len,
+ memPtr,
+ 0x40
+ )
+ ) {
+ revert(0, 0)
+ }
+ success := mload(memPtr)
+ value := mload(add(memPtr, 0x20))
+ mstore(0x40, add(memPtr, 0x40))
+ }
+
+ return (success, value);
+ }
+
+ function getArrayLen(string memory json, string memory pointer)
+ internal
+ returns (bool, int64)
+ {
+ bool success;
+ int64 value;
+
+ bytes memory encoded_params = abi.encode(json, pointer);
+ uint256 encoded_params_len = encoded_params.length;
+
+ assembly {
+ let memPtr := mload(0x40)
+ if iszero(
+ call(
+ not(0),
+ 0x044F,
+ 0,
+ add(encoded_params, 0x20),
+ encoded_params_len,
+ memPtr,
+ 0x40
+ )
+ ) {
+ revert(0, 0)
+ }
+ success := mload(memPtr)
+ value := mload(add(memPtr, 0x20))
+ mstore(0x40, add(memPtr, 0x40))
+ }
+
+ return (success, value);
+ }
+}
diff --git a/tee-worker/litentry/core/assertion-build/src/dynamic/contracts/tests/JsonTest.sol b/tee-worker/litentry/core/assertion-build/src/dynamic/contracts/tests/JsonTest.sol
new file mode 100644
index 0000000000..cc133ebdfa
--- /dev/null
+++ b/tee-worker/litentry/core/assertion-build/src/dynamic/contracts/tests/JsonTest.sol
@@ -0,0 +1,51 @@
+// Copyright 2020-2024 Trust Computing GmbH.
+// This file is part of Litentry.
+//
+// Litentry is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Litentry is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Litentry. If not, see .
+
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+pragma solidity ^0.8.8;
+
+import "../libraries/Json.sol";
+
+contract JsonTest {
+ function callGetString(string memory json, string memory pointer)
+ public
+ returns (bool, string memory)
+ {
+ return Json.getString(json, pointer);
+ }
+
+ function callGetI64(string memory json, string memory pointer)
+ public
+ returns (bool, int64)
+ {
+ return Json.getI64(json, pointer);
+ }
+
+ function callGetBool(string memory json, string memory pointer)
+ public
+ returns (bool, bool)
+ {
+ return Json.getBool(json, pointer);
+ }
+
+ function callGetArrayLen(string memory json, string memory pointer)
+ public
+ returns (bool, int64)
+ {
+ return Json.getArrayLen(json, pointer);
+ }
+}
diff --git a/tee-worker/litentry/core/evm-dynamic-assertions/src/precompiles/json_utils.rs b/tee-worker/litentry/core/evm-dynamic-assertions/src/precompiles/json_utils.rs
new file mode 100644
index 0000000000..e8f8a62ab6
--- /dev/null
+++ b/tee-worker/litentry/core/evm-dynamic-assertions/src/precompiles/json_utils.rs
@@ -0,0 +1,283 @@
+use crate::{
+ failure_precompile_output, json_get_fn, precompiles::PrecompileResult,
+ success_precompile_output,
+};
+use ethabi::Token;
+use serde_json::Value;
+use std::vec::Vec;
+
+json_get_fn!(json_get_string, String, as_str);
+json_get_fn!(json_get_bool, Bool, as_bool);
+json_get_fn!(json_get_i64, Uint, as_i64);
+
+pub fn get_array_len(input: Vec) -> PrecompileResult {
+ let decoded =
+ match ethabi::decode(&[ethabi::ParamType::String, ethabi::ParamType::String], &input) {
+ Ok(d) => d,
+ Err(e) => {
+ log::debug!("Could not decode bytes {:?}, reason: {:?}", input, e);
+ return Ok(failure_precompile_output(ethabi::Token::String(Default::default())))
+ },
+ };
+
+ let json = decoded.get(0).unwrap().clone().into_string().unwrap();
+ let pointer = decoded.get(1).unwrap().clone().into_string().unwrap();
+
+ let value: Value = match serde_json::from_str(&json) {
+ Ok(v) => v,
+ Err(e) => {
+ log::debug!("Could not parse json {:?}, reason: {:?}", json, e);
+ return Ok(failure_precompile_output(Token::Int(Default::default())))
+ },
+ };
+
+ let result = match value.pointer(&pointer) {
+ Some(v) => match v.as_array() {
+ Some(arr) => arr.len(),
+ None => {
+ log::debug!(
+ "There is no value or it might be of different type, pointer: ${:?}",
+ pointer
+ );
+ return Ok(failure_precompile_output(Token::Int(Default::default())))
+ },
+ },
+ None => {
+ log::debug!("No value under given pointer: :{:?}", pointer);
+ return Ok(failure_precompile_output(Token::Int(Default::default())))
+ },
+ };
+
+ Ok(success_precompile_output(Token::Int(result.into())))
+}
+
+#[cfg(test)]
+pub mod test {
+ use crate::{
+ precompiles::json_utils::{get_array_len, json_get_bool, json_get_i64, json_get_string},
+ success_precompile_output,
+ };
+ use ethabi::{encode, Token};
+ use serde_json::json;
+
+ #[test]
+ pub fn test_get_string() {
+ // given:
+ let json = json!(
+ {
+ "key": "value"
+ }
+ );
+ let data = prepare_input_data(&json.to_string(), "/key");
+
+ // when:
+ let result = json_get_string(data).unwrap();
+
+ // then
+ assert_eq!(success_precompile_output(Token::String("value".into())), result)
+ }
+
+ #[test]
+ pub fn test_get_i64() {
+ // given:
+ let json = json!(
+ {
+ "key": 14
+ }
+ );
+ let data = prepare_input_data(&json.to_string(), "/key");
+
+ // when:
+ let result = json_get_i64(data).unwrap();
+
+ // then
+ assert_eq!(success_precompile_output(Token::Int(14.into())), result)
+ }
+
+ #[test]
+ pub fn test_get_bool() {
+ // given:
+ let json = json!(
+ {
+ "key": true
+ }
+ );
+ let data = prepare_input_data(&json.to_string(), "/key");
+
+ // when:
+ let result = json_get_bool(data).unwrap();
+
+ // then
+ assert_eq!(success_precompile_output(Token::Bool(true)), result)
+ }
+
+ #[test]
+ pub fn test_get_array_len_with_array() {
+ // given:
+ let json = json!([{}, {}, {}]);
+ let data = prepare_input_data(&json.to_string(), "");
+
+ // when:
+ let result = get_array_len(data).unwrap();
+
+ // then
+ assert_eq!(success_precompile_output(Token::Int(3.into())), result)
+ }
+
+ #[test]
+ pub fn test_get_array_len_with_nested_array() {
+ // given:
+ let json = json!({
+ "nested": [
+ {},
+ {},
+ {}
+ ]
+ });
+ let data = prepare_input_data(&json.to_string(), "/nested");
+
+ // when:
+ let result = get_array_len(data).unwrap();
+
+ // then
+ assert_eq!(success_precompile_output(Token::Int(3.into())), result)
+ }
+
+ fn prepare_input_data(url: &str, pointer: &str) -> Vec {
+ encode(&[Token::String(url.to_string()), Token::String(pointer.to_string())])
+ }
+}
+
+#[cfg(test)]
+pub mod integration_test {
+ use crate::{execute_smart_contract, prepare_function_call_input};
+ use ethabi::{decode, encode, ethereum_types::U256, ParamType, Token};
+ use serde_json::json;
+
+ // tee-worker/litentry/core/assertion-build/src/dynamic/contracts/tests/JsonTest.sol
+ const FUNCTION_HASH_0: &str = "73260cf2"; // callGetString(string,string)
+ const FUNCTION_HASH_1: &str = "fe598591"; // callGetBool(string,string)
+ const FUNCTION_HASH_2: &str = "f5e19bc0"; // callGetI64(string,string)
+ const FUNCTION_HASH_3: &str = "c43558dc"; // callGetArrayLen(string,string)
+ const BYTE_CODE: &str = "608060405234801561001057600080fd5b50610466806100206000396000f3fe608060405234801561001057600080fd5b506004361061004c5760003560e01c806373260cf214610051578063c43558dc1461007b578063f5e19bc0146100a8578063fe598591146100bb575b600080fd5b61006461005f36600461032e565b6100e5565b6040516100729291906103df565b60405180910390f35b61008e61008936600461032e565b6100fe565b60408051921515835260079190910b602083015201610072565b61008e6100b636600461032e565b61010b565b6100ce6100c936600461032e565b610118565b604080519215158352901515602083015201610072565b600060606100f38484610125565b915091509250929050565b6000806100f38484610190565b6000806100f384846101f7565b6000806100f38484610241565b600060606000606060008686604051602001610142929190610402565b60408051601f1981840301815290829052805190925090611000818360208601600061044c600019f161017457600080fd5b805161100082016040908152909a910198509650505050505050565b600080600080600086866040516020016101ab929190610402565b60408051601f1981840301815282825280519093509190818360208601600061044f600019f16101da57600080fd5b805160208201516040928301909252999098509650505050505050565b60008060008060008686604051602001610212929190610402565b60408051601f1981840301815282825280519093509190818360208601600061044d600019f16101da57600080fd5b6000806000806000868660405160200161025c929190610402565b60408051601f1981840301815282825280519093509190818360208601600061044e600019f16101da57600080fd5b634e487b7160e01b600052604160045260246000fd5b600082601f8301126102b257600080fd5b813567ffffffffffffffff808211156102cd576102cd61028b565b604051601f8301601f19908116603f011681019082821181831017156102f5576102f561028b565b8160405283815286602085880101111561030e57600080fd5b836020870160208301376000602085830101528094505050505092915050565b6000806040838503121561034157600080fd5b823567ffffffffffffffff8082111561035957600080fd5b610365868387016102a1565b9350602085013591508082111561037b57600080fd5b50610388858286016102a1565b9150509250929050565b6000815180845260005b818110156103b85760208185018101518683018201520161039c565b818111156103ca576000602083870101525b50601f01601f19169290920160200192915050565b82151581526040602082015260006103fa6040830184610392565b949350505050565b6040815260006104156040830185610392565b82810360208401526104278185610392565b9594505050505056fea2646970667358221220805f7414e00fe67d1d12b44977aa199685e393cf50b59d5ed0a402a9f95e8a2564736f6c634300080b0033";
+
+ #[test]
+ pub fn test_get_string() {
+ let byte_code = hex::decode(BYTE_CODE).unwrap();
+ let return_types = vec![ParamType::Bool, ParamType::String];
+ let json = serde_json::to_string(&json!({
+ "nested": {
+ "object": {
+ "property": "value"
+ }
+ }
+ }))
+ .unwrap();
+
+ // given
+ let input_data = prepare_function_call_input(
+ FUNCTION_HASH_0,
+ encode(&[Token::String(json), Token::String("/nested/object/property".to_string())]),
+ )
+ .unwrap();
+
+ // when
+ let (_, return_data) = execute_smart_contract(byte_code, input_data);
+
+ // then
+ let decoded = decode(&return_types, &return_data).unwrap();
+ assert_eq!(true, decoded[0].clone().into_bool().unwrap());
+ assert_eq!("value", decoded[1].clone().into_string().unwrap());
+ }
+
+ #[test]
+ pub fn test_get_bool() {
+ let byte_code = hex::decode(BYTE_CODE).unwrap();
+ let return_types = vec![ParamType::Bool, ParamType::Bool];
+ let json = serde_json::to_string(&json!({
+ "nested": {
+ "object": {
+ "property": true
+ }
+ }
+ }))
+ .unwrap();
+
+ // given
+ let input_data = prepare_function_call_input(
+ FUNCTION_HASH_1,
+ encode(&[Token::String(json), Token::String("/nested/object/property".to_string())]),
+ )
+ .unwrap();
+
+ // when
+ let (_, return_data) = execute_smart_contract(byte_code, input_data);
+
+ // then
+ let decoded = decode(&return_types, &return_data).unwrap();
+ assert_eq!(true, decoded[0].clone().into_bool().unwrap());
+ assert_eq!(true, decoded[1].clone().into_bool().unwrap());
+ }
+
+ #[test]
+ pub fn test_get_i64() {
+ let byte_code = hex::decode(BYTE_CODE).unwrap();
+ let return_types = vec![ParamType::Bool, ParamType::Int(64)];
+ let json = serde_json::to_string(&json!({
+ "nested": {
+ "object": {
+ "property": 14
+ }
+ }
+ }))
+ .unwrap();
+
+ // given
+ let input_data = prepare_function_call_input(
+ FUNCTION_HASH_2,
+ encode(&[Token::String(json), Token::String("/nested/object/property".to_string())]),
+ )
+ .unwrap();
+
+ // when
+ let (_, return_data) = execute_smart_contract(byte_code, input_data);
+
+ // then
+ let decoded = decode(&return_types, &return_data).unwrap();
+ assert_eq!(true, decoded[0].clone().into_bool().unwrap());
+ assert_eq!(U256::from(14), decoded[1].clone().into_int().unwrap());
+ }
+
+ #[test]
+ pub fn test_get_get_array_len() {
+ let byte_code = hex::decode(BYTE_CODE).unwrap();
+ let return_types = vec![ParamType::Bool, ParamType::Int(64)];
+ let json = serde_json::to_string(&json!({
+ "nested": {
+ "object": {
+ "property": [
+ {},
+ {},
+ {}
+ ]
+ }
+ }
+ }))
+ .unwrap();
+
+ // given
+ let input_data = prepare_function_call_input(
+ FUNCTION_HASH_3,
+ encode(&[Token::String(json), Token::String("/nested/object/property".to_string())]),
+ )
+ .unwrap();
+
+ // when
+ let (_, return_data) = execute_smart_contract(byte_code, input_data);
+
+ // then
+ let decoded = decode(&return_types, &return_data).unwrap();
+ assert_eq!(true, decoded[0].clone().into_bool().unwrap());
+ assert_eq!(U256::from(3), decoded[1].clone().into_int().unwrap());
+ }
+}
diff --git a/tee-worker/litentry/core/evm-dynamic-assertions/src/precompiles/macros.rs b/tee-worker/litentry/core/evm-dynamic-assertions/src/precompiles/macros.rs
index 738144cfd2..9a1c1b0073 100644
--- a/tee-worker/litentry/core/evm-dynamic-assertions/src/precompiles/macros.rs
+++ b/tee-worker/litentry/core/evm-dynamic-assertions/src/precompiles/macros.rs
@@ -14,6 +14,54 @@
// You should have received a copy of the GNU General Public License
// along with Litentry. If not, see .
+#[macro_export]
+macro_rules! json_get_fn {
+ ($name:ident, $token:ident, $parse_fn_name:ident) => {
+ pub fn $name(input: Vec) -> $crate::precompiles::PrecompileResult {
+ let decoded = match ethabi::decode(
+ &[ethabi::ParamType::String, ethabi::ParamType::String],
+ &input,
+ ) {
+ Ok(d) => d,
+ Err(e) => {
+ log::debug!("Could not decode bytes {:?}, reason: {:?}", input, e);
+ return Ok(failure_precompile_output(ethabi::Token::$token(Default::default())))
+ },
+ };
+
+ // safe to unwrap
+ let json = decoded.get(0).unwrap().clone().into_string().unwrap();
+ let pointer = decoded.get(1).unwrap().clone().into_string().unwrap();
+ let value: serde_json::Value = match serde_json::from_str(&json) {
+ Ok(v) => v,
+ Err(e) => {
+ log::debug!("Could not parse json {:?}, reason: {:?}", json, e);
+ return Ok(failure_precompile_output(ethabi::Token::$token(Default::default())))
+ },
+ };
+ let result = match value.pointer(&pointer) {
+ Some(v) => v,
+ None => {
+ log::debug!("No value under given pointer: :{:?}", pointer);
+ return Ok(failure_precompile_output(ethabi::Token::$token(Default::default())))
+ },
+ };
+
+ let encoded = match result.$parse_fn_name() {
+ Some(v) => ethabi::Token::$token(v.into()),
+ None => {
+ log::debug!(
+ "There is no value or it might be of different type, pointer: ${:?}",
+ pointer
+ );
+ return Ok(failure_precompile_output(ethabi::Token::$token(Default::default())))
+ },
+ };
+ Ok(success_precompile_output(encoded))
+ }
+ };
+}
+
#[macro_export]
macro_rules! http_get_precompile_fn {
($name:ident, $token:ident, $parse_fn_name:ident) => {
diff --git a/tee-worker/litentry/core/evm-dynamic-assertions/src/precompiles/mod.rs b/tee-worker/litentry/core/evm-dynamic-assertions/src/precompiles/mod.rs
index 1b1b9b75da..be55b1c24d 100644
--- a/tee-worker/litentry/core/evm-dynamic-assertions/src/precompiles/mod.rs
+++ b/tee-worker/litentry/core/evm-dynamic-assertions/src/precompiles/mod.rs
@@ -40,6 +40,7 @@ mod hex_to_number;
mod http_get;
mod http_post;
mod identity_to_string;
+mod json_utils;
mod macros;
mod parse_decimal;
mod parse_int;
@@ -77,6 +78,10 @@ impl PrecompileSet for Precompiles {
a if a == hash(1053) => Some(hex_to_number(handle.input().to_vec())),
a if a == hash(1054) => Some(parse_decimal(handle.input().to_vec())),
a if a == hash(1055) => Some(parse_int(handle.input().to_vec())),
+ a if a == hash(1100) => Some(json_utils::json_get_string(handle.input().to_vec())),
+ a if a == hash(1101) => Some(json_utils::json_get_i64(handle.input().to_vec())),
+ a if a == hash(1102) => Some(json_utils::json_get_bool(handle.input().to_vec())),
+ a if a == hash(1103) => Some(json_utils::get_array_len(handle.input().to_vec())),
_ => None,
}
}
@@ -107,6 +112,14 @@ impl PrecompileSet for Precompiles {
IsPrecompileResult::Answer { is_precompile: true, extra_cost: 0 },
a if a == hash(1055) =>
IsPrecompileResult::Answer { is_precompile: true, extra_cost: 0 },
+ a if a == hash(1100) =>
+ IsPrecompileResult::Answer { is_precompile: true, extra_cost: 0 },
+ a if a == hash(1101) =>
+ IsPrecompileResult::Answer { is_precompile: true, extra_cost: 0 },
+ a if a == hash(1102) =>
+ IsPrecompileResult::Answer { is_precompile: true, extra_cost: 0 },
+ a if a == hash(1103) =>
+ IsPrecompileResult::Answer { is_precompile: true, extra_cost: 0 },
_ => IsPrecompileResult::Answer { is_precompile: false, extra_cost: 0 },
}
}