Skip to content

Commit 2e253fb

Browse files
committed
gnd: Add graph-node-style Ethereum validation (kind, address, block handlers)
Port Ethereum-specific validation from graph-node's DataSource::validate(): - Validate data source kind is 'ethereum' or 'ethereum/contract' - Require source address when call or block handlers are present - Enforce block handler constraints (no duplicates per filter type, no mixing non-filtered with polling/once) - Validate receipt requires apiVersion >= 0.0.7 - Validate eth call declarations require specVersion >= 1.2.0 To support this, enrich gnd's simplified types: event_handlers now carry receipt and call-decl flags, block_handlers carry filter kind. These structural validations run during build (validate_manifest) but not during codegen to avoid blocking code generation for manifests with semantic issues. Also fix codegen snapshot tests to use valid manifests (ABIs listed, handlers defined) per validation added in previous commits.
1 parent f7b173e commit 2e253fb

3 files changed

Lines changed: 569 additions & 19 deletions

File tree

gnd/src/commands/codegen.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -639,7 +639,12 @@ dataSources:
639639
file: ./mapping.ts
640640
entities:
641641
- MyEntity
642-
abis: []
642+
abis:
643+
- name: TestContract
644+
file: ./TestContract.json
645+
eventHandlers:
646+
- event: Transfer(address,address,uint256)
647+
handler: handleTransfer
643648
"#;
644649
fs::write(project_dir.join("subgraph.yaml"), manifest_content).unwrap();
645650

