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

Add ipfs data source #409

Merged
merged 2 commits into from
Sep 13, 2023
Merged
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
60 changes: 46 additions & 14 deletions src/context/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@ pub struct MatchstickInstanceContext<C: Blockchain> {
/// path to the file that matchstick should read and parse
pub(crate) ipfs: HashMap<String, String>,
templates: TemplateStore,
template_kinds: HashMap<String, String>,
}

/// Implementation of non-external functions.
Expand All @@ -170,6 +171,7 @@ impl<C: Blockchain> MatchstickInstanceContext<C> {
data_source_return_value: (None, None, None),
ipfs: HashMap::new(),
templates: HashMap::new(),
template_kinds: HashMap::new(),
};

// reads the graphql schema file and extracts all entities and their object types
Expand Down Expand Up @@ -964,7 +966,7 @@ impl<C: Blockchain> MatchstickInstanceContext<C> {
let name: String = asc_get(&self.wasm_ctx, _name_ptr, &GasCounter::new(), 0)?;
let params: Vec<String> = asc_get(&self.wasm_ctx, _params_ptr, &GasCounter::new(), 0)?;

data_source_create(name, params, None, &mut self.templates)
data_source_create(name, params, None, self)
}

/// function dataSource.createWithContext(
Expand All @@ -984,7 +986,7 @@ impl<C: Blockchain> MatchstickInstanceContext<C> {
asc_get(&self.wasm_ctx, _context_ptr, &GasCounter::new(), 0)?;
let context = DataSourceContext::from(context);

data_source_create(name, params, Some(context), &mut self.templates)
data_source_create(name, params, Some(context), self)
}

/// function dataSource.address(): Address
Expand All @@ -993,19 +995,33 @@ impl<C: Blockchain> MatchstickInstanceContext<C> {
_gas: &GasCounter,
) -> Result<AscPtr<Uint8Array>, HostExportError> {
let default_address_val = "0x0000000000000000000000000000000000000000";

let result = match &self.data_source_return_value.0 {
Some(value) => asc_new(
&mut self.wasm_ctx,
&Address::from_str(value).expect("Couldn't create Address."),
&GasCounter::new(),
)
.expect("Couldn't create pointer."),
None => asc_new(
&mut self.wasm_ctx,
&Address::from_str(default_address_val).expect("Couldn't create Address."),
&GasCounter::new(),
)
.expect("Couldn't create pointer."),
Some(value) => {
let address = Address::from_str(value).unwrap_or_default();
// checks whether the value is a valid ethereum address and parses it
// otherwise it is considered as ipfs cid
// Zero address is considered as valid only if matches the mocked value
if !address.is_zero() || value.eq(default_address_val) {
asc_new(&mut self.wasm_ctx, &address, &GasCounter::new())
.expect("Couldn't create pointer.")
} else {
asc_new(&mut self.wasm_ctx, value.as_bytes(), &GasCounter::new())
.expect("Couldn't create pointer.")
}
}
None => {
logging::error!(
"No mocked Eth address or Ipfs CID found, so fallback to Eth Zero address"
);

asc_new(
&mut self.wasm_ctx,
&Address::from_str(default_address_val).expect("Couldn't create address"),
&GasCounter::new(),
)
.expect("Couldn't create pointer.")
}
};

Ok(result)
Expand Down Expand Up @@ -1090,6 +1106,22 @@ impl<C: Blockchain> MatchstickInstanceContext<C> {
}
}

/// function readFile(path: string): Bytes
pub fn read_file(
&mut self,
_gas: &GasCounter,
file_path_ptr: AscPtr<AscString>,
) -> Result<AscPtr<Uint8Array>, HostExportError> {
let file_path: String = asc_get(&self.wasm_ctx, file_path_ptr, &GasCounter::new(), 0)?;

let string = std::fs::read_to_string(&file_path).unwrap_or_else(|err| {
logging::critical!("Failed to read file `{}` with error: {}", &file_path, err)
});
let result = asc_new(&mut self.wasm_ctx, string.as_bytes(), &GasCounter::new())?;

Ok(result)
}

