Skip to content

Commit

Permalink
update python and js bindings with control_flow_graph and basic_blocks
Browse files Browse the repository at this point in the history
  • Loading branch information
cdump committed Feb 17, 2025
1 parent eb76867 commit 07e0558
Show file tree
Hide file tree
Showing 7 changed files with 686 additions and 29 deletions.
72 changes: 72 additions & 0 deletions evmole.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,66 @@ class StorageRecord:
reads: List[str]
writes: List[str]

class DynamicJump:
"""
Represents a dynamic jump destination in the control flow.
Attributes:
path (List[int]): Path of basic blocks leading to this jump.
to (Optional[int]): Target basic block offset if known, None otherwise.
"""
path: List[int]
to: Optional[int]

class BlockType:
"""
Represents the type of a basic block and its control flow.
This is an enum-like class, all child classes are derived from BlockType class
"""
class Terminate:
"""Block terminates execution"""
success: bool # True for normal termination (STOP/RETURN), False for REVERT/INVALID

class Jump:
"""Block ends with unconditional jump"""
to: int # Destination basic block offset

class Jumpi:
"""Block ends with conditional jump"""
true_to: int # Destination if condition is true
false_to: int # Destination if condition is false (fall-through)

class DynamicJump:
"""Block ends with jump to computed destination"""
to: List[DynamicJump] # Possible computed jump destinations

class DynamicJumpi:
"""Block ends with conditional jump to computed destination"""
true_to: List[DynamicJump] # Possible computed jump destinations if true
false_to: int # Destination if condition is false (fall-through)

class Block:
"""
Represents a basic block in the control flow graph.
Attributes:
start (int): Byte offset where the block's first opcode begins
end (int): Byte offset where the block's last opcode begins
btype (BlockType): Type of the block and its control flow.
"""
start: int
end: int
btype: BlockType

class ControlFlowGraph:
"""
Represents the control flow graph of the contract bytecode.
Attributes:
blocks (List[Block]): List of basic blocks in the control flow graph.
"""
blocks: List[Block]

class Contract:
"""
Contains analyzed information about a smart contract.
Expand All @@ -47,11 +107,17 @@ class Contract:
None if storage layout was not extracted
disassembled (Optional[List[Tuple[int, str]]]): List of bytecode instructions, where each element is [offset, instruction].
None if disassembly was not requested
basic_blocks (Optional[List[Tuple[int, int]]]): List of basic block ranges as (first_op, last_op) offsets.
None if basic blocks were not requested
control_flow_graph (Optional[ControlFlowGraph]): Control flow graph of the contract.
None if control flow analysis was not requested
"""

functions: Optional[List[Function]]
storage: Optional[List[StorageRecord]]
disassembled: Optional[List[Tuple[int, str]]]
basic_blocks: Optional[List[Tuple[int, int]]]
control_flow_graph: Optional[ControlFlowGraph]