@@ -652,6 +657,7 @@ type MyEntity @entity(immutable: true) {
652657
"#;
653658
fs::write(project_dir.join("schema.graphql"), schema_content).unwrap();
654659
fs::write(project_dir.join("mapping.ts"), "").unwrap();
660+
fs::write(project_dir.join("TestContract.json"), "[]").unwrap();
655661

656662
// Run codegen
657663
let opt = CodegenOpt {
@@ -760,6 +766,9 @@ dataSources:
760766
abis:
761767
- name: ExampleContract
762768
file: ./Abi.json
769+
eventHandlers:
770+
- event: ExampleEvent(string)
771+
handler: handleExampleEvent
763772
"#;
764773
fs::write(project_dir.join("subgraph.yaml"), manifest_content).unwrap();
765774

gnd/src/manifest.rs

Lines changed: 61 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use graph::data_source::{
1414
UnresolvedDataSource as GraphUnresolvedDS, UnresolvedDataSourceTemplate as GraphUnresolvedDST,
1515
};
1616
use graph::prelude::DeploymentHash;
17-
use graph_chain_ethereum::Chain;
17+
use graph_chain_ethereum::{BlockHandlerFilter, Chain};
1818
use semver::Version;
1919

2020
use crate::output::{step, Step};
@@ -68,12 +68,12 @@ pub struct DataSource {
6868
pub source_address: Option<String>,
6969
/// The ABI name referenced in `source.abi` (Ethereum data sources only).
7070
pub source_abi: Option<String>,
71-
/// Event handler names from the mapping.
72-
pub event_handlers: Vec<String>,
71+
/// Event handlers from the mapping.
72+
pub event_handlers: Vec<EventHandler>,
7373
/// Call handler names from the mapping.
7474
pub call_handlers: Vec<String>,
75-
/// Block handler names from the mapping.
76-
pub block_handlers: Vec<String>,
75+
/// Block handlers from the mapping (with filter info for validation).
76+
pub block_handlers: Vec<BlockHandler>,
7777
}
7878

7979
impl DataSource {
@@ -94,12 +94,12 @@ pub struct Template {
9494
pub abis: Vec<Abi>,
9595
/// The ABI name referenced in `source.abi` (Ethereum templates only).
9696
pub source_abi: Option<String>,
97-
/// Event handler names from the mapping.
98-
pub event_handlers: Vec<String>,
97+
/// Event handlers from the mapping.
98+
pub event_handlers: Vec<EventHandler>,
9999
/// Call handler names from the mapping.
100100
pub call_handlers: Vec<String>,
101-
/// Block handler names from the mapping.
102-
pub block_handlers: Vec<String>,
101+
/// Block handlers from the mapping (with filter info for validation).
102+
pub block_handlers: Vec<BlockHandler>,
103103
}
104104

105105
impl Manifest {
@@ -116,6 +116,31 @@ pub struct Abi {
116116
pub file: String,
117117
}
118118

119+
/// An event handler with metadata needed for validation.
120+
#[derive(Debug)]
121+
pub struct EventHandler {
122+
pub handler: String,
123+
/// Whether this handler requires transaction receipts.
124+
pub receipt: bool,
125+
/// Whether this handler has eth call declarations.
126+
pub has_call_decls: bool,
127+
}
128+
129+
/// A block handler with filter info needed for validation.
130+
#[derive(Debug)]
131+
pub struct BlockHandler {
132+
pub handler: String,
133+
pub filter: Option<BlockHandlerFilterKind>,
134+
}
135+
136+
/// The kind of filter on a block handler (simplified from graph-node's `BlockHandlerFilter`).
137+
#[derive(Debug, Clone, PartialEq, Eq)]
138+
pub enum BlockHandlerFilterKind {
139+
Call,
140+
Once,
141+
Polling,
142+
}
143+
119144
/// Load a subgraph manifest from a YAML file.
120145
///
121146
/// This uses graph-node's `UnresolvedSubgraphManifest::parse()` which
@@ -197,7 +222,11 @@ fn convert_data_source(ds: GraphUnresolvedDS<Chain>) -> DataSource {
197222
.mapping
198223
.event_handlers
199224
.iter()
200-
.map(|h| h.handler.clone())
225+
.map(|h| EventHandler {
226+
handler: h.handler.clone(),
227+
receipt: h.receipt,
228+
has_call_decls: !h.calls.raw_decls.is_empty(),
229+
})
201230
.collect(),
202231
call_handlers: eth
203232
.mapping
@@ -209,7 +238,10 @@ fn convert_data_source(ds: GraphUnresolvedDS<Chain>) -> DataSource {
209238
.mapping
210239
.block_handlers
211240
.iter()
212-
.map(|h| h.handler.clone())
241+
.map(|h| BlockHandler {
242+
handler: h.handler.clone(),
243+
filter: h.filter.as_ref().map(convert_block_handler_filter),
244+
})
213245
.collect(),
214246
},
215247
GraphUnresolvedDS::Subgraph(sub) => DataSource {
@@ -287,7 +319,11 @@ fn convert_template(t: GraphUnresolvedDST<Chain>) -> Template {
287319
.mapping
288320
.event_handlers
289321
.iter()
290-
.map(|h| h.handler.clone())
322+
.map(|h| EventHandler {
323+
handler: h.handler.clone(),
324+
receipt: h.receipt,
325+
has_call_decls: !h.calls.raw_decls.is_empty(),
326+
})
291327
.collect(),
292328
call_handlers: eth
293329
.mapping
@@ -299,7 +335,10 @@ fn convert_template(t: GraphUnresolvedDST<Chain>) -> Template {
299335
.mapping
300336
.block_handlers
301337
.iter()
302-
.map(|h| h.handler.clone())
338+
.map(|h| BlockHandler {
339+
handler: h.handler.clone(),
340+
filter: h.filter.as_ref().map(convert_block_handler_filter),
341+
})
303342
.collect(),
304343
},
305344
GraphUnresolvedDST::Offchain(off) => Template {
@@ -339,6 +378,15 @@ fn convert_template(t: GraphUnresolvedDST<Chain>) -> Template {
339378
}
340379
}
341380

381+
/// Convert a graph-node `BlockHandlerFilter` to gnd's simplified `BlockHandlerFilterKind`.
382+
fn convert_block_handler_filter(filter: &BlockHandlerFilter) -> BlockHandlerFilterKind {
383+
match filter {
384+
BlockHandlerFilter::Call => BlockHandlerFilterKind::Call,
385+
BlockHandlerFilter::Once => BlockHandlerFilterKind::Once,
386+
BlockHandlerFilter::Polling { .. } => BlockHandlerFilterKind::Polling,
387+
}
388+
}
389+
342390
/// Get the directory containing a manifest file.
343391
///
344392
/// This handles the edge case where `manifest_path` is a bare filename

0 commit comments

Comments
 (0)