分析一套源代碼的代碼規範和風格並討論如何改進優化代碼

  這次選取的代碼是智能合約漏洞檢測及分析工具Oyente的源代碼。html

1、Oyente

   Oyente是melon.fund於2018年10月發佈的一款爲現有的以太坊智能合約開發人員構建的符號執行工具,以發現智能合約中潛在的安全漏洞。git

  開發語言:Pythongithub

  工具類型:靜態分析工具web

  分析內容:EVM字節碼編程

  工具原理:Oyente將須要分析的合約的字節碼和當前以太坊的全局狀態做爲輸入,檢測合約是否存在安全問題,並向用戶輸入有問題的符號路徑。在這個過程當中,使用Z3求解器來肯定可知足性。安全

  模塊劃分:Oyente遵循模塊化設計,由四個主要組件組成,分別是分別是CFGBuilder,Explorer,CoreAnalysis和Validator。app

      (1)CFGBuilder:構造合約的控制流圖,其中節點是基本執行塊,邊表示這些塊之間的執行跳轉。模塊化

      (2)Explorer:主要模塊,象徵性地執行合約。函數

      (3)CoreAnalsis:Explorer的輸出將輸入CoreAnalsis,在其中咱們實現針對漏洞的邏輯。工具

      (4)Validator:在向用戶報告前,由Validator確認是否有誤報。

  主要做用:(1)幫助開發人員編寫更好的合約 (2)防止用戶調用有問題的合約

  侷限性:可能會錯過嚴重違規、可能會產生誤報、也可能沒法在現實合約中得到足夠的代碼覆蓋率(在Parity錢包僅得到20.2%的覆蓋率)

 

 

  源代碼:https://github.com/melonproject/oyente  

  相關論文:Loi LuuDuc-Hiep ChuHrishi OlickelPrateek SaxenaAquinas HoborMaking Smart Contracts Smarter. IACR Cryptology ePrint Archive 2016: 633 (2016)

2、源代碼分析

(一)源代碼特色

一、目錄結構

圖1 源代碼目錄結構

  作法:除了自動生成的文件外,該源代碼分紅了三個文件夾:mic_utils、oyente、web,分別存儲接口相關代碼、oyente實現相關代碼和網頁相關代碼,結構簡明清晰,讓人一目瞭然。三個文件夾中各自的內容以下圖2-4所示。

圖2 misc_utils文件夾內容

圖3 oyente文件夾內容

圖4 web文件夾內容

二、命名規範

 1 def ceil32(x):
 2     return x if x % 32 == 0 else x + 32 - (x % 32)
 3 
 4 def isSymbolic(value):
 5     return not isinstance(value, six.integer_types)
 6 
 7 def isReal(value):
 8     return isinstance(value, six.integer_types)
 9 
10 def isAllReal(*args):
11     for element in args:
12         if isSymbolic(element):
13             return False
14     return True
15 
16 def to_symbolic(number):
17     if isReal(number):
18         return BitVecVal(number, 256)
19     return number
20 
21 def to_unsigned(number):
22     if number < 0:
23         return number + 2**256
24     return number
25 
26 def to_signed(number):
27     if number > 2**(256 - 1):
28         return (2**(256) - number) * (-1)
29     else:
30         return number
31 
32 def check_sat(solver, pop_if_exception=True):
33     try:
34         ret = solver.check()
35         if ret == unknown:
36             raise Z3Exception(solver.reason_unknown())
37     except Exception as e:
38         if pop_if_exception:
39             solver.pop()
40         raise e
41     return ret

  作法:

  (1)變量名:因爲Python中變量不用提早定義,此處直接使用小寫字母來表示變量

  (2)函數名:使用了駝峯命名法(Camel-Case),即第一個單詞以小寫字母開始;第二個單詞的首字母大寫或每個單詞的首字母都採用大寫字母;且使用了易於理解的命名,讓人見其形而知其意。

(二)符合代碼規範和風格通常要求的作法

  一、組織結構和目錄結構都比較清晰

  二、使用了編程中經常使用的駝峯命名法,命名比較規範且易於理解

  三、嚴格使用縮進和大小寫來進行組織

(三)有悖於「代碼的簡潔、清晰、無歧義」基本原則的作法

  全程沒有註釋,即便閱讀了工具相關的論文以後也不能理解代碼的具體含義和用途。

  改進方式:在合適的地方進行註釋,有助於對代碼的理解和應用。

3、Python編碼規範(國內)

(一)格式上的編碼規範

行長度:每行不超過80個字符;不要用反斜槓鏈接行

    如下狀況除外:(1)長的導入模塊語句

           (2)註釋裏的URL