def contract_info(
code: Union[bytes, str],
Expand All @@ -61,6 +127,8 @@ def contract_info(
state_mutability: bool = False,
storage: bool = False,
disassemble: bool = False,
basic_blocks: bool = False,
control_flow_graph: bool = False,
) -> Contract:
"""
Extracts information about a smart contract from its EVM bytecode.
Expand All @@ -76,6 +144,10 @@ def contract_info(
Defaults to False.
disassemble (bool, optional): When True, includes disassembled bytecode.
Defaults to False.
basic_blocks (bool, optional): When True, extracts basic block ranges.
Defaults to False.
control_flow_graph (bool, optional): When True, builds control flow graph.
Defaults to False.
Returns:
Contract: Object containing the requested smart contract information. Fields that
Expand Down
111 changes: 105 additions & 6 deletions javascript/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,14 +114,17 @@ Analyzes contract bytecode and returns contract information based on specified o
| args | <code>Object</code> | Configuration options for the analysis |
| [args.selectors] | <code>boolean</code> | When true, includes function selectors in the output |
| [args.arguments] | <code>boolean</code> | When true, includes function arguments information |
| [args.state_mutability] | <code>boolean</code> | When true, includes state mutability information for functions |
| [args.stateMutability] | <code>boolean</code> | When true, includes state mutability information for functions |
| [args.storage] | <code>boolean</code> | When true, includes contract storage layout information |
| [args.disassemble] | <code>boolean</code> | When true, includes disassembled bytecode |
| [args.basicBlocks] | <code>boolean</code> | When true, includes basic block analysis |
| [args.controlFlowGraph] | <code>boolean</code> | When true, includes control flow graph analysis |
<a name="Contract"></a>
### Contract : <code>Object</code>
Contains the analysis results of a contract
**Kind**: global typedef
**Properties**
Expand All @@ -130,25 +133,29 @@ Analyzes contract bytecode and returns contract information based on specified o
| [functions] | [<code>Array.&lt;ContractFunction&gt;</code>](#ContractFunction) | Array of functions found in the contract. Not present if no functions were extracted |
| [storage] | [<code>Array.&lt;StorageRecord&gt;</code>](#StorageRecord) | Array of storage records found in the contract. Not present if storage layout was not extracted |
| [disassembled] | <code>Array.&lt;Array.&lt;(number\|string)&gt;&gt;</code> | Array of bytecode instructions, where each element is [offset, instruction] |
| [basicBlocks] | <code>Array.&lt;Array.&lt;number&gt;&gt;</code> | Array of basic blocks found in the contract. Not present if basic blocks were not analyzed. |
| [controlFlowGraph] | [<code>ControlFlowGraph</code>](#ControlFlowGraph) | Control flow graph representation. Not present if CFG was not generated. |
<a name="ContractFunction"></a>
### ContractFunction : <code>Object</code>
Represents a function found in the contract bytecode
**Kind**: global typedef
**Properties**
| Name | Type | Description |
| --- | --- | --- |
| selector | <code>string</code> | Function selector as a 4-byte hex string without '0x' prefix (e.g., 'aabbccdd') |
| bytecode_offset | <code>number</code> | Starting byte offset within the EVM bytecode for the function body |
| bytecodeOffset | <code>number</code> | Starting byte offset within the EVM bytecode for the function body |
| [arguments] | <code>string</code> | Function argument types in canonical format (e.g., 'uint256,address[]'). Not present if arguments were not extracted |
| [state_mutability] | <code>string</code> | Function's state mutability ("pure", "view", "payable", or "nonpayable"). Not present if state mutability were not extracted |
| [stateMutability] | <code>string</code> | Function's state mutability ("pure", "view", "payable", or "nonpayable"). Not present if state mutability were not extracted |

<a name="StorageRecord"></a>


### StorageRecord : <code>Object</code>
Represents a storage record found in the contract

**Kind**: global typedef
**Properties**

Expand All @@ -159,3 +166,95 @@ Analyzes contract bytecode and returns contract information based on specified o
| type | <code>string</code> | Variable type (e.g., 'uint256', 'mapping(address => uint256)', 'bytes32') |
| reads | <code>Array.&lt;string&gt;</code> | Array of function selectors that read from this storage location |
| writes | <code>Array.&lt;string&gt;</code> | Array of function selectors that write to this storage location |

<a name="ControlFlowGraph"></a>

### ControlFlowGraph : <code>Object</code>
Represents the control flow graph of the contract bytecode

**Kind**: global typedef
**Properties**

| Name | Type | Description |
| --- | --- | --- |
| blocks | [<code>Array.&lt;Block&gt;</code>](#Block) | List of basic blocks in the control flow graph |

<a name="Block"></a>

### Block : <code>Object</code>
Represents a basic block in the control flow graph

**Kind**: global typedef
**Properties**

| Name | Type | Description |
| --- | --- | --- |
| start | <code>number</code> | Byte offset where the block's first opcode begins |
| end | <code>number</code> | Byte offset where the block's last opcode begins |
| type | <code>&#x27;Terminate&#x27;</code> \| <code>&#x27;Jump&#x27;</code> \| <code>&#x27;Jumpi&#x27;</code> \| <code>&#x27;DynamicJump&#x27;</code> \| <code>&#x27;DynamicJumpi&#x27;</code> | Block type |
| data | [<code>DataTerminate</code>](#DataTerminate) \| [<code>DataJump</code>](#DataJump) \| [<code>DataJumpi</code>](#DataJumpi) \| [<code>DataDynamicJump</code>](#DataDynamicJump) \| [<code>DataDynamicJumpi</code>](#DataDynamicJumpi) | Type Type-specific block data |

<a name="DataTerminate"></a>

### DataTerminate : <code>Object</code>
**Kind**: global typedef
**Properties**

| Name | Type | Description |
| --- | --- | --- |
| success | <code>boolean</code> | true for normal termination (STOP/RETURN), false for REVERT/INVALID |

<a name="DataJump"></a>

### DataJump : <code>Object</code>
**Kind**: global typedef
**Properties**

| Name | Type | Description |
| --- | --- | --- |
| to | <code>number</code> | Destination basic block offset |

<a name="DataJumpi"></a>

### DataJumpi : <code>Object</code>
**Kind**: global typedef
**Properties**

| Name | Type | Description |
| --- | --- | --- |
| true_to | <code>number</code> | Destination if condition is true |
| false_to | <code>number</code> | Destination if condition is false (fall-through) |

<a name="DataDynamicJump"></a>

### DataDynamicJump : <code>Object</code>
**Kind**: global typedef
**Properties**

| Name | Type | Description |
| --- | --- | --- |
| to | [<code>DynamicJump</code>](#DynamicJump) | Possible computed jump destinations |

<a name="DataDynamicJumpi"></a>

### DataDynamicJumpi : <code>Object</code>
**Kind**: global typedef
**Properties**

| Name | Type | Description |
| --- | --- | --- |
| true_to | [<code>DynamicJump</code>](#DynamicJump) | Possible computed jump destinations if true |
| false_to | <code>number</code> | Destination if condition is false (fall-through) |

<a name="DynamicJump"></a>

### DynamicJump : <code>Object</code>
Represents a dynamic jump destination in the control flow

**Kind**: global typedef
**Properties**

| Name | Type | Description |
| --- | --- | --- |
| path | <code>Array.&lt;number&gt;</code> | Path of basic blocks leading to this jump |
| [to] | <code>number</code> | Target basic block offset if known. Optional |
110 changes: 107 additions & 3 deletions python/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@ def contract_info(code: Union[bytes, str],
arguments: bool = False,
state_mutability: bool = False,
storage: bool = False,
disassemble: bool = False) -> Contract
disassemble: bool = False,
basic_blocks: bool = False,
control_flow_graph: bool = False) -> Contract
```

Extracts information about a smart contract from its EVM bytecode.
Expand All @@ -33,8 +35,9 @@ Extracts information about a smart contract from its EVM bytecode.
- `arguments` - When True, extracts function arguments.
- `state_mutability` - When True, extracts function state mutability.
- `storage` - When True, extracts the contract's storage layout.
- `disassemble` When True, includes disassembled bytecode.

- `disassemble` - When True, includes disassembled bytecode.
- `basic_blocks` - When True, extracts basic block ranges.
- `control_flow_graph` - When True, builds control flow graph.

**Returns**:

Expand All @@ -48,6 +51,8 @@ class Contract():
functions: Optional[List[Function]]
storage: Optional[List[StorageRecord]]
disassembled: Optional[List[Tuple[int, str]]]
basic_blocks: Optional[List[Tuple[int, int]]]
control_flow_graph: Optional[ControlFlowGraph]
```

Contains analyzed information about a smart contract.
Expand All @@ -57,6 +62,8 @@ Contains analyzed information about a smart contract.
- `functions` - List of detected contract functions. None if no functions were extracted
- `storage` - List of contract storage records. None if storage layout was not extracted
- `disassembled` - List of bytecode instructions, where each element is [offset, instruction]. None if disassembly was not requested
- `basic_blocks` - List of basic block ranges as (first_op, last_op) offsets. None if basic blocks were not requested
- `control_flow_graph` - Control flow graph of the contract. None if control flow analysis was not requested

### Function

Expand Down Expand Up @@ -99,3 +106,100 @@ Represents a storage variable record in a smart contract's storage layout.
- `type` - Variable type (e.g., 'uint256', 'mapping(address => uint256)', 'bytes32').
- `reads` - List of function selectors that read from this storage location.
- `writes` - List of function selectors that write to this storage location.

### ControlFlowGraph

```python
class ControlFlowGraph():
blocks: List[Block]
```

Represents the control flow graph of the contract bytecode.

**Attributes**:

- `blocks` - List of basic blocks in the control flow graph

### Block

```python
class Block():
start: int
end: int
btype: BlockType
```

Represents a basic block in the control flow graph.

**Attributes**:

- `start` - Byte offset where the block's first opcode begins
- `end` - Byte offset where the block's last opcode begins
- `btype` - Type of the block and its control flow


### BlockType

```python
class BlockType():
class Terminate:
success: bool

class Jump:
to: int

class Jumpi:
true_to: int
false_to: int

class DynamicJump:
to: List[DynamicJump]

class DynamicJumpi:
true_to: List[DynamicJump]
false_to: int
```

Represents the type of a basic block and its control flow.

This is an enum-like class, all child classes are derived from `BlockType` class

#### Terminate
Block terminates execution
- `success` - True for normal termination (STOP/RETURN), False for REVERT/INVALID


#### Jump
Block ends with unconditional jump
- `to` - Destination basic block offset

#### Jumpi
Block ends with conditional jump
- `true_to` - Destination if condition is true
- `false_to` - Destination if condition is false (fall-through)

#### DynamicJump
Block ends with jump to computed destination
- `to` - Possible computed jump destinations

#### DynamicJumpi
Block ends with conditional jump to computed destination
- `true_to` - Possible computed jump destinations if true
- `false_to` - Destination if condition is false (fall-through)


### DynamicJump

```python
class DynamicJump():
path: List[int]
to: Optional[int]
```

Represents a dynamic jump destination in the control flow.

**Attributes**:

- `path` - Path of basic blocks leading to this jump
- `to` - Target basic block offset if known, None otherwise

Loading

0 comments on commit 07e0558

Please sign in to comment.