{'fraction_non_alphanumeric': 0.026150542311142524, 'fraction_numerical': 0.007652159730339522, 'mean_word_length': 4.986574450772986, 'pattern_counts': {'":': 0, '<': 0, '<?xml version=': 0, '>': 0, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
Practical Mitigation of Smart Contract Bugs

In spite of their popularity, developing secure smart contracts remains a challenging task. Existing solutions are either impractical as they do not support many complex real-world contracts or leave the burden to developers for fixing bugs. In this paper, we propose the first practical smart contract compiler, called HCC, which automatically inserts security hardening checks at the source-code level. HCC develops a code property graph (CPG) to model control-flows and data-flows of a given smart contract. Due to the CPG notation, HCC can be applied to various smart contract platforms and programming languages. We demonstrate the effectiveness of this approach on Ethereum's Solidity smart contracts and show that it efficiently mitigates reentrancy and integer bugs. We also show how to integrate HCC within other blockchain platforms such as Hyperledger Fabric. Our evaluation on 10k real-world contracts demonstrates that HCC is highly practical and effective.


I. INTRODUCTION
Smart contracts are supported by most modern blockchain technologies as they allow to execute decentralized programs, which encode business logic within the blockchain. When used within existing cryptocurrencies such as Ethereum, smart contracts often handle very large amounts of cryptocurrency-often worth millions of US-Dollars. Unfortunately, the supporting tooling around smart contracts is still in its infancy. Namely, a number of important safety and security features prevalent in modern day development cycles are either not integrated within smart contracts or not sufficiently usable.
Several high-profile attacks on the Ethereum blockchain, such as the TheDAO attack, the Parity Multisig Wallet attack, and the more recent Lendf.me and Uniswap attacks have fueled research to strengthen the security of smart contracts. The bulk of the research on smart contract security is rooted in formal methods and verification [15,17,18,34]. Other proposals approach smart contract security from a testing perspective and utilize symbolic execution [19,26] or fuzzing [47] to generate inputs and identify bugs and security vulnerabilities. However, many of the proposed solutions are impractical for smart contract developers today as they are typically hard to use, suffer from false positives and negatives, or require huge manual effort by developers. Most importantly, the majority of existing approaches leave the burden on the developer for patching the vulnerability and sorting out false alarms.
Recently, several automated smart contract bytecode rewriting approaches have been proposed [32,51] but these cannot handle complex attack patterns such as reentrancy, are incompatible with the upcoming eWASM bytecode format [10], their inserted security checks can hardly be verified and inspected by developers, and they cannot be integrated in other blockchain systems such as Hyperledger Fabric. Similarly, contract repair tools that operate as source-to-source compilers such as Solythesis [20] and sGuard [25] do either rely on userspecified invariants, do not support many real-world contracts, or provide limited effectiveness (see Section VI).
Contributions. In this paper, we propose the first practical smart contract compiler, called HCC (hardening contract compiler), that automatically inserts security checks at the source-code level. HCC ensures full transparency for smart contract developers and is fully compatible with other source code related workflows. It leverages the concept of codeproperty graphs (CPGs) [49] introducing the first CPG schema to model smart contract code and bug types. That is, we develop a CPG to model control-flows and data-flows of the contract and query the CPG to detect potential vulnerabilities (Section III). Our design overcomes a set of unique challenges (Section III-A), namely: (1) it supports inter-procedural and inter-contract analysis, (2) it accurately determines mitigation points, and (3) provides support for a highly dynamic smart contract ecosystem.
Due to the CPG, HCC can be applied to diverse smart contract platforms and programming languages: we introduce an implementation of HCC that focuses on the Solidity language for Ethereum contracts (Section IV) and provide a detailed analysis of how HCC can be applied to the popular Hyperledger Fabric platform (Section V).
While HCC supports a wide variety of bugs, we demonstrate its effectiveness on all known patterns of reentrancy and integer overflow bugs (Section IV). These two bug classes are responsible for the most notorious and disastrous attacks observed on the Ethereum blockchain so far and have been intensively investigated in the literature. Furthermore, focusing on these bug classes allows us to evaluate HCC based on existing approaches [25,31,44,52].
We conducted an extensive evaluation (Section VI) to measure several aspects of HCC. To do so, we collected a dataset of 10k Solidity smart contracts. Our dataset especially includes vulnerable contracts based on the state-of-the-art in this space from Torres et al. [44] for integer bugs as well as contracts from Rodler et al. [31] and Zhou et al. [52] for reentrancy bugs. Our evaluation on 31,629 replayed transactions on the mentioned vulnerable contracts shows that HCC effectively prevents all known attack transactions without hampering legitimate transactions. We also validate the integer hardening of HCC based on existing vulnerability detection tools [44] and find that these tools raise no alarms for contracts hardened with HCC. Further, we compare HCC with existing source-level contract repair tools [25]. Our experiments show that these do not support many real-world contracts and often fail to protect against real-world exploits (Section VI-C). Finally, we apply HCC to the 25 top-valued Ethereum contracts with source code available as reported by etherscan.io to demonstrate that our approach handles large, complex, and frequently utilized contracts involving 49,265 transactions without affecting their functionality with a success rate of 99.99 %. The set of topvalued contracts even includes the Eth2 Deposit Contract which enables the transition to proof-of-stake for the Ethereum 2.0 platform. We also integrated HCC with Hyperledger Fabric and our results show that HCC was able to fix all the vulnerabilities reported in the five ERC-20 token contracts from the Osiris [44] dataset, without hampering the functionality of the contracts and with tolerable overhead.

II. PROBLEM STATEMENT & RELATED WORK
As there are many analysis tools and guidelines available for developers in order to improve smart contract security, we start by reviewing these approaches as a means to motivate the need for an automated approach to secure smart contracts. Security Guidelines. Several security guidelines have been created by well-known companies like ConsenSys [37] and Trail of Bits [9]. Such guidelines have been included in the documentation of smart contract programming languages [36]. However, they require developers to read, understand, and accurately follow the guidelines. Code Analysis Tooling. The literature features a plethora of analysis tooling that aims to support smart contract developers by pinpointing potential bugs and vulnerabilities. Basic syntax directed analysis usually uses the abstract syntax tree (AST) and is effective at providing immediate feedback to the developer. However, it cannot precisely discover complex security vulnerabilities. Various recently proposed static analysis tools translate smart contract code into a custom Datalog-based intermediate representation (IR) [4,5,14,45]. While these approaches provide good analysis capabilities, Datalog-based approaches usually suffer from scalability issues, forcing trade-offs between scalability and precision, and often do not faithfully model the EVM's semantics [35]. Similarly, symbolic execution-based approaches for smart contracts [12, 19, 21-23, 30, 43, 44, 48] achieve high precision, but suffer from scalability issues in practice.
Existing code analysis tools require developers to choose between good coverage and precision. Good coverage often entails many false alarms, which bears the risk that developers may simply start to ignore them. Tools with low number of false alarms on the other hand, often suffer from missing vulnerabilities. Lastly, all existing static and symbolic execution analysis tools for smart contracts just report potential vulnerabilities, but do not provide guidance to the developer on how to fix the discovered security issues.
Contract Hardening. Several automatic mitigation mechanisms have been proposed [7,20,25,31,42]. On-chain solutions [31,42] either require major changes to the whole blockchain ecosystem or to the way smart contracts are deployed. Further, bytecode-level analysis [12,19,21,22,44] and hardening tools [32,51] suffer from imprecision due to lack of high-level information (e.g., source types and control flow). For example, this was a major source for false alarms in prior work due to field-level over-tainting in data structures [31]. As such, these approaches cannot easily cover bug classes that involve complex control-flows and data-flows such as reentrancy. Moreover and most importantly, patching and hardening on the bytecode level [32,51] does not allow developers to inspect the modified contract.
More transparent approaches towards securing smart contracts follow a source-to-source compilation approach [20,25,50]. These allow developers to inspect the introduced changes. For instance, Solythesis [20] inserts runtime checks into Solidity smart contracts demonstrating the feasibility of enforcing smart contracts invariants at runtime. However, Solythesis requires a manual specification of invariants thereby requiring significant involvement of developers. Incorrect or incomplete invariants (i.e., specification bugs) would lead to vulnerable smart contracts. SCRepair [50] and sGuard [25] both leverage symbolic analyses techniques to perform sourceto-source compilation. Hence, similar to existing code analysis tooling based on symbolic analysis, they suffer from scalability issues. However, sGuard [25] aims at tackling this shortcoming by developing a custom symbolic analysis. Unfortunately, such custom symbolic analysis still does not support many complex, real-world contracts (cf. Section VI-C) and sacrifices security for performance (e.g., SGUARD does not prevent well-known exploits against ERC20 tokens). The Need for Practical & Automatic Source Code Hardening. Contrary to the majority of existing software, it is often cumbersome to publish a timely update to a vulnerable smart contract due to its decentralized nature. As such, it is best to minimize potential smart contract vulnerabilities before deployment. Nevertheless, current tooling around secure smart contract development has focused on identifying bugs with high precision, and existing source-to-source hardening approaches do not offer satisfactory assistance in producing bug fixes. To this date, there is no practical, automated approach to effectively and transparently mitigate vulnerabilities in smart contracts. As described earlier, existing source-to-source hardening approaches either rely on a correct specification of invariants [20] or test suites [50] which leaves the burden on the developers, or do not scale well due to the use of custom [25] or existing [50] symbolic analyses during vulnerability detection.

III. HCC: HARDENING CONTRACT COMPILER
In this section, we start by outlining the challenges encountered when designing smart contract compilers before introducing our smart contract compiler, called HCC (hardening contract compiler).

A. Challenges
There are a number of challenges that emerge when designing effective and practical smart contract compilers, namely: Challenge 1: Inter-Procedural and Inter-Contract Static Analysis. Smart contracts suffer from various subtle errors in the program code, e.g., an unexpected reentrant call leading to inconsistent state updates or integer bugs. A number of smart contract bugs-especially reentrancy bugs-can only be accurately discovered with inter-procedural and inter-contract analysis. However, performing inter-procedural and intercontract analysis for smart contracts is highly challenging since (1) smart contracts can create new smart contracts at runtime, (2) smart contracts use different types of memory for runtime data and persistent state, (3) smart contracts can be invoked through different entry functions, and (4) smart contracts interact with other contracts by invoking public functions of other contracts through so-called external calls. Hence, it is crucial to accurately model all the particularities of smart contracts. Challenge 2: Determining Mitigation Points. A key requirement when designing smart contract compilers is to know precisely where to insert runtime checks that harden the code against attacks. Moreover, some bug types can pose a challenge; especially if the bug is inside a program loop. Specifically, if there exists a bug in the loop header for the unary increment or decrement operation (e.g., i++), we need to investigate the entire loop to validate whether and how the affected variable is processed inside the loop body to avoid missing bugs. Challenge 3: Choosing Proper Runtime Security Checks. A hardening compiler should never break legitimate behavior of a contract. As the intentions of a contract cannot be inferred based on static analysis alone, this poses a challenge for compilerbased hardening, since we need to design the runtime security checks in a way that they are (1) sufficiently generic to harden against all bug flavors of a specific bug pattern, and (2) at the same time not too strict to revert legitimate executions. Challenge 4: Dealing with a Dynamic Ecosystem. The smart contract ecosystem and smart contract programming languages are continuously evolving. Currently, there are a number of blockchains platforms that support different smart contract programming languages (e.g., Solidity, Go, Java). Ultimately, we need to devise general purpose compilers that work independently of the underlying platform specifics and contract programming languages. Indeed, this would alleviate the need to come up with specific compilers for each platform and would considerably reduce the overhead incurred on developers when porting a compiler from one platform to the other.

B. Building block: Code Property Graphs
We now introduce a building block-code property graphsthat we will use throughout the paper. Code-property graphs (CPG) have been originally proposed to detect memory corruption bugs such as buffer overflow and memory disclosure in the Linux kernel [49]. A CPG is a graph representation of a program that unifies different perspectives on a program in  Figure 1: Architecture of HCC.
a single graph. More specifically, a CPG is a graph consisting of a program abstract syntax tree (AST), enriched with additional information such as control-flows and data-flows. Due to this unification, the CPG provides excellent analysis capabilities. The AST is a direct representation of the source code, its representation is highly suitable for code insertion and transformation. This means, the AST can be easily traversed, changed and emitted to pretty-printed source code again. Since the CPG contains the AST, we can leverage a CPG for both, analysis and hardening, of smart contracts. In what follows, we demonstrate for the first time how CPGs can be used as a unified compiler intermediate representation to perform automatic hardening against several bug patterns at source-code level. Applying the concept of CPGs to a compiler intermediate representation provides desirable flexibility that we leverage, e.g. to construct the CPG in a way that programming language constructs are independent of compiler versions (cf. Challenge 4 above). In the following, we describe the architecture and components of HCC in detail.

C. Overview of HCC
As shown in Figure 1, HCC comprises five phases: (1) CPG construction (2) CPG enrichment, (3) bug pattern discovery, (4) hardening, and (5) code generation. The first phase of HCC consists of transforming the AST representation of the source code of a smart contract into HCC's CPG representation. During CPG construction, we store information about the target compiler and compiler version in order to accurately generate hardened source code in the last phase of HCC.
HCC resolves function calls during the CPG construction. Resolving these function calls involves inserting edges into the CPG between the function call statement and the target function. These edges indicate the control flow transfer to the target function. This step is crucial to add inter-procedural control-flow data to tackle Challenge 1. However, this approach only works for functions whose implementation is directly available in the source code, as we cannot derive controlflow information for unimplemented functions. Hence, we introduce a enrichment phase after the initial CPG construction in which HCC (1) determines unresolvable function calls and (2) performs analysis on built-in API calls (e.g., in Ethereum msg.sender.call). In the former case, we label these as external calls to other contracts. The latter requires checking whether the call to the built-in API can result in a call to any public function of the contract, i.e., whether reentrancy is possible. Note that it is possible to introduce further analysis passes to the CPG enrichment phase, e.g., the write state analysis as described in Section IV-A. This makes HCC a highly flexible framework that can perform various sophisticated smart contract analysis tasks, while still providing the flexibility of adding new bug patterns.
In the third phase, HCC aims at discovering bugs using graph traversals on the enriched CPG. Specifically, the bug pattern discovery component searches for pre-defined patterns that represent a smart contract bug. For a hardening compiler, it is important for all static analysis passes to have a low number of missed bugs to insert all the necessary runtime checks. However, existing static analysis approaches typically optimize for a low number of false alarms to not overwhelm the developer with warnings. As such, we opted for developing our own conservative static analysis passes. In case a bug is discovered, we add a new node and multiple edges to the CPG that encode the bug instance and its location thereby tackling Challenge 2. This approach of directly integrating bug patterns into the compiler makes HCC independent of invariant specifications and guidelines, reducing the burden on smart contracts developers. In Section IV-B and Section IV-C, we show how to pinpoint the mitigation point for all patterns of reentrancy bugs and integer bugs-as well as how HCC handles the special case of program loops (Challenge 2).
The fourth phase mitigates the discovered bugs by inserting hardening patches. To generate a patch, HCC adopts several hardening strategies. A hardening strategy is based on a graph template and associated graph queries. HCC instantiates the template based on the contract's CPG and inserts the hardening patches as new nodes and edges in the CPG. As we describe in Section IV-A, we designed HCC's automated analysis and hardening such that the potential for accidentally breaking legitimate contract functionality is very low (Challenge 3). This is achieved by using CPG sub-graph templates that are instantiated in such a way that they are compatible to the existing contract code. This is highly dependent on the underlying bug class; in Section IV-A, we provide a detailed implementation of our graph templates.
Note that HCC emits the hardened AST during the code generation phase, which is contained in the CPG, as prettyprinted source code. To emit valid source code, HCC leverages the information collected during CPG construction about the target compiler and compiler version to correctly generate matching code thereby tackling Challenge 4.
Our modular design allows HCC to cover to a wide variety of bug types. Namely, to extend HCC to cover a new bug type, an analyst can define a new query-representing a certain graph traversal that defines the bug-specific control-and data-flow relations-on top of the existing CPG. If the current CPG does not contain the necessary details to detect a specific bug type, an additional enrichment step can be added to perform the analysis and extend the CPG with that information.

D. Phase 1: CPG Construction
In contrast to an AST, the CPG is a directed graph that consists of different kinds of nodes and directed edges that connect these nodes. Both nodes and edges have an arbitrary number of properties. For instance, a node representing a variable in the CPG features the property name to store the variable name. In the CPG nodes are labeled according to their semantics, i.e., the node representing a function is labeled Function and variables carry the label Variable or the label LocalVariable if the variable is local to a function. Labeling every piece of data in the CPG effectively creates a schema that facilitates further processing and analysis. It also allows HCC to store the analysis results into the CPG, which in turn enables HCC to provide a unified view on the source code and the analysis results.
In HCC, the AST is the prerequisite for generating the CPG and serves as starting point for further analysis steps such as compiler optimizations. The initial CPG is directly derived from the AST of a smart contract's source code. With parser generators such as ANTLR [3] and grammars for different programming languages readily available, it is easily possible to generate the AST directly from source code for various types of source languages. Since HCC's internal CPG representation preserves the general structure of the original AST, the CPG contains a node for every high-level syntactic element, e.g., any expression that is part of the source code, excluding white-space and punctuation characters. This is crucial as it enables HCC to transform the AST to contain hardening patches and to emit hardened source code. During CPG construction HCC resolves identifiers, e.g., function and variable names, and inserts edges between those identifiers and the variable or function they reference. For instance, we insert an edge between a function call expression and the called function in the CPG. This edge indicates a reference between the function name as an identifier and the actual function node in the CPG. Moreover, the inserted edge connects function call sites with their underlying implementation, if available, hereby providing inter-procedural control-flow information.

E. Phase 2: CPG Enrichment
Since ASTs are not well suited for sophisticated program analysis tasks, compilers typically convert the AST into a compiler-specific intermediate representation (IR). In contrast to the AST, the IR directly encodes information on controlflows and data-flows of a program. Many prior source-code based static analysis tools [11,18,41] similarly translate the AST into a custom intermediate representation before analysis.
Instead of converting the AST to a separate IR, HCC performs control-flow and data-flow analysis directly in the CPG, by enriching the AST-based CPG with control and data-flow information. For example, HCC analyzes function calls to insert inter-procedural control-flow data into the CPG. Moreover, our prototype implementation (see Section IV) performs intraprocedural data-flow analysis to detect accesses to variables, thereby integrating data-flow information with the CPG. The analysis passes over the CPG are represented as queries on top of the CPG, with the results further stored inside the CPG for additional processing during the hardening phase. During all analysis passes over the CPG, HCC maintains the AST as a sub-graph of the CPG-allowing HCC to easily re-emit source code.

F. Phase 3: Bug Pattern Discovery
The second major phase in HCC is the bug pattern discovery phase. In HCC's CPG-based approach, bug patterns are expressed in the form of different sub-graphs of the CPG. The presence in the CPG of a sub-graph matching with a bug pattern indicates that the contract is potentially susceptible to this bug type. In practice, such a sub-graph matching is performed using a query, i.e. a graph traversal, on top of the CPG. We refer to distinct matches of a bug pattern in the CPG as a bug instance. If a contract contains a specific bug, this means that one or more bug instances matching the pattern exist in the CPG. HCC extends the CPG with a node for each detected bug instance. Depending on the specific bug instance, the node may have specific properties as well as edges to other nodes in the CPG, such as the statements affected by the bug and a precise mitigation point. In this way, HCC leverages the CPG's aspect of providing a unified view on source code and its properties, including the potential bugs.
To find all bug instances, we developed our own conservative static analysis as part of HCC optimized for a low number of missed bugs at the expense of a higher number of false alarms compared to existing static analyses. We argue that this is a reasonable approach since the only side effect of mitigating a non-exploitable bug is just the execution of several additional instructions which perform an unnecessary check. This is a common and accepted side effect for compiledbased mitigation and hardening mechanisms as long as the performance overhead is low. Further, in contrast to traditional compiler-based hardening [1,38] HCC applies its hardening patches to the source code which allows developers to review the runtime security checks introduced by HCC and remove (if desired) unnecessary checks.

G. Phase 4: Hardening
The last major phase in HCC is the hardening phase, during which HCC generates hardening patches for the discovered bug instances in the previous phase. Similar to the definition of bug patterns for the bug pattern discovery phase, hardening patches in HCC are expressed as graph patterns that are used as templates for CPG-level patches against bugs. These hardening patches represent a hardening strategy in HCC, which have to be general enough to capture all possible bug instances. Hardening strategies and bug types naturally are closely related, therefore we define hardening strategies for all individual bug patterns. In Sections IV-B and IV-C, we give a detailed description of hardening strategies implemented in our HCC prototype. To perform the hardening, HCC iterates over all detected bugs by querying the enriched CPG for the bug instances. HCC applies the hardening strategy for the bug type of each of the bug instances, hereby resolving the expressions missing in the template. The resolved expressions are then inserted into the hardening strategy, completing the hardening for the bug instance. The hardening patches augment the AST, because the CPG nodes formulating the mitigations get inserted into the CPG in the same way as if they were constructed from the AST before.

H. Phase 5: Code Generation
HCC is designed as a source-to-source compiler. Thus, HCC generates the hardened source code as its last step in the compilation workflow. HCC traverses the hardened CPG, more precisely the hardened AST subgraph of the CPG, to generate the hardened source code. During this step, HCC takes into account the target compiler and compiler version suitable for the smart contract. The rewritten source code is then ready for further review or verification, compilation, and deployment.

IV. APPLICATION TO ETHEREUM
In this section, we describe how we apply HCC to Solidity smart contracts running on the Ethereum Virtual Machine (EVM). We choose Ethereum as the target environment since the security of Ethereum smart contracts is heavily researched (see Section II), revealing a number of different vulnerabilities in Ethereum smart contracts. As mentioned earlier, we focus on automatically discovering and mitigating integer bugs, such as integer overflows, and reentrancy vulnerabilities. By investigating these widely supported vulnerabilities, we can leverage the results of previous work as a ground truth [31,32,44,52] in the evaluation of our prototype (Section VI).

A. Creating CPGs from Solidity ASTs
Instead of implementing a Solidity parser from scratch, we opt to leverage a feature of the standard solc Solidity compiler [40]   that constructs and outputs the AST in a JSON format. 1 That is, we construct the CPG by calling the solc compiler and parsing its JSON-based AST output. We introduce labels and properties to the CPG that represent high-level constructs that are common in Solidity smart contracts, e.g., we mark the special fallback function with its own label. HCC also utilizes labels to differentiate between local variables that are only valid in a function's context and storage variables that belong to the contract's context and are ultimately stored inside the Ethereum blockchain. We also encode control-flow and dataflow related data into the CPG. In Solidity, the basic unit of control flow are statements, similar to other languages with C-like syntax.
To better illustrate this, consider the example of a smart contract written in Solidity shown in Listing 1. HCC constructs the CPG depicted in Figure 2 based on the AST of the Solidity code and performs several CPG enrichment steps, i.e., specific analyses (see Section IV-A). In this case, HCC detects a reentrancy vulnerability and inserts a reentrancy vulnerability node into the CPG, which is then taken up by the hardening strategy to insert patches into the CPG (see Section IV-B). To model control flows in the CPG, we insert control-flow edges between the individual statements of a function. HCC utilizes different types of edges to connect the statements. For example, the following two statements from Listing 1 are connected with a simple edge that indicates that the first statement is followed by the second: Other control flow constructs such as if statements or loops are more involved. In the case of if statements, we connect the if statement with two different types of edges to both branches. There is one edge in the CPG, which connects the if statement with the body that is executed when the condition is true and another type of edge that connects the if statement with the body that is executed when the condition is false. 1 Emitting the AST as JSON is available from solc version 0.4.12 and above. During CPG construction, we also collect and resolve data dependencies between identifiers and their references, e.g., the function, the variable, or the type declaration they refer to. For example, the balance identifier in line 19 of Listing 1 has an edge to the state variable balance, because it refers to that variable.
By relying on the information already contained in the basic CPG, HCC can conduct several analyses during the CPG enrichment phase: Call Analysis and Write State Analysis, which we will discuss in the remainder of this section. Note that the CPG enrichment steps are only necessary when analyzing for potential reentrancy vulnerabilities (see Section IV-B).
Call Analysis. When searching for reentrancy vulnerabilities, it is crucial to know the location of external function calls. Hence, HCC iterates over all function call expressions in the CPG and explicitly labels each external call. External calls are function calls that either target another contract, or statements that utilize the basic call functions that are associated with the address type of Solidity. HCC handles control-flow of external calls in a very specific way, i.e. HCC assumes that control may change to an arbitrary publicly callable function. This is an important measure to detect possible concurrent writes to the same storage location. Figure 3 shows an example for the analysis. HCC adds a label to the call expression ext.get() at , such that it can be easily identified as an external call. We also need to propagate this information to higher abstraction levels to simplify the bug patterns and hardening strategies. Every statement that contains a call expression that is labeled as an external call is labeled as contains an external call. As such, in the second analysis step, the return statement at is labeled as such. Similarly, every function is labeled to contain an external call if it contains such a statement that contains an external call. We continue to propagate the labels from the expressions up to the top-level functions in the call graph until no new labels are created. This ensures that when the fixed point of the analysis is reached all statements and functions that contain an external call are labeled accordingly. With reference to the example in Figure 3, this means that in the first analysis step the label is propagated from the expression to the statement . Then, it is propagated from the statement to the function , before undergoing another analysis iteration. Since the internal function call in the second function is now labeled to contain an external call, we now need to propagate this information also in the function f_public.  Figure 3: Example of a Call Analysis during CPG enrichment. The public Solidity function will be labeled as "contains external call". This essentially propagates the information backwards along the incoming edges of the CPG and labels all nodes along the way.
Similar to the first iteration, the labels are propagated from the internal call expression to the return statement , and finally to the function . Finally, the public function at is labeled to contain an external call. Note that Figure 3 only shows a simplified version of the CPG for the sake of clarity, while the complete CPG is depicted in Figure 5 in Appendix B.
Similar to the external call label we also need to identify delegate calls and constructor calls to cover the full space of reentrancy attacks [31]. The former is identified based on the same approach as external calls but assigned a different label. For the latter, we analyze Solidity's new expressions. Solidity utilizes the new keyword to instantiate both new inmemory data types such as structures or arrays, but also to create new contracts on the blockchain. As such, we need to identify constructor calls that create new contracts and label them accordingly. Note that creating new contracts by means of the new expression, requires the source code of the contract to be created. As such, the contract to be created is also contained in HCC's CPG.
Prior solutions for reentrancy detection treat constructor calls either as completely trusted or fully untrusted [31]. In contrast, HCC can precisely analyze whether a create-based reentrancy is possible since it analyzes the constructor code of the newly created contract. We assume that the constructor code itself is trusted, since it is part of the original source code file. When the constructor code performs an external call, we assume that the contract is potentially vulnerable to a create-based reentrancy attack. Write State Analysis. Reentrancy attacks involve reentering a contract after an external call before the state is updated. To effectively find statements that update the state, HCC follows a two-step approach. In the first analysis step, HCC collects all statements that change either state variables of a contract or a local variable that is stored in storage. We refer to those statements as state updates in the following. The collected statements and their surrounding functions are then also labeled as state updates and we add a new edge to the CPG, which represents the state update and directly connects the state update statement with the target state variable. In the second step of the write state analysis, HCC collects all internal function calls that update the contract's state. Those internal function calls and their surrounding functions also receive labels and edges just like the original state update statements above. Similar to the call analysis, we continue to propagate the edges and labels until no new labels were added to the CPG. In the end, the CPG contains edges that directly connect the functions and statements with the state variables they update.
Write state analysis is essential for detecting reentrancy attacks. When considering the Solidity code from Listing 1, there is one important state variable: balance. As such, the write state analysis inserts the dashed edges into the CPG at Figure 2, which connect the function node deposit, withdraw, and removeAccount with the state variable node for the balance variable.

B. Reentrancy Hardening
Reentrancy vulnerabilities enable attackers to reenter a smart contract in unexpected or unintended ways, that is, when the internal state of a smart contract is inconsistent. A basic form of malicious reentrancy is possible when the victim contract has a public function that contains an external call prior to a state update in the control flow. External calls are calls into other contracts allowing the callee contract to take over the control flow.
This subtle type of vulnerability class was responsible for the most devastating attacks on the Ethereum blockchain [16,32,52]. Several classes of reentrancy attacks have been studied in the past [31]: 1) same-function reentrancy, for which an attacker maliciously reenters a publicly callable function of the victim contract that operates on inconsistent state, 2) crossfunction reentrancy, where the inconsistent internal state is only exploitable in a sequence of publicly callable functions operating on the same state, 3) delegate-based reentrancy, which is similar to a same-function reentrancy in which either the external call or the state update can be replaced with a delegatecall and 4) create-based reentrancy, that happens when the victim contract creates a new contract by calling its constructor before updating the internal state. Note that reentrancy bugs are challenging to detect, since one has to reason about concurrent accesses to state variables in all functions of the smart contract.
HCC can detect and apply hardening for all four reentrancy patterns [6,31]. To do so, HCC utilizes a mutex to lock concurrent write access to variables. To efficiently lock with minimum gas costs, HCC implements a custom static analysis approach to determine the right set of variables that need to be locked. Listing 2 shows the hardened version of a Solidity smart contract, as it would be hardened by HCC. HCC introduces additional require statements to ensure that the mutex is unlocked when entering a function, and an additional code to lock the mutex before external calls. Note that in contrast to OpenZeppelin's library ReentrancyGuard [28] and other source-to-source compilers such as SGUARD [25], HCC applies fine-grained locks to storage variables instead of preventing all reentrant calls. Indeed, our analysis in appendix E shows that, otherwise, coarse-grained locking may prohibit certain legitimate behavior of real-world contracts. Instead, this aspect of HCC is similar to the EVM-level locking introduced by Sereum [31]. Sereum utilizes dynamic taint-tracking to determine at runtime which storage addresses should be locked and implements a locking mechanism to prevent write accesses on the EVM level. HCC introduces a mutex into the source code to lock reentrant access to those storage variables that the contract writes to after an external call. This prevents both further state updates during a reentrant call, but also prevents read access to the same storage variable. HCC leverages available type information to apply fine-grained reentrancy locks. This eliminates a number of false alarms that are present in prior solutions, such as SGUARD and Sereum. Since HCC locks individual variables and not storage addresses, HCC's locking code is unaffected when two variables share the same storage address. Furthermore, HCC can distinguish between storage deallocation in Solidity and setting a variable to zero. While this is indistinguishable on the EVM-level, these are semantically different statements in Solidity and as such, HCC treats them differently. Reentrancy Analysis. The detection of reentrancy patterns consists of 2 main steps. First, HCC queries the CPG for a state update. Although the updated variable is always stored in the storage of the contract, it can either be a local variable stored in storage or a state variable of the contract. Second, HCC searches for an external call that precedes it either directly or transitively. If HCC detects such a pair of state updates and external calls, the query captures the following information: (1) the assignment statement, (2) the manipulated variable, (3) the external call, (4) the function in which the external call and the state update happen, and (5) the contract this function belongs to. If the manipulated variable is a state variable of the contract, HCC optionally queries the CPG for all functions that belong to the same contract and that also update the same state variable. This approach detects same-function reentrancy as well as cross-function reentrancy.
However, in delegate-based reentrancy, either the external call or the state update can be replaced by a delegate call. Hence, there are actually two variants of delegate-based reentrancy. In the first case, the delegate call precedes a state update, i.e., the delegate call assumes the role of the external call. Since HCC models the delegate call as a special case of an external call in the CPG, this variant is handled by our existing detection approach. In the second case, however, the delegate call assumes the role of the state update. Moreover, we cannot determine which variable may be written to during the delegate call, i.e., we must conservatively assume that any variable can be written. Here, HCC queries the CPG for external calls that are followed by a delegate call in the control flow of the smart contract. In this case, HCC marks the delegate call as a state update to an unknown state variable.
The detection of create-based reentrancy in HCC works similar to the detection of same-function and cross-function reentrancy. However, instead of external calls, the create-based reentrancy detection searches for constructor calls which are followed by a state update. The CPG-based analysis allows HCC to also analyze the constructor that is being called, even though this constructor is part of another contract. In this way, HCC will only report a vulnerability if the callee constructor contains an external call. If the constructor calls an external function, HCC collects the same data as for same-function reentrancy and also conducts the cross-function analysis.
Identifying a potential vulnerability is implemented as a query on the CPG, which also returns CPG nodes as a result. HCC then inserts a vulnerability node into the CPG and labels it according to the vulnerability type, e.g., create-based reentrancy. Finally, HCC inserts different edges between the vulnerability node and the nodes returned from the detection queries. These edges show which role a node fulfills in the detected bug pattern and are later used by the hardening strategies. Reentrancy Hardening. In spite of the various reentrancy patterns, we resort to only one major hardening strategy: inserting mutex locks for state variables. We first describe the general hardening strategy for reentrancy and then discuss a special case of delegated reentrancy attacks, which require a slightly different hardening strategy. Recall that the reentrancy pattern consists of a state variable sv, a statement s w that writes to this state variable, an external function call c e , and the set of functions f c , which contain statements that write to the state variable sv. This captures both same-and cross-function reentrancy attacks.
First, HCC ensures that there is a lock state variable (l sv ) for the state variable sv, which depends on the type of sv itself. For basic types like uint256, l sv will have type bool. For mapping types, HCC utilizes a mapping from the same key type as sv to the lock type bool. As a result, HCC applies locks to individual entries of the mapping and not on all mapping entries at the same time. This is especially important for common smart contract patterns, where mappings are used to store per-account information. This also means that a reentrant call could still modify unrelated account information, which is required to support legitimate behavior of real-world contracts. In Appendix E we provide an example demonstrating the advantage of this fine-grained locking mechanism.
The lock variable l sv is then set right before the external call and released right after the external call. HCC then inserts the locking statement lock lsv , which sets the value to true, right before the external call c e . The unlocking statement unlock lsv , which sets the value to false, is inserted immediately after c e . The control flow of f is updated such that the locking statement, the external call and the unlocking statement directly follow each other: lock lsv → c e → unlock lsv . HCC then creates a guard code that checks for l sv to be unlocked and inserts it at the beginning of all functions in the set f , i.e., all functions that write to the state variable sv.
As shown in Listing Listing 2, the external call c e is part of the removeAccount function at line 28. The lock and unlock statement were inserted before and after in line 27 and 29, respectively. Additionally, one can see that every function now starts with a lock guard, in the form of a require statement that ensure the lock is not set.
Mitigating the second variant of delegated reentrancy requires a slightly different hardening strategy with course-grained locking strategy. To this end, HCC creates one main lock as a state variable that is used to lock all state variables at the same time. HCC subsequently surrounds the external call c e as well as the delegate call c d with the locking and unlocking statements for this main lock. Guards for the main lock are finally inserted at the beginning of every publicly callable function. This main lock is only ever created and checked when hardening against this edge case of delegated reentrancy, and in all other cases we apply the fine-grained locking strategy described above. Storage Layout Compatibility. Introducing new storage variables allows HCC to produce clean hardening code. However, this approach to introducing locks breaks the storage layout compatibility with the original contract. As a predeployment hardening solution this is usually not a problem. However, it makes HCC incompatible with existing schemes to achieve upgradable contracts, such as the delegatecall-proxy pattern [32]. Furthermore, changes to the storage layout might break our approach to evaluating the effectiveness of HCC's hardening (see Section VI). As such, we introduce another hardening mode that preserves the original storage layout by utilizing low-level inline EVM instructions to compute the storage addresses of the locks instead. Appendix B in Appendix B shows an example for this hardening mode.

