Skip to content

Commit 5f6071b

Browse files
committed
Subgraph Composition: Entity Ops Detection in Handlers
1 parent fe363f3 commit 5f6071b

File tree

14 files changed

+229
-51
lines changed

14 files changed

+229
-51
lines changed

graph/src/blockchain/block_stream.rs

Lines changed: 13 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -357,14 +357,13 @@ impl<C: Blockchain> TriggersAdapterWrapper<C> {
357357

358358
fn create_subgraph_trigger_from_entities(
359359
filter: &SubgraphFilter,
360-
entities: &Vec<EntityWithType>,
360+
entities: Vec<EntityWithType>,
361361
) -> Vec<subgraph::TriggerData> {
362362
entities
363-
.iter()
364-
.map(|e| subgraph::TriggerData {
363+
.into_iter()
364+
.map(|entity| subgraph::TriggerData {
365365
source: filter.subgraph.clone(),
366-
entity: e.entity.clone(),
367-
entity_type: e.entity_type.as_str().to_string(),
366+
entity,
368367
})
369368
.collect()
370369
}
@@ -373,25 +372,20 @@ async fn create_subgraph_triggers<C: Blockchain>(
373372
logger: Logger,
374373
blocks: Vec<C::Block>,
375374
filter: &SubgraphFilter,
376-
entities: BTreeMap<BlockNumber, Vec<EntityWithType>>,
375+
mut entities: BTreeMap<BlockNumber, Vec<EntityWithType>>,
377376
) -> Result<Vec<BlockWithTriggers<C>>, Error> {
378377
let logger_clone = logger.cheap_clone();
379378

380379
let blocks: Vec<BlockWithTriggers<C>> = blocks
381380
.into_iter()
382381
.map(|block| {
383382
let block_number = block.number();
384-
match entities.get(&block_number) {
385-
Some(e) => {
386-
let trigger_data = create_subgraph_trigger_from_entities(filter, e);
387-
BlockWithTriggers::new_with_subgraph_triggers(
388-
block,
389-
trigger_data,
390-
&logger_clone,
391-
)
392-
}
393-
None => BlockWithTriggers::new_with_subgraph_triggers(block, vec![], &logger_clone),
394-
}
383+
let trigger_data = entities
384+
.remove(&block_number)
385+
.map(|e| create_subgraph_trigger_from_entities(filter, e))
386+
.unwrap_or_else(Vec::new);
387+
388+
BlockWithTriggers::new_with_subgraph_triggers(block, trigger_data, &logger_clone)
395389
})
396390
.collect();
397391

@@ -433,14 +427,14 @@ async fn scan_subgraph_triggers<C: Blockchain>(
433427
}
434428
}
435429

436-
#[derive(Debug)]
430+
#[derive(Debug, Clone, Eq, PartialEq)]
437431
pub enum EntitySubgraphOperation {
438432
Create,
439433
Modify,
440434
Delete,
441435
}
442436

443-
#[derive(Debug)]
437+
#[derive(Debug, Clone, Eq, PartialEq)]
444438
pub struct EntityWithType {
445439
pub entity_op: EntitySubgraphOperation,
446440
pub entity_type: EntityType,

graph/src/data_source/subgraph.rs

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11
use crate::{
2-
blockchain::{Block, Blockchain},
3-
components::{
4-
link_resolver::LinkResolver,
5-
store::{BlockNumber, Entity},
6-
},
2+
blockchain::{block_stream::EntityWithType, Block, Blockchain},
3+
components::{link_resolver::LinkResolver, store::BlockNumber},
74
data::{subgraph::SPEC_VERSION_1_3_0, value::Word},
85
data_source,
96
prelude::{DataSourceContext, DeploymentHash, Link},
@@ -76,7 +73,7 @@ impl DataSource {
7673
}
7774

7875
let trigger_ref = self.mapping.handlers.iter().find_map(|handler| {
79-
if handler.entity != trigger.entity_type {
76+
if handler.entity != trigger.entity_type() {
8077
return None;
8178
}
8279

@@ -281,17 +278,16 @@ impl UnresolvedDataSourceTemplate {
281278
#[derive(Clone, PartialEq, Eq)]
282279
pub struct TriggerData {
283280
pub source: DeploymentHash,
284-
pub entity: Entity,
285-
pub entity_type: String,
281+
pub entity: EntityWithType,
286282
}
287283

288284
impl TriggerData {
289-
pub fn new(source: DeploymentHash, entity: Entity, entity_type: String) -> Self {
290-
Self {
291-
source,
292-
entity,
293-
entity_type,
294-
}
285+
pub fn new(source: DeploymentHash, entity: EntityWithType) -> Self {
286+
Self { source, entity }
287+
}
288+
289+
pub fn entity_type(&self) -> &str {
290+
self.entity.entity_type.as_str()
295291
}
296292
}
297293

graph/src/runtime/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -368,6 +368,9 @@ pub enum IndexForAscTypeId {
368368
// ...
369369
// LastStarknetType = 4499,
370370

371+
// Subgraph Data Source types
372+
AscEntityTrigger = 4500,
373+
371374
// Reserved discriminant space for a future blockchain type IDs: [4,500, 5,499]
372375
//
373376
// Generated with the following shell script:

runtime/wasm/src/module/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ impl ToAscPtr for subgraph::TriggerData {
7676
heap: &mut H,
7777
gas: &GasCounter,
7878
) -> Result<AscPtr<()>, HostExportError> {
79-
asc_new(heap, &self.entity.sorted_ref(), gas).map(|ptr| ptr.erase())
79+
asc_new(heap, &self.entity, gas).map(|ptr| ptr.erase())
8080
}
8181
}
8282

runtime/wasm/src/to_from/external.rs

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,18 @@
11
use ethabi;
22

3+
use graph::blockchain::block_stream::{EntitySubgraphOperation, EntityWithType};
34
use graph::data::store::scalar::Timestamp;
45
use graph::data::value::Word;
56
use graph::prelude::{BigDecimal, BigInt};
67
use graph::runtime::gas::GasCounter;
78
use graph::runtime::{
8-
asc_get, asc_new, AscIndexId, AscPtr, AscType, AscValue, HostExportError, ToAscObj,
9+
asc_get, asc_new, AscIndexId, AscPtr, AscType, AscValue, HostExportError, IndexForAscTypeId,
10+
ToAscObj,
911
};
1012
use graph::{data::store, runtime::DeterministicHostError};
1113
use graph::{prelude::serde_json, runtime::FromAscObj};
1214
use graph::{prelude::web3::types as web3, runtime::AscHeap};
15+
use graph_runtime_derive::AscType;
1316

1417
use crate::asc_abi::class::*;
1518

@@ -463,3 +466,43 @@ where
463466
})
464467
}
465468
}
469+
470+
#[derive(Debug, Clone, Eq, PartialEq, AscType)]
471+
pub enum AscSubgraphEntityOp {
472+
Create,
473+
Modify,
474+
Delete,
475+
}
476+
477+
#[derive(AscType)]
478+
pub struct AscEntityTrigger {
479+
pub entity_op: AscSubgraphEntityOp,
480+
pub entity_type: AscPtr<AscString>,
481+
pub entity: AscPtr<AscEntity>,
482+
pub vid: i64,
483+
}
484+
485+
impl ToAscObj<AscEntityTrigger> for EntityWithType {
486+
fn to_asc_obj<H: AscHeap + ?Sized>(
487+
&self,
488+
heap: &mut H,
489+
gas: &GasCounter,
490+
) -> Result<AscEntityTrigger, HostExportError> {
491+
let entity_op = match self.entity_op {
492+
EntitySubgraphOperation::Create => AscSubgraphEntityOp::Create,
493+
EntitySubgraphOperation::Modify => AscSubgraphEntityOp::Modify,
494+
EntitySubgraphOperation::Delete => AscSubgraphEntityOp::Delete,
495+
};
496+
497+
Ok(AscEntityTrigger {
498+
entity_op,
499+
entity_type: asc_new(heap, &self.entity_type.as_str(), gas)?,
500+
entity: asc_new(heap, &self.entity.sorted_ref(), gas)?,
501+
vid: self.vid,
502+
})
503+
}
504+
}
505+
506+
impl AscIndexId for AscEntityTrigger {
507+
const INDEX_ASC_TYPE_ID: IndexForAscTypeId = IndexForAscTypeId::AscEntityTrigger;
508+
}
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
1-
21
type Block @entity {
32
id: ID!
43
number: BigInt!
54
hash: Bytes!
5+
testMessage: String
66
}
77

88
type Block2 @entity {
99
id: ID!
1010
number: BigInt!
1111
hash: Bytes!
12+
testMessage: String
1213
}

tests/integration-tests/source-subgraph/src/mapping.ts

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { ethereum, log } from '@graphprotocol/graph-ts';
1+
import { ethereum, log, store } from '@graphprotocol/graph-ts';
22
import { Block, Block2 } from '../generated/schema';
33
import { BigInt } from '@graphprotocol/graph-ts';
44

@@ -22,4 +22,36 @@ export function handleBlock(block: ethereum.Block): void {
2222
blockEntity3.number = block.number;
2323
blockEntity3.hash = block.hash;
2424
blockEntity3.save();
25+
26+
if (block.number.equals(BigInt.fromI32(1))) {
27+
let id = 'TEST';
28+
let entity = new Block(id);
29+
entity.number = block.number;
30+
entity.hash = block.hash;
31+
entity.testMessage = 'Created at block 1';
32+
log.info('Created entity at block 1', []);
33+
entity.save();
34+
}
35+
36+
if (block.number.equals(BigInt.fromI32(2))) {
37+
let id = 'TEST';
38+
let blockEntity1 = Block.load(id);
39+
if (blockEntity1) {
40+
// Update the block entity
41+
blockEntity1.testMessage = 'Updated at block 2';
42+
log.info('Updated entity at block 2', []);
43+
blockEntity1.save();
44+
}
45+
}
46+
47+
if (block.number.equals(BigInt.fromI32(3))) {
48+
let id = 'TEST';
49+
let blockEntity1 = Block.load(id);
50+
if (blockEntity1) {
51+
blockEntity1.testMessage = 'Deleted at block 3';
52+
log.info('Deleted entity at block 3', []);
53+
blockEntity1.save();
54+
store.remove('Block', id);
55+
}
56+
}
2557
}

tests/integration-tests/subgraph-data-sources/schema.graphql

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,5 @@ type MirrorBlock @entity {
22
id: String!
33
number: BigInt!
44
hash: Bytes!
5+
testMessage: String
56
}
Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,46 @@
1-
import { Entity, log } from '@graphprotocol/graph-ts';
1+
import { Entity, log, store } from '@graphprotocol/graph-ts';
22
import { MirrorBlock } from '../generated/schema';
33

4-
export function handleEntity(blockEntity: Entity): void {
4+
export class EntityTrigger {
5+
constructor(
6+
public entityOp: u32,
7+
public entityType: string,
8+
public entity: Entity,
9+
public vid: i64,
10+
) {}
11+
}
12+
13+
export function handleEntity(trigger: EntityTrigger): void {
14+
let blockEntity = trigger.entity;
515
let blockNumber = blockEntity.getBigInt('number');
616
let blockHash = blockEntity.getBytes('hash');
17+
let testMessage = blockEntity.get('testMessage');
718
let id = blockEntity.getString('id');
819

920
log.info('Block number: {}', [blockNumber.toString()]);
1021

11-
let block = new MirrorBlock(id);
22+
if (trigger.entityOp == 2) {
23+
log.info('Removing block entity with id: {}', [id]);
24+
store.remove('MirrorBlock', id);
25+
return;
26+
}
27+
28+
let block = loadOrCreateMirrorBlock(id);
1229
block.number = blockNumber;
1330
block.hash = blockHash;
31+
if (testMessage) {
32+
block.testMessage = testMessage.toString();
33+
}
34+
1435
block.save();
1536
}
37+
38+
export function loadOrCreateMirrorBlock(id: string): MirrorBlock {
39+
let block = MirrorBlock.load(id);
40+
if (!block) {
41+
log.info('Creating new block entity with id: {}', [id]);
42+
block = new MirrorBlock(id);
43+
}
44+
45+
return block;
46+
}

tests/integration-tests/subgraph-data-sources/subgraph.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ dataSources:
66
name: Contract
77
network: test
88
source:
9-
address: 'QmeZhEiJuBusu7GxCe6AytvqSsgwV8QxkbSYx5ojSFB28a'
9+
address: 'Qmaqf8cRxfxbduZppSHKG9DMuX5JZPMoGuwGb2DQuo48sq'
1010
startBlock: 0
1111
mapping:
1212
apiVersion: 0.0.7

0 commit comments

Comments
 (0)