-
Notifications
You must be signed in to change notification settings - Fork 987
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
fix(offchain): Enforce entities list on offchain data sources #4147
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,7 +15,7 @@ use graph::components::subgraph::{ | |
PoICausalityRegion, ProofOfIndexingEvent, SharedProofOfIndexing, | ||
}; | ||
use graph::data::store; | ||
use graph::data_source::{DataSource, DataSourceTemplate}; | ||
use graph::data_source::{DataSource, DataSourceTemplate, EntityTypeAccess}; | ||
use graph::ensure; | ||
use graph::prelude::ethabi::param_type::Reader; | ||
use graph::prelude::ethabi::{decode, encode, Token}; | ||
|
@@ -63,6 +63,7 @@ pub struct HostExports<C: Blockchain> { | |
data_source_address: Vec<u8>, | ||
subgraph_network: String, | ||
data_source_context: Arc<Option<DataSourceContext>>, | ||
entity_type_access: EntityTypeAccess, | ||
|
||
/// Some data sources have indeterminism or different notions of time. These | ||
/// need to be each be stored separately to separate causality between them, | ||
|
@@ -89,6 +90,7 @@ impl<C: Blockchain> HostExports<C> { | |
data_source_name: data_source.name().to_owned(), | ||
data_source_address: data_source.address().unwrap_or_default(), | ||
data_source_context: data_source.context().cheap_clone(), | ||
entity_type_access: data_source.entities(), | ||
causality_region: PoICausalityRegion::from_network(&subgraph_network), | ||
subgraph_network, | ||
templates, | ||
|
@@ -97,6 +99,21 @@ impl<C: Blockchain> HostExports<C> { | |
} | ||
} | ||
|
||
/// Enfore the entity type access restrictions. See also: entity-type-access | ||
fn check_entity_type_access(&self, entity_type: &EntityType) -> Result<(), HostExportError> { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. even though this is not exported I think we should a bit of context here as a comment There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've written a short comment and linked it to the one on |
||
match self.entity_type_access.allows(entity_type) { | ||
true => Ok(()), | ||
false => Err(HostExportError::Deterministic(anyhow!( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could this error be added as a typed error? Seems quite specific to be matched on string so I think it could be useful to have an IsolationError subtype There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It could, but given that for now we don't need to match on it I think it's fine to just use the |
||
"entity type `{}` is not on the 'entities' list for data source `{}`. \ | ||
Hint: Add `{}` to the 'entities' list, which currently is: `{}`.", | ||
entity_type, | ||
self.data_source_name, | ||
entity_type, | ||
self.entity_type_access | ||
))), | ||
} | ||
} | ||
|
||
pub(crate) fn abort( | ||
&self, | ||
message: Option<String>, | ||
|
@@ -139,7 +156,7 @@ impl<C: Blockchain> HostExports<C> { | |
data: HashMap<String, Value>, | ||
stopwatch: &StopwatchMetrics, | ||
gas: &GasCounter, | ||
) -> Result<(), anyhow::Error> { | ||
) -> Result<(), HostExportError> { | ||
let poi_section = stopwatch.start_section("host_export_store_set__proof_of_indexing"); | ||
write_poi_event( | ||
proof_of_indexing, | ||
|
@@ -157,6 +174,7 @@ impl<C: Blockchain> HostExports<C> { | |
entity_type: EntityType::new(entity_type), | ||
entity_id: entity_id.into(), | ||
}; | ||
self.check_entity_type_access(&key.entity_type)?; | ||
|
||
gas.consume_host_fn(gas::STORE_SET.with_args(complexity::Linear, (&key, &data)))?; | ||
|
||
|
@@ -188,6 +206,7 @@ impl<C: Blockchain> HostExports<C> { | |
entity_type: EntityType::new(entity_type), | ||
entity_id: entity_id.into(), | ||
}; | ||
self.check_entity_type_access(&key.entity_type)?; | ||
|
||
gas.consume_host_fn(gas::STORE_REMOVE.with_args(complexity::Size, &key))?; | ||
|
||
|
@@ -207,6 +226,7 @@ impl<C: Blockchain> HostExports<C> { | |
entity_type: EntityType::new(entity_type), | ||
entity_id: entity_id.into(), | ||
}; | ||
self.check_entity_type_access(&store_key.entity_type)?; | ||
|
||
let result = state.entity_cache.get(&store_key)?; | ||
gas.consume_host_fn(gas::STORE_GET.with_args(complexity::Linear, (&store_key, &result)))?; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,6 +5,7 @@ use std::time::Duration; | |
|
||
use graph::blockchain::block_stream::BlockWithTriggers; | ||
use graph::blockchain::{Block, BlockPtr, Blockchain}; | ||
use graph::data::subgraph::schema::SubgraphError; | ||
use graph::data_source::CausalityRegion; | ||
use graph::env::EnvVars; | ||
use graph::object; | ||
|
@@ -142,7 +143,8 @@ async fn file_data_sources() { | |
let block_2 = empty_block(block_1.ptr(), test_ptr(2)); | ||
let block_3 = empty_block(block_2.ptr(), test_ptr(3)); | ||
let block_4 = empty_block(block_3.ptr(), test_ptr(4)); | ||
vec![block_0, block_1, block_2, block_3, block_4] | ||
let block_5 = empty_block(block_4.ptr(), test_ptr(5)); | ||
vec![block_0, block_1, block_2, block_3, block_4, block_5] | ||
}; | ||
let stop_block = test_ptr(1); | ||
|
||
|
@@ -204,6 +206,7 @@ async fn file_data_sources() { | |
ctx.provider.stop(ctx.deployment.clone()).await.unwrap(); | ||
let stop_block = test_ptr(4); | ||
ctx.start_and_sync_to(stop_block).await; | ||
ctx.provider.stop(ctx.deployment.clone()).await.unwrap(); | ||
let writable = ctx | ||
.store | ||
.clone() | ||
|
@@ -219,6 +222,19 @@ async fn file_data_sources() { | |
assert!(data_source.causality_region == causality_region.next()); | ||
causality_region = causality_region.next(); | ||
} | ||
|
||
let stop_block = test_ptr(5); | ||
let err = ctx.start_and_sync_to_error(stop_block).await; | ||
let message = "entity type `IpfsFile1` is not on the 'entities' list for data source `File2`. \ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. would it be enough to check if this is an isolation error somehow (left a suggestion about subtype but that might be lost). Maybe we could match an important part of the match like IpfsFile1 or somnething rather than the full message which also includes a backtrace. As it is it will be potentially broken if this function changes. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If the test breaks when functionality changes, that's good! Gives us a chance to check that the change was intended. Even if its for the backtrace which is not directly related to what's being tested. |
||
Hint: Add `IpfsFile1` to the 'entities' list, which currently is: `IpfsFile`.\twasm backtrace:\t 0: 0x33bf - <unknown>!src/mapping/handleFile1\t in handler `handleFile1` at block #5 ()".to_string(); | ||
let expected_err = SubgraphError { | ||
subgraph_id: ctx.deployment.hash.clone(), | ||
message, | ||
block_ptr: Some(test_ptr(5)), | ||
handler: None, | ||
deterministic: false, | ||
}; | ||
assert_eq!(err, expected_err); | ||
} | ||
|
||
#[tokio::test] | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Might be worth adding a bit more here about how this interacts with causality region based isolation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've written a more detailed comment.