C. Integer Bugs
Integer bugs occur when the result of an arithmetic operation grows either greater (integer overflow) or lower (integer underflow) than the size of the target type allows [39]. There is also an integer bug that is not introduced through arithmetic operations, the integer truncation bug [8]. This bug can occur when an integer value of a certain size is converted into an integer value with a lower size, e.g., when a uint16 is typecasted to uint8. Our prototype implementation of HCC hardens against all of these integer bugs.
Note that while manually patching integer bugs is straightforward as libraries for safe integer arithmetic are readily available, smart contracts typically involve a large number of integer arithmetic, which makes it a tedious task. Consider the BEC Token contract which was actively exploited [29] due to an integer overflow bug even though the contract already utilized a library for safe integer arithmetic; the developers however missed a vulnerable multiplication. We provide a detailed investigation of HCC's effectiveness against this exploit in Section VI-B. Integer Bug Analysis. To detect arithmetic bugs, HCC first queries the CPG for all nodes that represent an arithmetic expression. This includes the following 5 standard operations: addition, subtraction, multiplication, exponentiation and division. 2 HCC collects the nodes representing the arithmetic operations, and also recursively handles the left-hand and righthand side expressions of each arithmetic operation. Additionally, HCC also collects the statement containing the integer operation as the default mitigation point.
To detect truncation bugs, HCC queries the CPG for type conversions where the target type is any integer type, and collects the target type, the expression to be converted and the statement containing the type conversion. By treating essentially all operations as potentially vulnerable, HCC ensures that the hardened smart contract does not exhibit any integer bugs at runtime. For each discovered integer operation, we insert a new node into the CPG representing the potential vulnerability. Depending on the type of the statement, HCC inserts the integer overflow check at a different location. For example, integer operations in loop expressions have to be checked in every iteration of the loop. As the involved variable may be manipulated inside the loop body, HCC inserts the integer overflow check after the last statement inside the loop body. On the other hand, for normal assignment statements, HCC inserts the integer overflow check right in front the original statement. Integer Bug Hardening. Since arithmetic operations can be chained, when mitigating bugs in arithmetic expressions, HCC first hardens the current expression and then hardens the left and right expression recursively until it reaches a node representing a variable or constant. HCC inserts an assert statement containing a check on the arithmetic operations. The assert will trigger a transaction rollback in Ethereum,  leading to a rollback of any state update or ether transfer that happened before the integer bug was triggered, fully mitigating any impact of the overflow. The inserted assert contains integer arithmetic check for all integer operator expressions, which are captured in the statement. For each expression, HCC adds a check and chains the checks with logical operators. Consider the statement r = (a + b) * c. Here HCC inserts an overflow check for both a + b and for the full expression (a + b) * c. HCC inserts integer arithmetic checks that follow straightforward rules, as are also employed in other programming languages and also by the SafeMath solidity library [27]. For example, consider the assignment c = a + b, where HCC would mitigate a potential overflow by inserting an assert(c ≥ a) after the assignment. For expressions that are not part of an assignment, HCC inserts assert statements like assert(a + b ≥ a). HCC also handles signed integer arithmetic, where the required checks are a bit more involved. Underflows due to subtraction are mitigated exactly as the overflows of the addition, with the opposite comparison operator. Similarly, HCC also handles the unary increment and decrement operators and the multiplication operator. Line 5-6 of listing 3 show an example of an multiplication overflow check that is inserted by HCC into the BEC Token contract. Truncation bugs are avoided by verifying that the value of the cast to the target type and back to the original type is the same as the original value. For example, when casting the variable a with type uint16 to uint8, HCC would insert the check assert(a == uint16(uint8(a))), ensuring no information is lost. Due to Solidity's limitation, there is no easy way to perform a proper overflow verification for the exponent operation, as the inverse operation (log) is not available in the language. This is however not an issue, as in most contracts, exponentiation is used only with a hard coded base and exponent, and is therefore expected to behave similarly regardless of the input parameters of the function. More details on the integer arithmetic checks can be found in Appendix A.

