Skip to content

Commit e3d4ee1

Browse files
authored
Merge pull request #8 from Web3-Pi/mgordel/tx-extra-data
Firewall operational modes (interactive/non-interactive)
2 parents 9c3fa07 + b01e4cd commit e3d4ee1

23 files changed

Lines changed: 1289 additions & 199 deletions

.env-template

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,11 @@ WSS_PORT=18501
1010
# RPC endpoint for communication with Ethereum/blockchain node
1111
RPC_ENDPOINT=http://localhost:8545
1212

13+
# The application supports two operation modes:
14+
# - interactive: requires user decisions during transaction execution via the UI
15+
# - non-interactive: relies on predefined validation rules without user intervention
16+
FIREWALL_MODE=interactive
17+
1318
# Path to file containing authorized addresses
1419
AUTHORIZED_ADDR_PATH=auth_addr.json
1520

@@ -22,6 +27,15 @@ CONTRACT_ABIS_PATH=known_contract_abis.json
2227
# Timeout duration for user decision in interactive mode (in seconds)
2328
INTERACTIVE_MODE_TIMEOUT_SEC=60
2429

30+
# Path to file containing rules for allowed/denied address combinations
31+
ADDRESS_RULES_PATH=address_rules.json
32+
33+
# Path to file containing rules for value and gas price limits
34+
VALUE_RULES_PATH=value_rules.json
35+
36+
# Path to file containing rules for contract function calls
37+
CONTRACT_RULES_PATH=contract_rules.json
38+
2539
# Configuration for metrics
2640
METRICS_MODE=stdout
2741
METRICS_BATCH_SIZE=100

README.md

Lines changed: 175 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,42 @@ cp .env-template .env
2929

3030
```
3131

32+
### Firewall Mode
33+
34+
The application supports two operational modes for the transaction firewall, which can be configured according to security requirements and operational needs:
35+
36+
#### Interactive Mode
37+
38+
In interactive mode, the application requires active user participation in the transaction decision-making process. Key features of this mode:
39+
40+
- Each incoming transaction is presented to the user through the interface
41+
- The user can review transaction details (destination address, value, data, etc.)
42+
- The system awaits an explicit user decision (approve or reject) before continuing processing
43+
- Provides the highest level of control, ideal for critical operations or environments requiring human supervision
44+
- Uses UI frontend via websocket protocol for real-time communication with the user interface
45+
46+
#### Non-Interactive Mode
47+
48+
Non-interactive mode automates the transaction validation process based on predefined rules, eliminating the need for manual intervention. Key features:
49+
50+
- Transactions are automatically verified against a predefined set of validation rules
51+
- Rules may include checking address allow/deny, value limits, gas fee restrictions, contract type verification, etc.
52+
- Validation occurs instantly without delays related to waiting for user response
53+
- Ideal for environments requiring high throughput or automation
54+
55+
The choice of mode depends on the specific use case, security level, and operational requirements. Interactive mode offers the highest level of control at the expense of performance, while non-interactive mode provides better scalability and automation at the cost of less flexibility in ad-hoc decision making.
56+
57+
The mode can be configured in the application configuration file using the `FIREWALL_MODE` parameter.
58+
3259
#### Environment
3360

3461
The `.env` file allows you to configure various settings required for the Ethereum Transactions Firewall. Below is a
3562
description of the available variables that you can set:
3663

64+
- `FIREWALL_MODE`: Operational mode of the firewall. Can be set to either `interactive` for manual transaction approval
65+
or `non-interactive` for automated rule-based validation.
66+
**Default:** `interactive`
67+
3768
- `SERVER_PORT`: Port number where the main server will listen for incoming connections.
3869
**Default:** `8454`
3970

@@ -51,11 +82,22 @@ description of the available variables that you can set:
5182

5283
- `KNOWN_CONTRACTS_PATH`: Path to the file containing information about known contracts mapped to their labels and abi.
5384
**Default:** `known_contracts.json`
85+
-
5486
- `INTERACTIVE_MODE_TIMEOUT_SEC`: Timeout duration for user decision in interactive mode (in seconds).
5587
**Default:** `60`
5688

89+
- `ADDRESS_RULES_PATH`: Path to file containing rules for allowed/denied address combinations.
90+
**Default:** `address_rules.json`
91+
92+
- `VALUE_RULES_PATH`: Path to file containing rules for value and gas price limits.
93+
**Default:** `value_rules.json`
94+
95+
- `CONTRACT_RULES_PATH`: Path to file containing rules for contract function calls.
96+
**Default:** `contract_rules.json`
97+
5798
Be sure to restart the application after making changes to the `.env` file for them to take effect.
5899

100+
### Interactive Mode Configuration
59101

60102
#### Authorized addresses
61103

@@ -121,6 +163,138 @@ If a contract address is not matched with any entries in the `known_contracts.js
121163