語句:一般每一個語句應該獨佔一行

導入格式:每一個導入應該獨佔一行

縮進:用4個空格來縮進代碼。絕對不可使用Tab鍵,也不能將Tab和空格混用!!!

空行:頂級定義之間空兩行, 方法定義之間空一行。

空格:按照標準的排版規範來使用標點兩邊的空格

  (1)括號內不能有空格

  (2)逗號, 分號, 冒號前面不要加空格, 但在它們後面應該加空格(除了在行尾)

  (3)參數列表, 索引或切片的左括號前不該加空格

  (4)在二元操做符兩邊都加上一個空格, 好比賦值(=), 比較(==, <, >, !=, <>, <=, >=, in, not in, is, is not), 布爾(and, or, not);算術運算符自行把握,但要注意保持一致

  (5)當'='用於指示關鍵字參數或默認參數值時, 不要在其兩側使用空格

分號:不要在行尾加分號, 也不要用分號將兩條命令放在同一行。

括號:寧缺毋濫的使用括號。

   除非是用於實現行鏈接, 不然不要在返回語句或條件語句中使用括號; 不過在元組兩邊使用括號是能夠的

(二)命名規範

應該避免的名稱:

(1)單字符名稱, 除了計數器和迭代器.

(2)包/模塊名中的連字符(-)

(3)雙下劃線開頭並結尾的名稱(Python保留, 例如__init__)

命名約定:

(1)所謂"內部(Internal)"表示僅模塊內可用, 或者, 在類內是保護或私有的.

(2)用單下劃線(_)開頭表示模塊變量或函數是protected的(使用import * from時不會包含).

(3)用雙下劃線(__)開頭的實例變量或方法表示類內私有.

(4)將相關的類和頂級函數放在同一個模塊裏. 不像Java, 不必限制一個類一個模塊.

(5)對類名使用大寫字母開頭的單詞(如CapWords, 即Pascal風格), 可是模塊名應該用小寫加下劃線的方式(如lower_with_under.py). 儘管已經有不少現存的模塊使用相似於CapWords.py這樣的命名, 但如今已經不鼓勵這樣作, 由於若是模塊名碰巧和類名一致, 這會讓人困擾

(三)函數和方法

【下文所指的函數,包括函數, 方法以及生成器】

一個函數必需要有文檔字符串, 除非它知足如下條件:外部不可見、很是短小、簡單明瞭

文檔字符串:

(1)應該包含函數作什麼, 以及輸入和輸出的詳細描述

(2)應該提供足夠的信息, 當別人編寫代碼調用該函數時, 只要看文檔字符串就能夠了

(3)對於複雜的代碼, 在代碼旁邊加註釋會比使用文檔字符串更有意義

 關於函數的幾個方面(以下所示:Args、Returns、Raises)應該在特定的小節中進行描述記錄, 每節應該以一個標題行開始, 標題行以冒號結尾;除標題行外,,其餘內容應被縮進2個空格。

  Args:列出每一個參數的名字, 並在名字後使用一個冒號和一個空格, 分隔對該參數的描述.若是描述太長超過了單行80字符,用2或者4個空格的懸掛縮進(與文件其餘部分保持一致)。 描述應該包括所需的類型和含義, 若是一個函數接受*foo(可變長度參數列表)或者**bar (任意關鍵字參數), 應該詳細列出*foo和**bar。
   Returns:(或者 Yields: 用於生成器)描述返回值的類型和語義。 若是函數返回None, 這一部分能夠省略。
   Raises:列出與接口有關的全部異常。
 1 def fetch_bigtable_rows(big_table, keys, other_silly_variable=None):
 2     """Fetches rows from a Bigtable.
 3 
 4     Retrieves rows pertaining to the given keys from the Table instance
 5     represented by big_table.  Silly things may happen if
 6     other_silly_variable is not None.
 7 
 8     Args:
 9         big_table: An open Bigtable Table instance.
10         keys: A sequence of strings representing the key of each table row
11             to fetch.
12         other_silly_variable: Another optional variable, that has a much
13             longer name than the other args, and which does nothing.
14 
15     Returns:
16         A dict mapping keys to the corresponding table row data
17         fetched. Each row is represented as a tuple of strings. For
18         example:
19 
20         {'Serak': ('Rigel VII', 'Preparer'),
21          'Zim': ('Irk', 'Invader'),
22          'Lrrr': ('Omicron Persei 8', 'Emperor')}
23 
24         If a key from the keys argument is missing from the dictionary,
25         then that row was not found in the table.
26 
27     Raises:
28         IOError: An error occurred accessing the bigtable.Table object.
29     """
30     pass
相關文章
相關標籤/搜索