V. APPLICATION TO HYPERLEDGER FABRIC
In what follows, we describe our integration and evaluation of HCC with Hyperledger Fabric [2]. Fabric is one of the most popular/adopted permissioned blockchain platform, is written in Golang, and mainly supports Golang-based smart contracts.
In order to apply HCC to Hyperledger Fabric, we modified three of the phases as shown in fig. 4. Here, the phases with a dashed border are phases that are platform specific: namely the compiler to AST model, the CPG enrichment, and the code generation phase. Since not all compilers generate an AST in a machine-readable format, and in order to reduce the  Figure 4: Integrating HCC with Hyperledger Fabric. The phases in dashed are phases that are platform and language specific. differences in ASTs generated by language specific compilers (such as the Golang compiler for Golang code), we make use of a generic parser generator such as ANTLR [3]. ANTLR has grammars targeted at many existing programming languages, such as Golang, and can be used to generate the AST model of smart contracts written in any of the supported languages.
Since ANLTR provides a uniform parsing behavior across all the supported languages, it ensures minimal modifications to the CPG enrichment phase. The code generation phase requires an adaptation to ensure that the emitted code is valid in the considered programming language; Golang in the case of Fabric. Since the logical operator are usually the same across languages, we only need to change the error reporting mechanism. This requires changing the assert (condition) used in Solidity, to the standard "if not condition, return error" of Golang as follows: if(!condition) { return nil, errors.New("HCC: Illegal Operation") } Finally, the CPG enrichment phase also requires some changes to properly capture the specificities of the platform. This typically includes information on the API of Fabric smart contracts, such as access storage patterns (Fabric's 'PutState' and 'GetState' methods) and external call pattern (Fabric's InvokeChaincode method).
While it is clear that reentrancy attacks detailed in Section IV are specific to Ethereum, other types of attacks, such as integer overflows and underflows, are directly applicable to Fabric's smart contracts. After modifying the three phases as described before, the bug discovery and hardening phases can be reused as is in order to mitigate potential integer overflows and underflows of Fabric, without any modification of the detection and hardening strategies. Due to the type inference of Golang that does not always provide information on the type of a variable, it is not always easy to differentiate between signed and unsigned integers. For this reason, when HCC cannot ensure with certainty the type, it always assumes the type is signed as it creates additional check compared to the unsigned version.
We evaluated our integration of HCC with Fabric using the five ERC-20 token contracts from the Osiris [44] dataset. To do so, we translated the vulnerable function from Solidity to Golang and then applied HCC on the resulting smart contract. We then measured the overhead introduced (in LOC and execution time) in each function by HCC. Our results are shown in table I; here each point is averaged over 1000 independent runs. Our results show that HCC introduces an average code overhead of 31% as shown in table I; this corresponds to adding 3 lines required for each assert introduced by HCC. On the other hand, HCC introduces an average of 23.5% in terms of execution times (around 76 nanoseconds). Notice that this overhead varies between BEC and the other considered contracts. This is mainly due to the fact that the BEC contract has only one patch and therefore introduces little overhead. Recall that the Fabric framework requires few seconds to process a transaction-as such, an overhead of hundreds of nanoseconds can be easily tolerated.
Beyond Hyperledger Fabric, we argue that the reliance on ANTLR ensures support for many smart contract based languages since ANTLR has grammars targeted at many popular programming languages, such as Java, C++, C#, Python, etc. This also means that HCC can also a wide variety of blockchain platform such as Hyperledger Sawtooth (Python) [33] and Neo (Java, Python, C#) [24] without incurring changes in the AST generation step.

