Skip to content
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
30 changes: 14 additions & 16 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 12 additions & 1 deletion postman/.env.sample
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,15 @@ POSTGRES_DB=postman_db
DB_CLEANER_ENABLED=false
DB_CLEANING_INTERVAL=10000
DB_DAYS_BEFORE_NOW_TO_DELETE=1
ENABLE_LINEA_ESTIMATE_GAS=false
ENABLE_LINEA_ESTIMATE_GAS=false

# Optional event filter params
L1_EVENT_FILTER_FROM_ADDRESS=<FROM_ADDRESS>
L1_EVENT_FILTER_TO_ADDRESS=<TO_ADDRESS>
L1_EVENT_FILTER_CALLDATA=<criteria>
L1_EVENT_FILTER_CALLDATA_FUNCTION_INTERFACE=<FUNCTION_INTERFACE>

L2_EVENT_FILTER_FROM_ADDRESS=<FROM_ADDRESS>
L2_EVENT_FILTER_TO_ADDRESS=<TO_ADDRESS>
L2_EVENT_FILTER_CALLDATA=<criteria>
L2_EVENT_FILTER_CALLDATA_FUNCTION_INTERFACE=<FUNCTION_INTERFACE>
22 changes: 21 additions & 1 deletion postman/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,16 @@ All messages are stored in a configurable Postgres DB.
- `L1_LISTENER_BLOCK_CONFIRMATION`: Required block confirmations
- `L1_MAX_BLOCKS_TO_FETCH_LOGS`: Maximum blocks to fetch in one request
- `L1_MAX_GAS_FEE_ENFORCED`: Enable/disable gas fee enforcement
- `L1_EVENT_FILTER_FROM_ADDRESS`: Filter events using a from address
- `L1_EVENT_FILTER_TO_ADDRESS`: Filter events using a to address
- `L1_EVENT_FILTER_CALLDATA`: MessageSent event calldata filtering criteria expression. See [Filtrex repo](https://github.com/joewalnes/filtrex/tree/master).
<br>
You can filter by the calldata field:
<br>

Example:
`calldata.funcSignature == "0x6463fb2a" and calldata.params.messageNumber == 85804`,
- `L1_EVENT_FILTER_CALLDATA_FUNCTION_INTERFACE`: Calldata data function interface following this format: `"function transfer(address to, uint256 amount)"`. Make sure you specify parameters names in order to use syntax like `calldata.params.messageNumber`.

#### L2 Configuration
- `L2_RPC_URL`: Linea node RPC endpoint
Expand All @@ -39,6 +49,16 @@ All messages are stored in a configurable Postgres DB.
- `L2_MAX_BLOCKS_TO_FETCH_LOGS`: Maximum blocks to fetch in one request
- `L2_MAX_GAS_FEE_ENFORCED`: Enable/disable gas fee enforcement
- `L2_MESSAGE_TREE_DEPTH`: Depth of the message Merkle tree
- `L2_EVENT_FILTER_FROM_ADDRESS`: Filter events using a from address
- `L2_EVENT_FILTER_TO_ADDRESS`: Filter events using a to address
- `L2_EVENT_FILTER_CALLDATA`: MessageSent event calldata filtering criteria expression. See [Filtrex repo](https://github.com/joewalnes/filtrex/tree/master).
<br>
You can filter by the calldata field:
<br>

Example:
`calldata.funcSignature == "0x6463fb2a" and calldata.params.messageNumber == 85804`,
- `L2_EVENT_FILTER_CALLDATA_FUNCTION_INTERFACE`: Calldata data function interface following this format: `"function transfer(address to, uint256 amount)"`. Make sure you specify parameters names in order to use syntax like `calldata.params.messageNumber`.

#### Message Processing
- `MESSAGE_SUBMISSION_TIMEOUT`: Timeout for message submission (ms)
Expand Down Expand Up @@ -80,7 +100,7 @@ All messages are stored in a configurable Postgres DB.

From the root folder, run the following command:
```bash
make fresh-start-all
make start-env-with-tracing-v2
```

Stop the postman docker container manually.
Expand Down
1 change: 1 addition & 0 deletions postman/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
"class-validator": "0.14.1",
"dotenv": "16.4.5",
"ethers": "6.13.4",
"filtrex": "3.1.0",
"pg": "8.13.1",
"typeorm": "0.3.20",
"typeorm-naming-strategies": "4.1.0",
Expand Down
36 changes: 36 additions & 0 deletions postman/scripts/runPostman.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,24 @@ async function main() {
...(parseInt(process.env.L1_LISTENER_BLOCK_CONFIRMATION ?? "") >= 0
? { blockConfirmation: parseInt(process.env.L1_LISTENER_BLOCK_CONFIRMATION ?? "") }
: {}),
...(process.env.L1_EVENT_FILTER_FROM_ADDRESS ||
process.env.L1_EVENT_FILTER_TO_ADDRESS ||
(process.env.L1_EVENT_FILTER_CALLDATA && process.env.L1_EVENT_FILTER_CALLDATA_FUNCTION_INTERFACE)
? {
eventFilters: {
fromAddressFilter: process.env.L1_EVENT_FILTER_FROM_ADDRESS,
toAddressFilter: process.env.L1_EVENT_FILTER_TO_ADDRESS,
...(process.env.L1_EVENT_FILTER_CALLDATA && process.env.L1_EVENT_FILTER_CALLDATA_FUNCTION_INTERFACE
? {
calldataFilter: {
criteriaExpression: process.env.L1_EVENT_FILTER_CALLDATA,
calldataFunctionInterface: process.env.L1_EVENT_FILTER_CALLDATA_FUNCTION_INTERFACE,
},
}
: {}),
},
}
: {}),
},
claiming: {
signerPrivateKey: process.env.L1_SIGNER_PRIVATE_KEY ?? "",
Expand Down Expand Up @@ -65,6 +83,24 @@ async function main() {
...(parseInt(process.env.L2_LISTENER_BLOCK_CONFIRMATION ?? "") >= 0
? { blockConfirmation: parseInt(process.env.L2_LISTENER_BLOCK_CONFIRMATION ?? "") }
: {}),
...(process.env.L2_EVENT_FILTER_FROM_ADDRESS ||
process.env.L2_EVENT_FILTER_TO_ADDRESS ||
(process.env.L2_EVENT_FILTER_CALLDATA && process.env.L2_EVENT_FILTER_CALLDATA_FUNCTION_INTERFACE)
? {
eventFilters: {
fromAddressFilter: process.env.L2_EVENT_FILTER_FROM_ADDRESS,
toAddressFilter: process.env.L2_EVENT_FILTER_TO_ADDRESS,
...(process.env.L2_EVENT_FILTER_CALLDATA && process.env.L2_EVENT_FILTER_CALLDATA_FUNCTION_INTERFACE
? {
calldataFilter: {
criteriaExpression: process.env.L2_EVENT_FILTER_CALLDATA,
calldataFunctionInterface: process.env.L2_EVENT_FILTER_CALLDATA_FUNCTION_INTERFACE,
},
}
: {}),
},
}
: {}),
},
claiming: {
signerPrivateKey: process.env.L2_SIGNER_PRIVATE_KEY ?? "",
Expand Down
2 changes: 2 additions & 0 deletions postman/src/application/postman/app/PostmanServiceClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ export class PostmanServiceClient {
blockConfirmation: config.l1Config.listener.blockConfirmation,
isEOAEnabled: config.l1Config.isEOAEnabled,
isCalldataEnabled: config.l1Config.isCalldataEnabled,
eventFilters: config.l1Config.listener.eventFilters,
},
new WinstonLogger(`L1${MessageSentEventProcessor.name}`, config.loggerOptions),
);
Expand Down Expand Up @@ -245,6 +246,7 @@ export class PostmanServiceClient {
blockConfirmation: config.l2Config.listener.blockConfirmation,
isEOAEnabled: config.l2Config.isEOAEnabled,
isCalldataEnabled: config.l2Config.isCalldataEnabled,
eventFilters: config.l2Config.listener.eventFilters,
},
new WinstonLogger(`L2${MessageSentEventProcessor.name}`, config.loggerOptions),
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,36 @@ describe("PostmanServiceClient", () => {
new Error("Something went wrong when trying to generate Wallet. Please check your private key."),
);
});

it("should throw an error when events filters are not valid", () => {
const postmanServiceClientOptionsWithInvalidPrivateKey: PostmanOptions = {
...postmanServiceClientOptions,
l1Options: {
...postmanServiceClientOptions.l1Options,
listener: {
...postmanServiceClientOptions.l1Options.listener,
eventFilters: {
fromAddressFilter: "0x",
},
},
claiming: {
...postmanServiceClientOptions.l1Options.claiming,
signerPrivateKey: "0x",
},
},
l2Options: {
...postmanServiceClientOptions.l2Options,
claiming: {
...postmanServiceClientOptions.l2Options.claiming,
signerPrivateKey: "0x",
},
},
};

expect(() => new PostmanServiceClient(postmanServiceClientOptionsWithInvalidPrivateKey)).toThrow(
new Error("Invalid fromAddressFilter: 0x"),
);
});
});

