Skip to content

Commit

Permalink
Decentland ark issue (#231)
Browse files Browse the repository at this point in the history
  • Loading branch information
andreespirela authored Mar 13, 2023
1 parent 726afb0 commit b9b7624
Show file tree
Hide file tree
Showing 4 changed files with 311 additions and 1 deletion.
2 changes: 1 addition & 1 deletion crates/exm/base.js
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@
} else {
try {
if (this.requests[reqHash]) {
return Object.freeze(this.requests[reqHash])
return Object.freeze(BaseReqResponse.from(this.requests[reqHash]))
} else {
const fetchData = await props.fetch(...args);
const buff = await fetchData.arrayBuffer();
Expand Down
42 changes: 42 additions & 0 deletions js/napi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -553,4 +553,46 @@ mod tests {
let str_state = contract_result.to_string();
assert!(str_state.contains("wearemintingyes"));
}

#[tokio::test]
pub async fn simulate_contract_ark() {
let contract_source_bytes =
include_bytes!("../../../testdata/contracts/ark.js");
let contract_source_vec = contract_source_bytes.to_vec();
let execution_context: SimulateExecutionContext =
SimulateExecutionContext {
contract_id: String::new(),
interactions: vec![SimulateInput {
id: String::from("abcd"),
owner: String::from("210392sdaspd-asdm-asd_sa0d1293-lc"),
quantity: String::from("12301"),
reward: String::from("12931293"),
target: None,
tags: vec![],
block: None,
input: serde_json::json!({
"function": "createContainer",
"caller_address": "0x197f818c1313dc58b32d88078ecdfb40ea822614",
"type": "evm",
"label": "test-evm",
"sig": "0x0e8cda3652185efcb9e68cbd932836c46df14dcf3b052931f463f0cd189a0fdd619206accafbb44afc155ce1c629da2eda4370fd71376ad250f28b50711b112b1c"
}).to_string(),
}],
contract_init_state: Some(String::from(include_str!("../../../testdata/contracts/ark.json"))),
maybe_config: None,
maybe_cache: Some(false),
maybe_bundled_contract: None,
maybe_settings: None,
maybe_exm_context: None,
maybe_contract_source: Some(ContractSource {
contract_src: contract_source_vec.into(),
contract_type: SimulateContractType::JAVASCRIPT,
}),
};

let contract = simulate_contract(execution_context).await.unwrap();
assert_eq!(contract.errors.len(), 0);
let contract_result = contract.state;
let str_state = contract_result.to_string();
}
}
252 changes: 252 additions & 0 deletions testdata/contracts/ark.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,252 @@
export async function handle(state, action) {
const input = action.input;

const signatures = state.signatures;
let message_counter = state.message_counter;

// if (input.function === "createContainer") {
// const { caller_address, sig, type, label } = input;
// ContractAssert(
// caller_address && sig && type,
// "ERROR_MISSING_REQUIRED_ARGUMENTS"
// );


// const caller = type === "ar" ? await _ownerToAddress(caller_address) : caller_address;

// ContractAssert(
// typeof label === "string" && label.trim().length,
// "ERROR_INVALID_CONTAINER_LABEL"
// );

// type === "ar"
// ? await _verifyArSignature(caller_address, sig, state.messages.ar)
// : await _moleculeSignatureVerification(
// caller_address,
// btoa(state.messages.evm + state.message_counter),
// sig,
// type
// );

// const timestamp = EXM.getDate().getTime();

// state.containers.push({
// id: SmartWeave.transaction.id,
// label: label.trim(),
// controller_address: caller,
// network: type,
// first_linkage: timestamp,
// last_modification: timestamp,
// addresses: [{ address: caller, network: type, proof: sig }],
// vouched_by: [],
// });

// return { state };
// }

if (input.function === "createContainer") {
try {
const { caller_address, sig, type, label } = input;
const callerMessage = btoa(state.messages.evm + state.message_counter)
ContractAssert(
caller_address && sig && type,
"ERROR_MISSING_REQUIRED_ARGUMENTS"
);

let caller;
EXM.print("createContainer @ 1\n")
if (type === "ar") {
caller = await _ownerToAddress(caller_address)
} else {
caller = await _moleculeAddr(caller_address, callerMessage, sig, type);
}
EXM.print(`createContainer @ 2 -- caller resolved: ${caller}\n`)


// const caller = type === "ar" ? await _ownerToAddress(caller_address) : await
// ContractAssert(
// typeof label === "string" && label.trim().length,
// "ERROR_INVALID_CONTAINER_LABEL"
// );

type === "ar"
? await _verifyArSignature(caller_address, sig, state.messages.ar)
: await _moleculeSignatureVerification(
caller_address,
btoa(state.messages.evm + state.message_counter),
sig,
type
);

EXM.print("createContainer @ 3\n")

const timestamp = EXM.getDate().getTime();

state.containers.push({
id: SmartWeave.transaction.id,
label: label.trim(),
controller_address: caller,
network: type,
first_linkage: timestamp,
last_modification: timestamp,
addresses: [{ address: caller, network: type, proof: sig }],
vouched_by: [],
});

EXM.print("createContainer @ 4\n")

return { state };
} catch(error) {
EXM.print(error)
throw new ContractError("error")
}
}


// UTILS

function _getContainerIndex(id) {
const index = state.containers.findIndex(
(container) => container.id === id
);
ContractAssert(index >= 0, "ERROR_INVALID_CONTAINER_ID");
return index;
}

async function _ownerToAddress(pubkey) {
try {
const req = await EXM.deterministicFetch(
`${state.molecule_endpoints.ar}/${pubkey}`
);
const address = req.asJSON()?.address;
_validateArweaveAddress(address);
return address;
} catch (error) {
throw new ContractError("ERROR_MOLECULE_SERVER_ERROR");
}
}

async function _typeToMolecule(type) {
switch (type) {
case "evm":
return `${state.molecule_endpoints.evm}`;
case "sol":
return `${state.molecule_endpoints.sol}`;
case "tez":
return `${state.molecule_endpoints.tez}`;
}
}

async function _getStateAddresses() {
return state.containers
.map((container) => container.addresses)
.flat()
.map((obj) => obj.address);
}

async function _moleculeSignatureVerification(
caller,
message,
signature,
type
) {
try {
EXM.print(`_moleculeSignatureVerification @ 1`)
ContractAssert(!signatures.includes(signature));
const moleculeEndpoint = await _typeToMolecule(type);
const endpoint = `${moleculeEndpoint}/${caller}/${message}/${signature}`;
EXM.print(endpoint);
const isValid = await EXM.deterministicFetch(
endpoint
);
EXM.print(isValid);
EXM.print(`_moleculeSignatureVerification @ 2`)
// EXM.print("molecule here 1")
// EXM.print(`here---> ${isValid.asJSON()}`)
// EXM.print(`result: ${isValid.asJSON()?.result}`)
EXM.print(`_moleculeSignatureVerification @ 3: ${isValid.asJSON()}`)
ContractAssert(isValid.asJSON()?.result, "ERROR_INVALID_CALLER");
EXM.print(`_moleculeSignatureVerification @ 4`)
signatures.push(signature);
state.message_counter += 1;
// EXM.print("molecule here 1")
if (isValid.asJSON()?.address) {
return isValid.asJSON()?.address;
}
return caller;
} catch (error) {
EXM.print(error.stack)
throw new ContractError("ERROR_MOLECULE_CONNECTION");
}
}


async function _moleculeAddr(
caller,
message,
signature,
type
) {
try {
EXM.print("_moleculeAddr @ 1\n")
const moleculeEndpoint = await _typeToMolecule(type);
const isValid = await EXM.deterministicFetch( // mask the DF and the code will work
`${moleculeEndpoint}/${caller}/${message}/${signature}`
);
EXM.print("_moleculeAddr @ 2\n")

if (isValid.asJSON()?.address) {
return isValid.asJSON()?.address;
}
// EXM.print(isValid.asJSON())
// EXM.print(caller)
EXM.print("_moleculeAddr @ 3\n")
return caller;

} catch (error) {
EXM.print("error")
throw new ContractError("ERROR_MOLECULE_CONNECTION");
}
}


async function _verifyArSignature(owner, signature, message) {
try {
_validatePubKeySyntax(owner);

const encodedMessage = new TextEncoder().encode(message);
const typedArraySig = Uint8Array.from(atob(signature), (c) =>
c.charCodeAt(0)
);
const isValid = await SmartWeave.arweave.crypto.verify(
owner,
encodedMessage,
typedArraySig
);

ContractAssert(isValid, "ERROR_INVALID_CALLER_SIGNATURE");
ContractAssert(
!state.signatures.includes(signature),
"ERROR_SIGNATURE_ALREADY_USED"
);
state.signatures.push(signature);
// return await _ownerToAddress(owner)
} catch (error) {
throw new ContractError("ERROR_INVALID_CALLER_SIGNATURE");
}
}

function _validateArweaveAddress(address) {
ContractAssert(
/[a-z0-9_-]{43}/i.test(address),
"ERROR_INVALID_ARWEAVE_ADDRESS"
);
}

function _validatePubKeySyntax(jwk_n) {
ContractAssert(
typeof jwk_n === "string" && jwk_n?.length === 683,
"ERROR_INVALID_JWK_N_SYNTAX"
);
}
}
16 changes: 16 additions & 0 deletions testdata/contracts/ark.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"containers": [],
"molecule_endpoints": {
"evm": "http://evm.molecule.sh/signer",
"ar": "http://ar.molecule.sh/ota",
"sol": "http://sol.molecule.sh/auth",
"tez": "http://tez.molecule.sh"
},
"message_counter": 0,
"messages": {
"ar": "ark-variant--",
"evm": "ark-variant--"
},
"signatures": []

}

0 comments on commit b9b7624

Please sign in to comment.