VI. EVALUATION
We evaluate the application of HCC to Solidity smart contracts according to two main aspects: (1) functional correctness of the compiler and (2) effectiveness of the introduced security checks.
Methodology. Similar to prior work [32], we utilize an extensive differential testing approach to evaluate functional correctness and hardening effectiveness of HCC on a set of important real-world contracts. We use a modified Ethereum node based on go-ethereum to trace the contracts transactions using both the original and the hardened bytecode. Next, we compare the execution traces of the original transaction and the transaction re-executed with the code hardened by HCC. When assessing functional correctness, we ensure that both transaction traces contain the same state updates (i.e., storage writes), ether transfers, and external calls (i.e., any interaction with the blockchain state) in the same order. We only re-execute transactions targeting the smart contract which were successful on the original unhardened contract code. This is due to the fact that (1) we cannot determine functional correctness of already failing transactions, and (2) HCC aims at not changing the functional behavior, hence it does not affect the conditions that lead to the failure of transactions in the original contract. We evaluate HCC on contracts that fulfill the following criteria: (1) the account is a contract with source code available (2) the contract is programmed in Solidity, (3) the source code is compatible to a solc version of 0.4.12 or above and (4) consists of a single source file, and (5) the original source code can be compiled by solc without errors (and also without the stack too deep issue in the Solidity compiler when adding new local variables [46]). Note that these are not inherent limitations of HCC, as it would require considerable engineering effort to support every minor Solidity version in our implementation setup.