describe("connectDatabase", () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { describe } from "@jest/globals";
import { getConfig } from "../utils";
import { getConfig, validateEventsFiltersConfig } from "../utils";
import {
TEST_ADDRESS_1,
TEST_ADDRESS_2,
Expand Down Expand Up @@ -235,4 +235,61 @@ describe("Config utils", () => {
});
});
});

describe("validateEventsFiltersConfig", () => {
it("should throw an error when the from address event filter is not valid", () => {
expect(() =>
validateEventsFiltersConfig({
fromAddressFilter: "0x123",
}),
).toThrow("Invalid fromAddressFilter: 0x123");
});

it("should throw an error when the to address event filter is not valid", () => {
expect(() =>
validateEventsFiltersConfig({
toAddressFilter: "0x123",
}),
).toThrow("Invalid toAddressFilter: 0x123");
});

it("should not throw an error when filters are valid", () => {
expect(() =>
validateEventsFiltersConfig({
fromAddressFilter: "0xc59d8de7f984AbC4913f0177bfb7BBdaFaC41fA6",
toAddressFilter: "0xc59d8de7f984AbC4913f0177bfb7BBdaFaC41fA6",
calldataFilter: {
criteriaExpression: `calldata.funcSignature == "0x26dfbc20" and calldata.amount > 0`,
calldataFunctionInterface: "function receiveFromOtherLayer(address recipient, uint256 amount)",
},
}),
).not.toThrow();
});

it("should throw an error when calldataFilter filter expression is invalid", () => {
expect(() =>
validateEventsFiltersConfig({
fromAddressFilter: "0xc59d8de7f984AbC4913f0177bfb7BBdaFaC41fA6",
toAddressFilter: "0xc59d8de7f984AbC4913f0177bfb7BBdaFaC41fA6",
calldataFilter: {
criteriaExpression: `calldata.funcSignature == "0x26dfbc20" and calldata.amount = 0`,
calldataFunctionInterface: "function receiveFromOtherLayer(address recipient, uint256 amount)",
},
}),
).toThrow('Invalid calldataFilter expression: calldata.funcSignature == "0x26dfbc20" and calldata.amount = 0');
});

it("should throw an error when calldataFunctionInterface is invalid", () => {
expect(() =>
validateEventsFiltersConfig({
fromAddressFilter: "0xc59d8de7f984AbC4913f0177bfb7BBdaFaC41fA6",
toAddressFilter: "0xc59d8de7f984AbC4913f0177bfb7BBdaFaC41fA6",
calldataFilter: {
criteriaExpression: `calldata.funcSignature == "0x26dfbc20" and calldata.amount > 0`,
calldataFunctionInterface: "function receiveFromOtherLayer(address recipient uint256 amount)",
},
}),
).toThrow("Invalid calldataFunctionInterface: function receiveFromOtherLayer(address recipient uint256 amount)");
});
});
});
11 changes: 10 additions & 1 deletion postman/src/application/postman/app/config/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,15 @@ export type ListenerOptions = {
blockConfirmation?: number;
maxFetchMessagesFromDb?: number;
maxBlocksToFetchLogs?: number;
eventFilters?: {
fromAddressFilter?: string;
toAddressFilter?: string;
calldataFilter?: {
criteriaExpression: string;
calldataFunctionInterface: string;
};
};
};

export type ListenerConfig = Required<ListenerOptions>;
export type ListenerConfig = Required<Omit<ListenerOptions, "eventFilters">> &
Partial<Pick<ListenerOptions, "eventFilters">>;
Comment thread
VGau marked this conversation as resolved.
Loading