/// function mockIpfsFile(hash: string, file_path: string): void
pub fn mock_ipfs_file(
&mut self,
Expand Down
37 changes: 25 additions & 12 deletions src/context/template.rs
Original file line number Diff line number Diff line change
@@ -1,33 +1,37 @@
use super::{MatchstickInstanceContext, TemplateInfo, TemplateStore};
use std::collections::HashMap;

use super::{MatchstickInstanceContext, TemplateInfo};
use anyhow::{Context as _, Result};
use graph::{
prelude::DataSourceContext,
runtime::{DeterministicHostError, HostExportError},
};
use std::collections::HashMap;

pub(crate) fn populate_templates<C: graph::blockchain::Blockchain>(
context: &mut MatchstickInstanceContext<C>,
) {
crate::MANIFEST_LOCATION.with(|path| {
let names = crate::parser::collect_template_names(
let templates = crate::parser::collect_templates(
path.borrow().to_str().expect("Cannot convert to string."),
);

names.iter().for_each(|name| {
context.templates.insert(name.to_string(), HashMap::new());
templates.iter().for_each(|(name, kind)| {
context
.template_kinds
.insert(name.to_string(), kind.to_string());
});
});
}

pub(crate) fn data_source_create(
pub(crate) fn data_source_create<C: graph::blockchain::Blockchain>(
name: String,
params: Vec<String>,
context: Option<DataSourceContext>,
templates: &mut TemplateStore,
instance_ctx: &mut MatchstickInstanceContext<C>,
) -> Result<(), HostExportError> {
// Resolve the name into the right template
templates
instance_ctx
.template_kinds
.iter()
.find(|template| template.0 == &name)
.with_context(|| {
Expand All @@ -36,7 +40,8 @@ pub(crate) fn data_source_create(
No template with this name available. \
Available names: {}.",
name,
templates
instance_ctx
.template_kinds
.iter()
.map(|template| template.0.to_owned())
.collect::<Vec<_>>()
Expand All @@ -45,15 +50,23 @@ pub(crate) fn data_source_create(
})
.map_err(DeterministicHostError::from)?;

let kind = instance_ctx.template_kinds.get(&name).unwrap();

let template_info = TemplateInfo {
kind: "ethereum/contract".to_string(),
kind: kind.to_string(),
name: name.clone(),
address: params[0].clone(),
context,
};

let template = templates.get_mut(&name).expect("Template not found.");
template.insert(params[0].clone(), template_info);
if instance_ctx.templates.contains_key(&name) {
let template = instance_ctx.templates.get_mut(&name).unwrap();
template.insert(params[0].clone(), template_info);
} else {
let mut template: HashMap<String, TemplateInfo> = HashMap::new();
template.insert(params[0].clone(), template_info);
instance_ctx.templates.insert(name.clone(), template);
}

Ok(())
}
1 change: 1 addition & 0 deletions src/instance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,7 @@ impl<C: Blockchain> MatchstickInstance<C> {
);

link!("mockIpfsFile", mock_ipfs_file, hash, file_path);
link!("readFile", read_file, file_path);

link!("ipfs.cat", mock_ipfs_cat, "host_export_ipfs_cat", hash_ptr);
link!(
Expand Down
7 changes: 4 additions & 3 deletions src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,16 +93,17 @@ pub fn collect_handlers(path: &str) -> HashMap<String, Vec<String>> {
.collect()
}

pub fn collect_template_names(path: &str) -> Vec<String> {
pub fn collect_templates(path: &str) -> Vec<(String, String)> {
let subgraph_yaml = parse_yaml(path);

extract_vec(&subgraph_yaml, "templates")
.iter()
.filter_map(|template| {
let kind = template.get("kind").unwrap().as_str().unwrap().to_owned();
if kind == "ethereum/contract" {

if kind == "ethereum/contract" || kind == "file/ipfs" {
let name = template.get("name").unwrap().as_str().unwrap().to_owned();
Some(name)
Some((name, kind))
} else {
None
}
Expand Down