A. Functional Correctness
We start by evaluating the functional correctness on a set of top accounts sorted by value from etherscan. That is, we collected the top 25 high-value accounts with balances ranging from 20,000 to 7.2 million Ether at the time of our evaluation that fulfill the above criteria. We first harden the contracts, with our integer bug and reentrancy hardening strategies enabled, and compile the hardened contracts to bytecode using the standard Solidity compiler. We then apply the aforementioned differential testing methodology to determine whether all tested transactions work as intended, to assess functional correctness, and to measure the gas overhead introduced by HCC's hardening patches.
The results of our functional evaluation are depicted in Table II. Replaying 49,272 transactions required one week execution, and only 7 failed on the hardened contracts. This means that hardening with HCC achieves a functional correctness of 99.99 %. However, all of those 7 transactions that failed were deployment transactions and the deployment of smart contracts during re-execution is expected to fail, since due to the reexecution implementation the smart contract already exist at the addresses in question.
Notice that few contracts in Table II have a negative value in the minimum gas overhead. This is because even slightly different versions of the compiler and parameters between the original smart contract and the hardened smart contract can lead to lower gas cost through compiler optimization. In a separate experiment, we measured the effect of using different compiler versions for patched and unpatched contracts. Our results show that the gas overhead deviates only minimally (between −0.24 % and 0.52 %) when measurements are done across different compiler versions-which also explains the negative values in Table II. Indeed, using different versions of the compiler before and after hardening results in those cases in lower gas cost due to compiler optimization.
We note that all contracts with a potential reentrancy vulnerability are variants and extensions of multi-signature wallet contracts [13]. As such, they share large parts of their code and exhibit a similar number of reentrancy and integer bug mitigations. The gas overhead of RE mitigation is typically higher than for IO due to the storage writes, which incur a higher gas cost compared to the assert statement used for integer bug hardening. Nonetheless, this overhead is largely tolerable. For instance, contract 0x2265. . . 1e41, with a mean and maximum gas increase of 35,149 and 99,916 respectively, yields a mean increase of 0.0012 Ether and maximum increase of 0.0034 Ether (using an average of 34 GWei per gas).
Our results show that IO mitigation results in a relatively low overhead ranging from 0.14 % and up to 10.8 % with an average of 2.82 %. For special cases where the overhead incurred is nontrivial, such as contracts 0xa646. . . 0a2b and 0x0000. . . 05fa, additional care can be taken by the developers to remove superfluous runtime checks that degrade the performance and increase the costs.

