-
Notifications
You must be signed in to change notification settings - Fork 13
Description
First task of issue #83
Data Model
class Instruction:
prev: List[Instruction]
next: List[Instruction]
line_num: int
source_code_line: str
comment: str
comments_before_ins: List[str]
tealer_comments: List[str]
bb: Optional["BasicBlock"]
supported_version: int
supported_mode: Mode.Application | Mode.LogicSig
class BasicBlock:
instructions: List[Instruction]
prev: List[BasicBlock]
next: List[BasicBlock]
idx: int
subroutine: Optional["Subroutine"]
tealer_comments: List[str]
class Subroutine:
subroutine_name: str
entry: BasicBlock
basicblocks: List[BasicBlock]
exit_blocks: List[BasicBlock]
contract: Optional["Teal"] = None
class Function:
cfg: List[BasicBlock]
function_name: str
contract: Optional["Teal"]
class Teal:
contract_name: str
version: int
execution_mode: ContractType
instructions: List[Instruction]
basicblocks: List[BasicBlock]
main: Subroutine
subroutines: Dict[str, Subroutine]
functions: Dict[str, Function]
class ContractType(Enum):
LogicSig
ApprovalProgram
ClearStateProgramCFG of the contract is divided into subroutines. Every subroutine is an independent CFG. The blocks of one subroutines are not connected to blocks of any other subroutine. In the CFG, The callsub instruction is connected with immediately next instruction (the return point/address of the called subroutine.)
The contract needs to be divided into "Functions/Operations" as well. Functions are equivalent to ARC-4 methods. Every function should have their own CFG for analysis.
The CFG containing the basicblocks that are not part of any of the subroutines is referred to as "contract-entry" CFG. The execution always starts at the entry block of this CFG. If we consider every subroutine as external contract/program then "contract-entry" CFG can be considered as the CFG of the entire contract.
If the method/function dispatcher used by the contract does not touch any of the subroutines, each function can be represented by a CFG that consists of basicblocks that are related to that particular function.
In common use cases, method dispatcher is not part of any subroutine. The method dispatcher generated by PyTeal's Router class also does not touch the subroutines. Under this assumption, The "contract-entry" CFG can be divided into individual function CFGs. Functions can be analyzed independently.
A block might be part of multiple functions. In that case, Every function should have their own copy of the block.
class Transaction:
type: TransactionType
logic_sig: Optional[Function]
application: Optional[Function]
logic_sig_group_context: Optional[Dict[Instruction, Transaction]]
application_group_context: Optional[Dict[Instruction, Transaction]]
class TransactionType(Enum):
Payment
AssetConfig
AssetTransfer
AssetFreeze
KeyReg
ApplicationCallA transaction may involve execution of both a LogicSig and an Application.
The group-context information contains a mapping from every group related instruction of a function to the Transaction object.
group related instructions:
- gtxn t f, gtxna t f i, gtxns f, gtxnsa f i, gtxnas t f, gtxnsas f
- gaid t, gaids,
- gload t i, gloads i, gloadss
group-context of "gtxn 1 RekeyTo" would point to the Transaction object representing the transaction at index 1.
The Transaction object for an instruction is specified for each execution of the function.
A group of transactions may involve execution of same function of a program in multiple transactions Or a block of code can be executed for two different functions and both functions are part of a group. So, The group-context should be provided per (Transaction, Function) because a group-context instruction can refer to different Transaction object for each of the execution.
At the same time, It is not possible to provide a Transaction object for instructions which are executed multiple times in a given execution.
If a gtxns f instruction is used in a loop with different transaction indices then the Transaction object will be different for each iteration. These kind of instructions are ignored for the analysis.
class GroupConfig:
transactions: List[Transaction]
tealer: Optional[Tealer] = None
class Tealer:
contracts: List[Teal]
group_configs: List[GroupConfig]Detector API
class AbstractDetector:
def __init__(self, tealer: Tealer):
pass
def detect(self):
passThe detectors can be classified into two classes:
- Type 1: Detectors which does not need any information about other contract executed in the group to detect the bug.
- Example "Inner Txn Fee must be zero" bug: The detector have to find all the inner transactions in a function and check if the fee field is set to zero or not. Execution of other contracts does not invalidate the bug.
- Type 2: Detectors which require information of group transactions.
- Example RekeyTo, AssetCloseTo, ... Any bug which relies on possibility of value for a transaction field. A different contract executed in the same group can access the transaction field and perform validations on it.
Type 1 detectors will have to use the Tealer.contracts to access the individual contracts and run analysis on each of them.
Type 2 detectors will have to use the Tealer.group_configs to access the transaction configs and run analysis on each group of transactions.
Output format:
TBD