122164
All these interfaces are imported from the [OpenZeppelin](https://github.com/OpenZeppelin/openzeppelin-contracts) library, which provides secure and community-vetted implementations of common smart contract standards. This automatic detection allows the firewall to properly parse and display transaction data, enhancing security even when interacting with previously undefined contracts.
123165

166+
### Non-Interactive Mode Configuration
167+
168+
In non-interactive mode, the application uses three types of rules to validate transactions automatically without user intervention. Each rule type is configured in a separate JSON file, and the paths to these files are specified in the application configuration.
169+
170+
#### Address Rules
171+
172+
Address rules define policies for interactions between specific Ethereum addresses. Each rule specifies whether transactions between particular source and destination addresses should be allowed or denied.
173+
174+
To set this up, edit the `address_rules.json` file and add the rules. For example:
175+
176+
```json
177+
[
178+
{
179+
"action": "allow",
180+
"from": "0x8626f6940E2eb28930eFb4CeF49B2d1F2C9C1199",
181+
"to": "0xdAC17F958D2ee523a2206206994597C13D831ec7",
182+
"comment": "Allow transfers to USDT contract"
183+
},
184+
{
185+
"action": "allow",
186+
"from": "*",
187+
"to": "0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D",
188+
"comment": "Allow all addresses to interact with Uniswap router"
189+
},
190+
{
191+
"action": "deny",
192+
"from": "0x8626f6940E2eb28930eFb4CeF49B2d1F2C9C1199",
193+
"to": "0x6B175474E89094C44Da98b954EedeAC495271d0F",
194+
"comment": "Deny specific wallet from interacting with DAI contract"
195+
}
196+
]
197+
```
198+
**Key features:**
199+
- `action`: Either "allow" or "deny" to permit or block transactions
200+
- `from`: Source address, use "*" as a wildcard to match any address
201+
- `to`: Destination address, use "*" as a wildcard to match any address
202+
- `comment`: Optional description of the rule purpose (recommended for maintenance)
203+
204+
Transactions are evaluated against each rule sequentially until a match is found. If a transaction matches a "deny" rule, it is rejected. If no matching rule is found, the transaction is rejected by default.
205+
206+
#### Value Rules
207+
208+
Value rules define constraints for transaction values and gas prices. These rules help prevent unwanted high-value transfers or excessive gas fees.
209+
210+
To set this up, edit the `value_rules.json` file and add the rules. For example:
211+
212+
```json
213+
[
214+
{
215+
"minValue": 0,
216+
"maxValue": 1000000000000000000,
217+
"minGasPrice": 1000000000,
218+
"maxGasPrice": 50000000000,
219+
"comment": "Allow transactions up to 1 ETH with reasonable gas prices"
220+
},
221+
{
222+
"minValue": 0,
223+
"maxValue": 10000000000000000000,
224+
"minGasPrice": 1000000000,
225+
"maxGasPrice": 100000000000,
226+
"comment": "Allow transactions up to 10 ETH with higher gas price allowance"
227+
},
228+
{
229+
"minValue": null,
230+
"maxValue": null,
231+
"minGasPrice": 1000000000,
232+
"maxGasPrice": 30000000000,
233+
"comment": "Allow any value transaction with standard gas prices"
234+
}
235+
]
236+
```
237+
**Key features:**
238+
- `minValue`: Minimum transaction value in wei (use null for no lower limit)
239+
- `maxValue`: Maximum transaction value in wei (use null for no upper limit)
240+
- `minGasPrice`: Minimum gas price in wei (use null for no lower limit)
241+
- `maxGasPrice`: Maximum gas price in wei (use null for no upper limit)
242+
- `comment`: Optional description of the rule purpose
243+
244+
For EIP-1559 transactions, the system uses `maxFeePerGas` as the equivalent of `gasPrice` when evaluating these rules.
245+
246+
#### Contract Rules
247+
248+
Contract rules allow or deny interactions with specific smart contract functions. These rules provide fine-grained control over which contract functions can be called.
249+
250+
To set this up, edit the `contract_rules.json` file and add the rules. For example:
251+
```json
252+
[
253+
{
254+
"action": "allow",
255+
"functionName": "transfer",
256+
"args": {},
257+
"comment": "Allow transfer function calls"
258+
},
259+
{
260+
"action": "deny",
261+
"functionName": "approve",
262+
"args": {},
263+
"comment": "Block all approve function calls"
264+
},
265+
{
266+
"action": "allow",
267+
"functionName": "swapExactTokensForTokens",
268+
"args": {},
269+
"comment": "Allow Uniswap swap function"
270+
},
271+
{
272+
"action": "allow",
273+
"functionName": "transfer",
274+
"args": {
275+
"to": "0x1234567890123456789012345678901234567890",
276+
"value": "1000000000000000000"
277+
},
278+
"comment": "Allow transfer of 1 ETH to specific address"
279+
},
280+
{
281+
"action": "deny",
282+
"functionName": "approve",
283+
"args": {
284+
"spender": "*",
285+
"amount": "115792089237316195423570985008687907853269984665640564039457584007913129639935"
286+
},
287+
"comment": "Block unlimited token approvals"
288+
}
289+
]
290+
```
291+
**Key features:**
292+
- `action`: Either "allow" or "deny" to permit or block transactions
293+
- `functionName`: Name of the contract function to match
294+
- `args`: Object mapping parameter names to expected values (empty object matches any arguments)
295+
- `comment`: Optional description of the rule purpose
296+
297+
- Contract rules are only applied to transactions that interact with smart contracts. Transactions that don't involve contract interactions automatically pass contract rule validation. **Important:** Contract rules require that the ABI for the target contract is pre-defined in the system's known contracts configuration file (specified by `KNOWN_CONTRACTS_PATH` in the environment configuration). Without the appropriate ABI, the contract's function calls cannot be decoded, and as a result, contract rules will not be matched, causing the transaction to be rejected by default.
124298

125299
## Running
126300

@@ -136,7 +310,7 @@ On successful startup, the application will print the following (or similar) out
136310
```
137311
INFO: WebSocket server listening on port 18501
138312
INFO: Transaction Firewall HTTP Server (to accept/reject transactions): http://eop-1.local:8454
139-
INFO: Validating Proxy is running
313+
INFO: Validating Proxy is running in interactive mode
140314
Proxy address (endpoint to be used in a wallet): "http://eop-1.local:18500"
141315
Ethereum RPC endpoint used by the firewall: "http://eop-1.local:8545"
142316
```

address_rules.json

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
[
2+
{
3+
"action": "allow",
4+
"from": "0x8626f6940E2eb28930eFb4CeF49B2d1F2C9C1199",
5+
"to": "0xdAC17F958D2ee523a2206206994597C13D831ec7",
6+
"comment": "Allow transfers to USDT contract"
7+
},
8+
{
9+
"action": "allow",
10+
"from": "*",
11+
"to": "0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D",
12+
"comment": "Allow all addresses to interact with Uniswap router"
13+
},
14+
{
15+
"action": "deny",
16+
"from": "0x8626f6940E2eb28930eFb4CeF49B2d1F2C9C1199",
17+
"to": "0x6B175474E89094C44Da98b954EedeAC495271d0F",
18+
"comment": "Deny specific wallet from interacting with DAI contract"
19+
},
20+
{
21+
"action": "allow",
22+
"from": "*",
23+
"to": "0x75271b9276d9b0104a16dfdcd62d40ba5666a9c3",
24+
"comment": "My rule"
25+
}
26+
]
27+

contract_rules.json

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
[
2+
{
3+
"action": "allow",
4+
"functionName": "transfer",
5+
"args": {},
6+
"comment": "Allow transfer function calls"
7+
},
8+
{
9+
"action": "deny",
10+
"functionName": "approve",
11+
"args": {},
12+
"comment": "Block all approve function calls"
13+
},
14+
{
15+
"action": "allow",
16+
"functionName": "swapExactTokensForTokens",
17+
"args": {},
18+
"comment": "Allow Uniswap swap function"
19+
},
20+
{
21+
"action": "allow",
22+
"functionName": "transfer",
23+
"args": {
24+
"to": "0x1234567890123456789012345678901234567890",
25+
"value": "1000000000000000000"
26+
},
27+
"comment": "Allow transfer of 1 ETH to specific address"
28+
},
29+
{
30+
"action": "deny",
31+
"functionName": "approve",
32+
"args": {
33+
"spender": "*",
34+
"amount": "115792089237316195423570985008687907853269984665640564039457584007913129639935"
35+
},
36+
"comment": "Block unlimited token approvals"
37+
}
38+
]

frontend/src/App.tsx

Lines changed: 32 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,22 @@
11
import "./App.css";
22
import backgroundImage from "/src/assets/shielded_logo.png";
3-
import { ConnectionAlert } from "@/components/ConnectionAllert.tsx";
3+
import { Alert } from "@/components/Alert";
44
import { TransactionDialog } from "@/components/TransactionDialog.tsx";
55
import { useWebSocket } from "@/hooks/useWebSocket";
66
import { useEffect, useState } from "react";
77

88
type AppConfig = {
99
WS_URL?: string;
10+
FIREWALL_MODE?: "interactive" | "non-interactive";
1011
INTERACTIVE_MODE_TIMEOUT_SEC?: number;
1112
};
13+
1214
function App() {
1315
const appConfig = (window as Window & { __CONFIG?: AppConfig }).__CONFIG;
1416
const websocketUrl = appConfig?.WS_URL || "ws://localhost:18501";
1517
const interactiveModeTimeoutSec =
1618
appConfig?.INTERACTIVE_MODE_TIMEOUT_SEC || 30;
19+
const firewallMode = appConfig?.FIREWALL_MODE || "non-interactive";
1720

1821
const { status, currentTransaction, connect, sendMessage } =
1922
useWebSocket(websocketUrl);
@@ -67,20 +70,37 @@ function App() {
6770
backgroundImage: `url(${backgroundImage}), radial-gradient(#1f0D36, #000000)`,
6871
}}
6972
>
70-
{(status === "error" || status === "disconnected") && (
71-
<ConnectionAlert
72-
onRetryClick={handleRetryConnection}
73+
{firewallMode === "non-interactive" ? (
74+
<Alert
7375
isOpen={isAlertOpen}
7476
onOpenChange={setIsAlertOpen}
77+
title={"Non-interactive mode"}
78+
body={
79+
"Firewall is set to non-interactive mode. This means all transactions will be automatically accepted or rejected based on predefined rules without user intervention."
80+
}
81+
buttons={[]}
7582
/>
76-
)}
77-
{currentTransaction && isDialogOpen && (
78-
<TransactionDialog
79-
transactionPayload={currentTransaction}
80-
onAccept={handleAcceptTransaction}
81-
onReject={handleRejectTransaction}
82-
timeout={interactiveModeTimeoutSec * 1000}
83-
/>
83+
) : (
84+
<>
85+
{(status === "error" || status === "disconnected") && (
86+
<Alert
87+
isOpen={isAlertOpen}
88+
onOpenChange={setIsAlertOpen}
89+
buttons={[
90+
{ label: "Close", variant: "outline" },
91+
{ label: "Try Again", onClick: handleRetryConnection },
92+
]}
93+
/>
94+
)}
95+
{currentTransaction && isDialogOpen && (
96+
<TransactionDialog
97+
transactionPayload={currentTransaction}
98+
onAccept={handleAcceptTransaction}
99+
onReject={handleRejectTransaction}
100+
timeout={interactiveModeTimeoutSec * 1000}
101+
/>
102+
)}
103+
</>
84104
)}
85105
</main>
86106
);

0 commit comments

Comments
 (0)