B. Hardening Effectiveness
To evaluate the hardening effectiveness of HCC, we leverage existing datasets that contain smart contracts with known vulnerabilities [31,44,52] and hardening the respective contracts with HCC. To ensure that the hardening strategies for integer bugs do not interfere with those for reentrancy, we evaluate the effectiveness of the two hardening mechanisms separately. We therefore construct two distinct datasets, one for each vulnerability class, and apply the differential testing approach described above: for all transactions, we trace and compare the transaction with both the original vulnerable contract and the HCC-hardened contract to validate whether the hardening introduced by HCC prevents attack transactions. Integer Bug Hardening. We evaluate the effectiveness of integer bug hardening on the five ERC-20 token contracts from the Osiris [44] dataset, which all have confirmed vulnerabilities that have been actively exploited [32]. We harden the five contracts in our dataset against integer bugs with HCC and compile the hardened source code to bytecode using the standard Solidity compiler. We replay and re-execute a total of 31,520 transactions, of which 68 transactions are confirmed attacks. Table III shows that HCC effectively prevents all confirmed attack transactions-with a hardening effectiveness of 100 % for integer bugs. Moreover, HCC does not hamper the legitimate behavior of the studied contracts, i.e., all the benign transactions do not raise an alarm in HCC. Overall, HCC introduces an average runtime gas overhead of 1.64 %.
We now describe in detail the token contract belonging to the BeautyChain (BEC) token to demonstrate how HCC hardens integer bugs. We select the BEC contract because it was actively exploited resulting in a loss of 10 58 BEC tokens [29]. While the BEC contract is compatible to the ERC-20 standard for token transfers, it also implements custom functionality, such as the ability to transfer an amount of tokens to multiple receivers in its batchTransfer function. Listing 3 shows the function that implements this behavior for the BEC token contract. Note that the highlighted lines (5 and 6) are added only after hardening through HCC. The batchTransfer function takes an array of addresses (receivers) and an integer as its input (_value). All addresses in the receivers array will receive an amount of tokens equal to _value. Interestingly, the developers were aware of integer bugs as they use the sub and add functions of the SafeMath library [27] in this function. However, the batchTransfer function is vulnerable to an integer overflow due to a missing check before the multiplication in line 7, demonstrating that just using SafeMath does not ensure full hardening against integer overflows. Since both multiplication operands, i.e., the length of the array and _value, are inputs to this publicly callable function, an attacker can construct an exploit transaction by providing selected values for the parameters that trigger the multiplication overflow in line 7. Consequently, the amount variable overflows to a very low value or even to zero, which bypasses the check in line 9. Listing 3 shows the source code of the batchTransfer function after hardening with HCC. Due to the inserted overflow check in lines 5 and 6, the multiplication overflow becomes unexploitable.
We further analyze the effectiveness of HCC's integer bug hardening by extending our dataset to all contracts of the original OSIRIS dataset [44]. 134 vulnerable contracts were deemed suitable for our experiment based on our criteria list described in Section VI. To accurately perform this experiment, we need to ensure that OSIRIS does not report integer vulnerabilities for vulnerable code that is protected by HCC's inserted hardening checks. Recall that our implementation for integer vulnerabilities (see Section IV-C) does not remove the vulnerability but rather inserts runtime checks that prevent exploitation of the vulnerability. In Appendix C, we demonstrate how we carefully sort out OSIRIS alarms for non-exploitable vulnerabilities. Our evaluation results show that none of hardened contracts raised an alarm-thereby demonstrating the effectiveness of automated integer bug hardening with HCC. Reentrancy Hardening. For this experiment, we selected 5 contracts from the Sereum dataset [31] where the following conditions are met: (a) source code is available, (b) the contract uses a HCC-compatible solidity version and (c) we manually identified at least one true attack transaction, i.e., the contract is not a Sereum [31] false alarm. Additionally, we included all contracts susceptible to reentrancy from the recent Ever-Evolving Game [52] dataset. We hardened the contracts against reentrancy bugs with HCC and compile the hardened source code to bytecode using the standard Solidity compiler. We replayed and re-executed a total of 109 transactions, including 54 confirmed attack transactions. HCC's hardening introduces an average runtime gas overhead of 11.95 %. As can be seen in Table III, HCC effectively prevents all confirmed attack transactions, thereby achieving 100 % patch effectiveness for reentrency hardening, while all the benign transactions do not raise an alarm in HCC. In contrast to the EEG dataset, we discovered two new reentrant transactions for the contracts 0x85d2. . . ea0b and 0x516d. . . 29a9. We manually analyzed the two new transactions of these contracts and confirmed that they are indeed reentrant calls correctly prevented by HCC.

