Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding specific function #202

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
118 changes: 117 additions & 1 deletion crates/executor/executor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,19 @@ pub async fn op_smartweave_read_contract(
pub fn generate_interaction_context(
tx: &GQLNodeInterface,
) -> InteractionContext {
let tags = tx.tags.to_owned();

let specific_function = {
let data = tags
.into_iter()
.find(|tag| tag.name == String::from("3EM-Function"));
if let Some(specific_function_tag) = data {
Some(specific_function_tag.value)
} else {
None
}
};

InteractionContext {
transaction: InteractionTx {
id: tx.id.to_owned(),
Expand Down Expand Up @@ -142,6 +155,7 @@ pub fn generate_interaction_context(
height: tx.block.height.to_owned(),
timestamp: tx.block.timestamp.to_owned(),
},
specific_function,
}
}

Expand Down Expand Up @@ -203,8 +217,11 @@ pub async fn raw_execute_contract<
});

let interaction_context = generate_interaction_context(&tx);
let specific_fn = interaction_context.specific_function.clone();

let valid = match rt.call(call_input, Some(interaction_context)).await
let valid = match rt
.call(call_input, Some(interaction_context), specific_fn)
.await
{
Ok(None) => serde_json::Value::Bool(true),
Ok(Some(CallResult::Evolve(evolve))) => {
Expand Down Expand Up @@ -674,6 +691,105 @@ mod tests {
}
}

#[tokio::test]
pub async fn test_executor_specific_function_js() {
let init_state = serde_json::json!({
"users": []
});

let fake_contract = generate_fake_loaded_contract_data(
include_bytes!(
"../../testdata/contracts/user_function_specific_contract.js"
),
ContractType::JAVASCRIPT,
init_state.to_string(),
);
let fake_interactions = vec![
generate_fake_interaction(
serde_json::json!({
"function": "addUser",
"username": "Andres"
}),
"tx1",
None,
None,
None,
None,
None,
None,
None,
None,
),
generate_fake_interaction(
serde_json::json!({
"username": "Tate"
}),
"tx2",
None,
None,
None,
None,
Some(GQLTagInterface {
name: String::from("3EM-Function"),
value: String::from("addUser"),
}),
None,
None,
None,
),
];

let execute = || async {
let result = raw_execute_contract(
String::new(),
fake_contract,
fake_interactions,
IndexMap::new(),
None,
true,
false,
|_, _| {
panic!("not implemented");
},
&Arweave::new(
443,
"arweave.net".to_string(),
String::from("https"),
ArweaveCache::new(),
),
HashMap::new(),
)
.await;

result
};

if let ExecuteResult::V8(value, validity, exm_context) = execute().await {
assert_eq!(validity.len(), 2);
let (tx1, tx2) = (validity.get("tx1"), validity.get("tx2"));
assert_eq!(tx1.is_some(), true);
assert_eq!(tx2.is_some(), true);
assert_eq!((tx1.unwrap()).to_owned(), true);
assert_eq!((tx2.unwrap()).to_owned(), true);

let value_state = value.get("users");
assert_eq!(value_state.is_some(), true);
let users = value_state
.unwrap()
.to_owned()
.as_array()
.unwrap()
.to_owned();
assert_eq!(
users.get(0).unwrap().to_owned(),
serde_json::json!("Andres")
);
assert_eq!(users.get(1).unwrap().to_owned(), serde_json::json!("Tate"));
} else {
panic!("Failed");
}
}

#[tokio::test]
async fn test_contract_evolve() {
let init_state = serde_json::json!({
Expand Down
37 changes: 20 additions & 17 deletions crates/js/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,7 @@ impl Runtime {
&mut self,
action: R,
interaction_data: Option<InteractionContext>,
specific_fn: Option<String>,
) -> Result<Option<CallResult>, AnyError>
where
R: Serialize + 'static,
Expand All @@ -225,11 +226,13 @@ impl Runtime {
}
};

let func_name = &specific_fn.unwrap_or(String::from("handle"))[..];

let action: v8::Local<v8::Value> =
serde_v8::to_v8(scope, action).unwrap();

let module_obj = self.module.open(scope).to_object(scope).unwrap();
let key = v8::String::new(scope, "handle").unwrap().into();
let key = v8::String::new(scope, func_name).unwrap().into();
let func_obj = module_obj.get(scope, key).unwrap();
let func = v8::Local::<v8::Function>::try_from(func_obj).unwrap();

Expand Down Expand Up @@ -334,7 +337,7 @@ mod test {
.await
.unwrap();

rt.call((), None).await.unwrap();
rt.call((), None, None).await.unwrap();

let value = rt.get_contract_state::<i32>().unwrap();
assert_eq!(value, -69);
Expand All @@ -359,7 +362,7 @@ export async function handle(slice) {
.await
.unwrap();

rt.call((), None).await.unwrap();
rt.call((), None, None).await.unwrap();
let hash = rt.get_contract_state::<[u8; 20]>().unwrap();
assert_eq!(
hash.to_vec(),
Expand Down Expand Up @@ -398,7 +401,7 @@ try {
.await
.unwrap();

rt.call((), None).await.unwrap();
rt.call((), None, None).await.unwrap();
let calls = rt.get_exm_context::<ExmContext>().unwrap();

let tx_id = rt.get_contract_state::<String>().unwrap();
Expand Down Expand Up @@ -436,11 +439,11 @@ export async function handle() {
.await
.unwrap();

rt.call((), None).await.unwrap();
rt.call((), None, None).await.unwrap();
let rand1 = rt.get_contract_state::<f64>().unwrap();
assert_eq!(rand1, 0.3800000002095474);

rt.call((), None).await.unwrap();
rt.call((), None, None).await.unwrap();
let rand2 = rt.get_contract_state::<f64>().unwrap();
assert_eq!(rand2, 0.1933761369163034);
}
Expand All @@ -463,11 +466,11 @@ export async function handle() {
.await
.unwrap();

rt.call((), None).await.unwrap();
rt.call((), None, None).await.unwrap();
let rand1 = rt.get_contract_state::<[u8; 8]>().unwrap();
assert_eq!(rand1.as_ref(), &[127, 111, 44, 205, 178, 63, 42, 187]);

rt.call((), None).await.unwrap();
rt.call((), None, None).await.unwrap();
let rand2 = rt.get_contract_state::<[u8; 8]>().unwrap();
assert_eq!(rand2.as_ref(), &[123, 105, 39, 142, 148, 124, 1, 198]);
}
Expand Down Expand Up @@ -496,7 +499,7 @@ export async function handle() {
.await
.unwrap();

rt.call(&(), None).await.unwrap();
rt.call(&(), None, None).await.unwrap();
let gced = rt.get_contract_state::<bool>().unwrap();
assert_eq!(gced, false);
}
Expand All @@ -522,7 +525,7 @@ export async function handle() {
.await
.unwrap();

rt.call((), None).await.unwrap();
rt.call((), None, None).await.unwrap();
let exists = rt.get_contract_state::<bool>().unwrap();
assert_eq!(exists, true);
}
Expand All @@ -544,7 +547,7 @@ export async function handle() {
.unwrap();

let err = rt
.call((), None)
.call((), None, None)
.await
.unwrap_err()
.downcast::<Error>()
Expand Down Expand Up @@ -572,7 +575,7 @@ export async function handle() {
.await
.unwrap();

let evolved = rt.call((), None).await.unwrap();
let evolved = rt.call((), None, None).await.unwrap();
assert_eq!(evolved, Some(CallResult::Evolve("xxdummy".to_string())));
}

Expand All @@ -592,7 +595,7 @@ export async function handle() {
.await
.unwrap();

rt.call((), None).await.unwrap();
rt.call((), None, None).await.unwrap();
let host = rt.get_contract_state::<String>().unwrap();
assert_eq!(host, "http://arweave.net:12345");
}
Expand Down Expand Up @@ -625,7 +628,7 @@ export async function handle() {
.await
.unwrap();

rt.call((), None).await.unwrap();
rt.call((), None, None).await.unwrap();
let data = rt.get_contract_state::<Vec<String>>().unwrap();
assert_eq!(data.get(0).unwrap(), "App-Name");
assert_eq!(data.get(1).unwrap(), "SmartWeaveContract");
Expand Down Expand Up @@ -663,7 +666,7 @@ export async function handle() {
.await
.unwrap();

rt.call((), None).await.unwrap();
rt.call((), None, None).await.unwrap();
let data = rt
.get_contract_state::<(String, bool, deno_core::serde_json::Value)>()
.unwrap();
Expand Down Expand Up @@ -707,7 +710,7 @@ try {
interaction_data.transaction.id =
String::from("YzVdaDBnaiGToFQJAnJCGtyJwJZbaCASotWEPFhBgBY");

rt.call((), Some(interaction_data)).await.unwrap();
rt.call((), Some(interaction_data), None).await.unwrap();
let host = rt.get_contract_state::<Vec<String>>().unwrap();
assert_eq!(host.get(0).unwrap().to_owned(), String::from("eyJjb250ZW50Ijp7ImJvZHkiOiJoZWxsbyB3b3JsZCIsInRpbWVzdGFtcCI6MTY0MTYzNDQzOCwidGl0bGUiOiJoZWxsbyB3b3JsZCJ9LCJkaWdlc3QiOiJOLVJ6UmZXMmxzV0RqdV9GcE5RUzZhWmdzTzdma1Z5eVo3bWJzRWhpNjZ3IiwiYXV0aG9yc2hpcCI6eyJjb250cmlidXRvciI6IjB4ZWFlYjIyNjIwREJEOTgwRTc0NjNjOWQ5NkE1YWU0ZDk0NDhiMTMzYyIsInNpZ25pbmdLZXkiOiJ7XCJjcnZcIjpcIlAtMjU2XCIsXCJleHRcIjp0cnVlLFwia2V5X29wc1wiOltcInZlcmlmeVwiXSxcImt0eVwiOlwiRUNcIixcInhcIjpcImRVcEI3MVhJV1lZYjBQSGlqdkJicTNtMl9CNFA2aGZFZHNBdndkS0JDNk1cIixcInlcIjpcIlBHRmlveWtaODU4cHN3cEZXODZtdjZKQlBFZ1Y4WFNVZWY5M3pqNUFoNFFcIn0iLCJzaWduYXR1cmUiOiJ1WE9YWVJrX1dFVmdaM0xOR0xhWUVsNmVGYS1xa1JyWURwRjJ5ektwUXJhS1I1R0lqWEdiSXg4QUFZQ0VPNEZEQXhVeF93bjVkUWR1RldZZTNKTHEtUSIsInNpZ25pbmdLZXlTaWduYXR1cmUiOiIweDhhMmFjNTE3NTg5OTA2NDkwMWEzZTBlM2VjODc0ZjZmOTFjMWE3NzVjODM4NjNmMDY2OWE0YjUxMjhiYjgxODcwYTQzMmUwZTM1YjAwMjUxZTdmMTg0ZTRlYzE2ZTNjOWVkNDM0YjBjMjkzMDc3N2I3M2UzMzg3MDk5MWQ0NjhlMWIiLCJzaWduaW5nS2V5TWVzc2FnZSI6IkkgYXV0aG9yaXplIHB1Ymxpc2hpbmcgb24gbWlycm9yLnh5eiBmcm9tIHRoaXMgZGV2aWNlIHVzaW5nOlxue1wiY3J2XCI6XCJQLTI1NlwiLFwiZXh0XCI6dHJ1ZSxcImtleV9vcHNcIjpbXCJ2ZXJpZnlcIl0sXCJrdHlcIjpcIkVDXCIsXCJ4XCI6XCJkVXBCNzFYSVdZWWIwUEhpanZCYnEzbTJfQjRQNmhmRWRzQXZ3ZEtCQzZNXCIsXCJ5XCI6XCJQR0Zpb3lrWjg1OHBzd3BGVzg2bXY2SkJQRWdWOFhTVWVmOTN6ajVBaDRRXCJ9IiwiYWxnb3JpdGhtIjp7Im5hbWUiOiJFQ0RTQSIsImhhc2giOiJTSEEtMjU2In19LCJuZnQiOnt9LCJ2ZXJzaW9uIjoiMTItMjEtMjAyMCIsIm9yaWdpbmFsRGlnZXN0IjoiTi1SelJmVzJsc1dEanVfRnBOUVM2YVpnc083ZmtWeXlaN21ic0VoaTY2dyJ9"));
assert_eq!(
Expand All @@ -732,7 +735,7 @@ export async function handle() {
.unwrap();

let result = rt
.call((), None)
.call((), None, None)
.await
.unwrap()
.expect("Expected CallResult");
Expand Down
1 change: 1 addition & 0 deletions crates/smartweave/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ pub struct InteractionBlock {
pub struct InteractionContext {
pub transaction: InteractionTx,
pub block: InteractionBlock,
pub specific_function: Option<String>,
}

pub fn init(
Expand Down
17 changes: 17 additions & 0 deletions testdata/contracts/user_function_specific_contract.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
export async function addUser(state, action) {
state.users.push(action.input.username);
return state;
}

export async function handle(state, action) {

if(action.input.function === 'addUser') {
state.users.push(action.input.username);
} else {
throw new Error("Invalid operation");
}

return {
state
};
}