AoC 2022 Day 8: Introduction to Blockchain and Smart Contracts
What is a Blockchain?
The blockchain and its effects on contemporary computing are among the most recent innovations and technologies to be addressed. Although historically it has been employed as a financial technology, it has recently been incorporated into numerous other fields and uses. Informally, a blockchain serves as a database to store data in a certain format that is shared across network participants with no central authority.
A blockchain is a distributed digital database or ledger that is shared among network nodes that operate on a peer-to-peer basis. Decentralized means no central servers are involved; the blockchain is dispersed across “peers,” or participants and each peer is responsible for preserving the blockchain’s integrity. If a network participant made a malicious attempt to alter a blockchain, other members would compare it to their blockchain for integrity and determine if the whole network should express that change.
The core blockchain technology aims to be decentralized and maintain integrity; cryptography is employed to negotiate transactions and provide utility to the blockchain.
But what does this mean for security? If we ignore the core blockchain technology itself, which relies on cryptography, and instead focus on how data is transferred and negotiated, we may find the answer concerning.
Introduction to Smart Contracts
The vast majority of blockchain’s real-world uses rely on a technology called smart contracts. To sustain a cryptocurrency on a blockchain, smart contracts are most frequently utilized as the foundation of DeFi applications (Decentralized Finance applications). DeFi applications enable the exchange of money between entities; the specifics of the exchange are defined by a smart contract. A smart contract is a piece of software that is kept on a blockchain and activates when certain criteria are met.
Smart contracts are pretty similar to any other scripting-language-based program. To make the creation of contracts easier, several languages have been developed, including Solidity, Vyper, and Yul. Even conventional programming languages like JavaScript and Rust can be used to create smart contracts; at their heart, smart contracts wait for conditions to occur and execute actions, similar to traditional logic.
The functionality of a Smart Contract
Building a smart contract may seem intimidating, but it greatly contrasts with core object-oriented programming concepts.
Let’s pretend a contract was a class before delving further into its capabilities. You might want specific fields to be private, restricting access or modification until certain requirements are met, depending on the fields or data stored in a class. Fields or data in a smart contract should be kept private and only accessible through contract-defined methods. A contract frequently performs several actions that behave similarly to accessors and mutators, like monitoring the balance and making deposits and withdrawals of money.
Once a contract is deployed on a blockchain, another contract can then use its functions to call or execute the functions we just defined above.
If we controlled Contract A and Contract B wanted to first deposit 1 Ethereum, and then withdraw 1 Ethereum from Contract A,
Contract B calls the deposit function of Contract A.
Contract A authorizes the deposit after checking if any pre-determined conditions need to be met.
Contract B calls the withdraw function of Contract A.
Contract A authorizes the deposit if the pre-determined conditions for withdrawal are met.
Contract B can execute other functions after the Ether is sent from Contract A but before the function resolves.
How do Vulnerabilities in Smart Contracts Occur?
Most smart contract vulnerabilities arise due to logic issues or poor exception handling. Most vulnerabilities arise in functions when conditions are insecurely implemented through the previously mentioned issues.
Let’s take a step back to Contract A in the previous section. The conditions of the withdraw function are,
- Balance is greater than zero
- Send Etherium
This may seem okay at first glance, but when will the sum be sent be deducted from the account balance? In keeping with the contract diagram, it is never taken out of the account until the Etherium is sent. Is this a problem? The function shall be complete before processing any additional functions under the contract. However, if you remember, a contract can call a function repeatedly while an older function is still running. The pre-defined conditions will always be satisfied because an attacker can repeatedly try to call the withdraw function before it clears the balance. Before making another call or imposing stricter conditions, a developer must change the function’s logic to remove the balance.
The Re-entrancy Attack
In the previous section, we introduced the term “re-entrancy”— a widespread vulnerability. Reiterating what was said earlier, re-entrancy happens when an evil contract, after using a withdraw function first, employs a fallback mechanism to keep dwindling the contract’s total amount.
We have broken up the attack into diagrams similar to those previously seen to explain this better.
Assuming that contract B is the attacking contract and contract A is the storage contract. Contract B will execute a function to deposit, then withdraw at least one Ether. This process is required to initiate the withdraw function’s response.
Note the difference between account balance and total balance in the above diagram. The storage contract separates balances per account and keeps each account’s total balance combined. We are specifically targeting the total balance we do not own to exploit the contract.
At this point, contract B can either drop the response from the withdraw function or invoke a fallback function. A fallback function is a reserved function in Solidity that can appear once in a single contract. The function is executed when currency is sent with no other context or data, for example, what is happening in this example. The fallback function calls the withdraw function again, while the original call to the function was never fully resolved. Remember, the balance is never set back to zero, and the contract thinks of Ether as its total balance when sending it, not divided into each account, so it will continue to send currency beyond the account’s balance until the total balance is zero.
Because the withdraw function sends Ether with no context or data, the fallback function will be called again, and thus an infinite loop can now occur.