C. Practicality
We compared HCC coverage and effectiveness to the recently proposed SGUARD state-of-the-art source-to-source compiler   to demonstrate the high practicality of HCC. To this end, we conducted a large-scale experiment on a set 10,000 contracts sampled from etherscan.io without any filtering using a timeout of one minute per contract and 64GB memory budget. Note that a timeout of one minute is sufficient even for complex real-world contracts, as the compilation times in Tables II  and III show. HCC successfully processes 7174 (71.74%), while SGUARD processes only 3286 (32.86%).
One might think that a timeout of one minute is too short for symbolic execution-based approaches. Hence, we additionally compiled the 15 contracts containing known integer overflow and reentrancy vulnerabilities as well as the Top-25 ERC20 contracts with SGUARD using a very high timeout threshold of 12 hours per contract. Note that setting a timeout of 12 hours for all 10,000 contracts is not an option as it would require months to complete the experiment. Even with this excessive timeout threshold, only 17 of the 40 contracts are compiled by SGUARD, whereas HCC compiles 36 out of 40 contracts. In particular, only 4 of the Top-25 contracts are successfully processed by SGUARD, while HCC is able to successfully compile 20 contracts. In addition, SGUARD does not add any patch to any contract of our integer bug dataset including the real-world ERC20 Token contracts that were actively exploited in 2018. Further, we noticed a minor implementation error in SGUARD that prevented hardening of two known reentrancy vulnerabilities since SGUARD only considers the last contract declared in a Solidity source file. By reordering the contracts in the source file, the reentrancy vulnerabilities could be patched with SGUARD. We also observed that existing approaches hamper legitimate transactions (false positive) when checking for reentrancy vulnerabilities. For instance, SGUARD applies a locking mechanism similar to OpenZeppelin's ReentrancyGuard [28], i.e., it creates a single mutex-lock for the whole contract. However, this locking strategy prevents legitimate access patterns, like withdrawals from different accounts within the same transaction that cannot lead to state-inconsistency. In contrast, HCC's locking strategy is field-and offset-sensitive as it considers both, fields of data structures and indices to mappings without limits to nesting, thereby allowing contracts In Appendix E, we include a detailed code example showcasing this issue.

