Skip to content

Commit c51f147

Browse files
committed
[GRPC] Simple Transaction Filtering
1 parent c2b3482 commit c51f147

18 files changed

+1359
-16
lines changed

Cargo.lock

Lines changed: 67 additions & 15 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ members = [
123123
"ecosystem/indexer-grpc/indexer-grpc-server-framework",
124124
"ecosystem/indexer-grpc/indexer-grpc-table-info",
125125
"ecosystem/indexer-grpc/indexer-grpc-utils",
126+
"ecosystem/indexer-grpc/transaction-filter",
126127
"ecosystem/nft-metadata-crawler-parser",
127128
"ecosystem/node-checker",
128129
"ecosystem/node-checker/fn-check-client",
@@ -428,6 +429,7 @@ aptos-storage-service-notifications = { path = "state-sync/inter-component/stora
428429
aptos-storage-service-types = { path = "state-sync/storage-service/types" }
429430
aptos-storage-service-server = { path = "state-sync/storage-service/server" }
430431
aptos-system-utils = { path = "crates/aptos-system-utils" }
432+
aptos-transaction-filter = { path = "ecosystem/indexer-grpc/transaction-filter" }
431433
aptos-telemetry = { path = "crates/aptos-telemetry" }
432434
aptos-telemetry-service = { path = "crates/aptos-telemetry-service" }
433435
aptos-temppath = { path = "crates/aptos-temppath" }
@@ -534,6 +536,7 @@ datatest-stable = "0.1.1"
534536
debug-ignore = { version = "1.0.3", features = ["serde"] }
535537
derivative = "2.2.0"
536538
derivation-path = "0.2.0"
539+
derive_builder = "0.20.0"
537540
determinator = "0.12.0"
538541
derive_more = "0.99.11"
539542
diesel = "2.1"
@@ -619,7 +622,7 @@ libfuzzer-sys = "0.4.6"
619622
libsecp256k1 = "0.7.0"
620623
log = "0.4.17"
621624
lru = "0.7.5"
622-
lz4 = "1.24.0"
625+
lz4 = "1.25.0"
623626
maplit = "1.0.2"
624627
merlin = "3"
625628
memory-stats = "1.0.0"
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
fixtures/compressed_files_lz4_00008bc1d5adcf862d3967c1410001fb_705101000.pb.lz4 filter=lfs diff=lfs merge=lfs -text
2+
fixtures/compressed_files_lz4_0013c194ec4fdbfb8db7306170aac083_445907000.pb.lz4 filter=lfs diff=lfs merge=lfs -text
3+
fixtures/compressed_files_lz4_f3d880d9700c70d71fefe71aa9218aa9_301616000.pb.lz4 filter=lfs diff=lfs merge=lfs -text
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
[package]
2+
name = "aptos-transaction-filter"
3+
version = "0.1.0"
4+
5+
# Workspace inherited keys
6+
authors = { workspace = true }
7+
edition = { workspace = true }
8+
homepage = { workspace = true }
9+
license = { workspace = true }
10+
publish = { workspace = true }
11+
repository = { workspace = true }
12+
rust-version = { workspace = true }
13+
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
14+
15+
[dependencies]
16+
anyhow = { workspace = true }
17+
aptos-protos = { workspace = true }
18+
19+
derive_builder = { workspace = true }
20+
21+
prost = { workspace = true }
22+
23+
serde = { workspace = true }
24+
serde_json = { workspace = true }
25+
serde_yaml = { workspace = true }
26+
27+
thiserror = { workspace = true }
28+
29+
[dev-dependencies]
30+
# we only decompress the fixture protos in test
31+
lz4 = { workspace = true }
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
# Transaction Filter
2+
3+
## Overview
4+
5+
The goal of **transaction filtering** is to be able to save resources downstream of wherever filtering is used.
6+
For this to be true, the filtering itself must be **fast and use minimal resources**, and so we do a few things:
7+
8+
1. We avoid clones, copies, etc as much as possible
9+
2. We do a single pass over the transaction data
10+
11+
## Transaction Filtering
12+
13+
There are a few different parts of a transaction that are queryable:
14+
15+
1. The "root" level. This includes:
16+
- Transaction type
17+
- Success
18+
2. User Transactions. Each user transaction has:
19+
- Sender
20+
- Payload: we only support the entry function payload
21+
- Entry function (address, module, name)
22+
- Entry function ID string
23+
3. Events. Each event has:
24+
- Key
25+
- Type
26+
27+
### Usage & Examples
28+
29+
There are two different patterns for building a filter- you can either use the `TransactionFilterBuilder` or
30+
the `TransactionFilter` struct directly.
31+
32+
The `TransactionFilterBuilder` is a more ergonomic way to build a filter, and is not significantly worse construction
33+
performance, assuming this is being done infrequently.
34+
35+
```
36+
use transaction_filter::filters::EventFilterBuilder;
37+
38+
let ef = EventFilterBuilder::default()
39+
.data("spins")
40+
.struct_type(
41+
MoveStructTagFilterBuilder::default()
42+
.address("0x0077")
43+
.module("roulette")
44+
.name("spin")
45+
.build()?,
46+
)
47+
.build()?;
48+
```
49+
50+
The `TransactionFilter` struct is also available, but requires direct construction of the structs.
51+
52+
```
53+
use transaction_filter::filters::EventFilter;
54+
55+
let ef = EventFilter {
56+
data: Some("spins".into()),
57+
struct_type: Some(MoveStructTagFilter {
58+
address: Some("0x0077".into()),
59+
module: Some("roulette".into()),
60+
name: Some("spin".into()),
61+
}),
62+
};
63+
```
64+
65+
Once you have some filters built, you can combine them with the boolean operators `and`, `or`, and `not`.
66+
67+
```
68+
let trf = TransactionRootFilterBuilder::default()
69+
.success(true).build()?;
70+
71+
let utf = UserTransactionFilterBuilder::default()
72+
.sender("0x0011".into()).build()?;
73+
74+
let ef = EventFilterBuilder::default()
75+
.struct_type(
76+
MoveStructTagFilterBuilder::default()
77+
.address("0x0077")
78+
.module("roulette")
79+
.name("spin")
80+
.build()?,
81+
)
82+
.build()?;
83+
84+
// Combine filters using logical operators!
85+
// (trf OR utf)
86+
let trf_or_utf = BooleanTransactionFilter::from(trf).or(utf);
87+
// ((trf OR utf) AND ef)
88+
let query = trf_or_utf.and(ef);
89+
90+
let transactions: Vec<Transaction> = transaction_stream.next().await;
91+
let filtered_transactions = query.filter_vec(transactions);
92+
```
93+
94+
## API & Serialization
95+
96+
`BooleanTransactionFilter` is the top level filter struct, and it uses `serde` for serialization and deserialization.
97+
98+
This means we can use it across all of our projects, whether they be GRPC services, REST services, or CLI tools.
99+
100+
The above example can be serialized to JSON like so:
101+
102+
```json
103+
{
104+
"and": [
105+
{
106+
"or": [
107+
{
108+
"type": "TransactionRootFilter",
109+
"success": true
110+
},
111+
{
112+
"type": "UserTransactionFilter",
113+
"sender": "0x0011"
114+
}
115+
]
116+
},
117+
{
118+
"type": "EventFilter",
119+
"struct_type": {
120+
"address": "0x0077",
121+
"module": "roulette",
122+
"name": "spin"
123+
}
124+
}
125+
]
126+
}
127+
```
128+
129+
Or, if you prefer, as yaml:
130+
131+
```yaml
132+
---
133+
and:
134+
- or:
135+
- type: TransactionRootFilter
136+
success: true
137+
- type: UserTransactionFilter
138+
sender: '0x0011'
139+
- type: EventFilter
140+
struct_type:
141+
address: '0x0077'
142+
module: roulette
143+
name: spin
144+
```
145+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
version https://git-lfs.github.com/spec/v1
2+
oid sha256:dedf23a08bc7bd9fc9c96dc5d45741fe52c225ccfc2f7c72030240585a531dc3
3+
size 465907
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
version https://git-lfs.github.com/spec/v1
2+
oid sha256:596c2e0582123c36e06c3be8ded161afa63b39c742acdbf279da8d97bfa8ccd3
3+
size 3039165
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
version https://git-lfs.github.com/spec/v1
2+
oid sha256:55750a530523ed097005e2c95e832d2f1ef8c50f535a65f3d4d025073f635e9a
3+
size 40801877

0 commit comments

Comments
 (0)