VII. CONCLUSION
While the popularity and adoption of blockchain technologies steadily increase, little attention is paid for developing secure contracts to implement the various blockchain use-cases. Instead of requiring developers to strictly follow security guidelines and fixing (potentially) vulnerable code on their own based on static analysis and symbolic execution tools, novel approaches need to be developed that take the burden from developers. HCC is the first practical compiler that achieves this by enforcing both automated detection and fixing of vulnerabilities in a transparent manner allowing developers to inspect and backtrace HCC patches. Given the infancy of blockchain technology and the continuous changes in this domain, it is vital to support many languages and platforms, which HCC accurately addresses by introducing a generic smart contract representation, namely a code-property graph. For our prototype implementation, we demonstrate that HCC detects and fixes all known integer overflow and reentrancy bugs, and is applicable to complex and high-value state-of-the-art contracts. Figure 5 depicts the complete CPG, which corresponds to the source code example in Figure 3. Note that in contrast to the original figure, the complete CPG contains several nodes, which we omitted for the sake of simplicity in Figure 3. In addition to the nodes in Figure 3, Figure 5 also contains CPG nodes that represent the contracts and also edges, which represent references to state variables and functions.

D. Contract Addresses
We list the full address for each of the contracts from Tables II  and III in Table IV.

E. Full Contract Locks and Legitimate Behavior
In section VI-C, we note that SGUARD's locking strategy hampers legitimate contract access patterns, e.g., withdrawals from different accounts within the same transaction. We now include a detailed code example showcasing this issue. Consider the contract in Listing 5. The function withdrawTo is susceptible to a reentrancy attack if msg.sender and the parameter to are equal and a contract exists at this address. The contract on the address to can then reenter into the withdrawTo function and carry our a simple reentrancy attack, leveraging the inconsistent state of the balance state variable. Such an attack is not possible when msg.sender and the address to are different: even though a contract deployed at address to can reenter this function, the state variable balance remains consistent, because its access offset is different-therefore, this access is legitimate, as it accesses the balances of different accounts, which cannot lead to state inconsistency. SGUARD's locking strategy works by adding a contract to the source file that contains the nonReentrant-modifier shown in Listing 6. The hardened contract inherits from the sGuard contract, and SGUARD adds the modifier to each function that is susceptible to reentrancy.
Since this modifier only uses a single mutex-lock variable for the whole contract, SGUARD prevents the legitimate access pattern described above, changing the functionality of the hardened contract. In contrast, HCC's locking strategy locks the critical balance variable while taking the access offset into account, as we explain in Section IV-B.