Skip to content

Commit d6f19be

Browse files
committed
Fixed a few minor spelling mistakes throughout - though note about solidity 0.5.0 needs to be reviewed before this is ready for merge.
1 parent b2a460d commit d6f19be

File tree

1 file changed

+7
-7
lines changed

1 file changed

+7
-7
lines changed

README.md

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -628,7 +628,7 @@ contract FibonacciBalance {
628628
```
629629
This contract allows a participant to withdraw ether from the contract, with the amount of ether being equal to the Fibonacci number corresponding to the participants withdrawal order; i.e., the first participant gets 1 ether, the second also gets 1, the third gets 2, the forth gets 3, the fifth 5 and so on (until the balance of the contract is less than the Fibonacci number being withdrawn).
630630

631-
There are a number of elements in this contract that may require some explanation. Firstly, there is an interesting-looking variable, `fibSig`. This holds the first 4 bytes of the Keccak (SHA-3) hash of the string "setFibonacci(uint256)". This is known as the [function selector](https://solidity.readthedocs.io/en/latest/abi-spec.html#function-selector) and is put into `calldata` to specify which function of a smart contract will be called. It is used in the `delegatecall` function on line \[21\] to specify that we wish to run the `fibonacci(uint256)` function. The second argument in `delegatecall` is the parameter we are passing to the function. Secondly, we assume that the address for the `FibonacciLib` library is correctly referenced in the constructor (section [External Contract Referencing](#contract-reference) discuss some potential vulnerabilities relating to this kind if contract reference initialisation).
631+
There are a number of elements in this contract that may require some explanation. Firstly, there is an interesting-looking variable, `fibSig`. This holds the first 4 bytes of the Keccak (SHA-3) hash of the string "setFibonacci(uint256)". This is known as the [function selector](https://solidity.readthedocs.io/en/latest/abi-spec.html#function-selector) and is put into `calldata` to specify which function of a smart contract will be called. It is used in the `delegatecall` function on line \[21\] to specify that we wish to run the `setFibonacci(uint256)` function. The second argument in `delegatecall` is the parameter we are passing to the function. Secondly, we assume that the address for the `FibonacciLib` library is correctly referenced in the constructor (section [External Contract Referencing](#contract-reference) discuss some potential vulnerabilities relating to this kind if contract reference initialisation).
632632

633633
Can you spot any error(s) in this contract? If you put this into remix, fill it with ether and call `withdraw()`, it will likely revert.
634634

@@ -663,7 +663,7 @@ It is also important to notice that when we say that `delegatecall` is state-pre
663663

664664
<h3 id="dc-prevention">Preventative Techniques</h3>
665665

666-
Solidity provides the `library` keyword for implementing library contracts (see the [Solidity Docs](http://solidity.readthedocs.io/en/latest/contracts.html?highlight=library#libraries) for further details). This ensures the library contract is stateless and non-self-destructable. Forcing libraries to be stateless mitigates the complexities of storage context demonstrated in this section. Stateless libraries also prevent attacks whereby attackers modify the state of the library directly in order to effect the contracts that depend on the library's code.
666+
Solidity provides the `library` keyword for implementing library contracts (see the [Solidity Docs](http://solidity.readthedocs.io/en/latest/contracts.html?highlight=library#libraries) for further details). This ensures the library contract is stateless and non-self-destructable. Forcing libraries to be stateless mitigates the complexities of storage context demonstrated in this section. Stateless libraries also prevent attacks whereby attackers modify the state of the library directly in order to affect the contracts that depend on the library's code.
667667
As a general rule of thumb, when using `DELEGATECALL` pay careful attention to the possible calling context of both the library contract and the calling contract, and whenever possible, build state-less libraries.
668668

669669
<h3 id="dc-example">Real-World Example: Parity Multisig Wallet (Second Hack)</h3>
@@ -1081,7 +1081,7 @@ function transfer(address to, uint tokens) public returns (bool success);
10811081
```
10821082
Now consider, an exchange, holding a large amount of a token (let's say `REP`) and a user wishes to withdraw their share of 100 tokens. The user would submit their address, `0xdeaddeaddeaddeaddeaddeaddeaddeaddeaddead` and the number of tokens, `100`. The exchange would encode these parameters in the order specified by the `transfer()` function, i.e. `address` then `tokens`. The encoded result would be `a9059cbb000000000000000000000000deaddeaddeaddeaddeaddeaddeaddeaddeaddead0000000000000` `000000000000000000000000000000000056bc75e2d63100000`. The first four bytes (`a9059cbb`) are the `transfer()` [function signature/selector](https://solidity.readthedocs.io/en/latest/abi-spec.html#function-selector), the second 32 bytes are the address, followed by the final 32 bytes which represent the `uint256` number of tokens. Notice that the hex `56bc75e2d63100000` at the end corresponds to 100 tokens (with 18 decimal places, as specified by the `REP` token contract).
10831083

1084-
Ok, so now lets look at what happens if we were to send an address that was missing 1 byte (2 hex digits). Specifically, let's say an attacker sends `0xdeaddeaddeaddeaddeaddeaddeaddeaddeadde`as an address (missing the last two digits) and the same `100` tokens to withdraw. If the exchange doesn't validate this input, it would get encoded as `a9059cbb000000000000000000000000deaddeaddeaddeaddeaddeaddeaddeaddeadde00000000000000` `00000000000000000000000000000000056bc75e2d6310000000`. The difference is subtle. Note that `00` has been padded to the end of the encoding, to make up for the short address that was sent. When this gets sent to the smart contract, the `address` parameters will read as `0xdeaddeaddeaddeaddeaddeaddeaddeaddeadde00` and the value will be read as `56bc75e2d6310000000` (notice the two extra `0`'s). This value is now, `25600` tokens (the value has been multiplied by `256`). In this example, if the exchange held this many tokens, the user would withdraw `25600` tokens (whilst the exchange thinks the user is only withdrawing `100`) to the modified address. Obviously the attacker wont possess the modified address in this example, but if the attacker were to generate any address which ended in `0`'s (which can be easily brute forced) and used this generated address, they could easily steal tokens from the unsuspecting exchange.
1084+
Ok, so now lets look at what happens if we were to send an address that was missing 1 byte (2 hex digits). Specifically, let's say an attacker sends `0xdeaddeaddeaddeaddeaddeaddeaddeaddeadde`as an address (missing the last two digits) and the same `100` tokens to withdraw. If the exchange doesn't validate this input, it would get encoded as `a9059cbb000000000000000000000000deaddeaddeaddeaddeaddeaddeaddeaddeadde00000000000000` `00000000000000000000000000000000056bc75e2d6310000000`. The difference is subtle. Note that `00` has been padded to the end of the encoding, to make up for the short address that was sent. When this gets sent to the smart contract, the `address` parameters will read as `0xdeaddeaddeaddeaddeaddeaddeaddeaddeadde00` and the value will be read as `56bc75e2d6310000000` (notice the two extra `0`'s). This value is now, `25600` tokens (the value has been multiplied by `256`). In this example, if the exchange held this many tokens, the user would withdraw `25600` tokens (whilst the exchange thinks the user is only withdrawing `100`) to the modified address. Obviously the attacker won't possess the modified address in this example, but if the attacker were to generate any address which ended in `0`'s (which can be easily brute forced) and used this generated address, they could easily steal tokens from the unsuspecting exchange.
10851085

10861086
<h3 id="short-prev">Preventative Techniques</h3>
10871087

@@ -1436,15 +1436,15 @@ contract NameRegistrar {
14361436

14371437
This simple name registrar has only one function. When the contract is `unlocked`, it allows anyone to register a name (as a `bytes32` hash) and map that name to an address. Unfortunately, this registrar is initially locked and the `require` on line \[23\] prevents `register()` from adding name records. There is however a vulnerability in this contract, that allows name registration regardless of the `unlocked` variable.
14381438

1439-
To discuss this vulnerability, first we need to understand how storage works in Solidity. As a high level overview (without any proper technical detail - I suggest reading the Solidity docs for a proper review), state variables are stored sequentially in *slots* as they appear in the contract (they can be grouped together, but not in this example, so we wont worry about that). Thus, `unlocked` exists in `slot 0`, `registeredNameRecord` exists in `slot 1` and `resolve` in `slot 2` etc. Each of these slots are of byte size 32 (there are added complexities with mappings which we ignore for now). The boolean `unlocked` will look like `0x000...0` (64 `0`'s, excluding the `0x`) for `false` or `0x000...1`(63 `0`'s) for `true`. As you can see, there is a significant waste of storage in this particular example.
1439+
To discuss this vulnerability, first we need to understand how storage works in Solidity. As a high level overview (without any proper technical detail - I suggest reading the Solidity docs for a proper review), state variables are stored sequentially in *slots* as they appear in the contract (they can be grouped together, but not in this example, so we won't worry about that). Thus, `unlocked` exists in `slot 0`, `registeredNameRecord` exists in `slot 1` and `resolve` in `slot 2` etc. Each of these slots are of byte size 32 (there are added complexities with mappings which we ignore for now). The boolean `unlocked` will look like `0x000...0` (64 `0`'s, excluding the `0x`) for `false` or `0x000...1`(63 `0`'s) for `true`. As you can see, there is a significant waste of storage in this particular example.
14401440

14411441
The next piece of information that we need, is that Solidity defaults complex data types, such as `structs`, to `storage` when initialising them as local variables. Therefore, `newRecord` on line \[16\] defaults to `storage`. The vulnerability is caused by the fact that `newRecord` is not initialised. Because it defaults to storage, it becomes a pointer to storage and because it is uninitialised, it points to slot `0` (i.e. where `unlocked` is stored). Notice that on lines \[17\] and \[18\] we then set `nameRecord.name` to `_name` and `nameRecord.mappedAddress` to `_mappedAddress`, this in effect changes the storage location of slot 0 and slot 1 which modifies both `unlocked` and the storage slot associated with `registeredNameRecord`.
14421442

14431443
This means that `unlocked` can be directly modified, simply by the `bytes32 _name` parameter of the `register()` function. Therefore, if the last byte of `_name` is non-zero, it will modify the last byte of storage `slot 0` and directly change `unlocked` to `true`. Such `_name` values will pass the `require()` on line \[23\] as we are setting `unlocked` to `true`. Try this in Remix. Notice the function will pass if you use a `_name` of the form: `0x0000000000000000000000000000000000000000000000000000000000000001`
14441444

14451445
<h3 id="storage-prev">Preventative Techniques</h3>
14461446

1447-
The Solidity compiler raises unintialised storage variables as warnings, thus developers should pay careful attention to these warnings when building smart contracts. The current version of mist (0.10), doesn't allow these contracts to be compiled. It is often good practice to explicitly use the `memory` or `storage` when dealing with complex types to ensure they behave as expected.
1447+
The Solidity compiler raises unintialised storage variables as warnings, thus developers should pay careful attention to these warnings when building smart contracts. The current version of mist (0.10), doesn't allow these contracts to be compiled. It is often good practice to explicitly use the `memory` or `storage` when dealing with complex types to ensure they behave as expected. (NOTE: explicit use of `memory` and `storage` are required as of `0.5.0`)
14481448

14491449
<h3 id="storage-example">Real-World Examples: Honey Pots: OpenAddressLottery and CryptoRoulette</h3>
14501450

@@ -1463,7 +1463,7 @@ For further reading, see [Ethereum Contract Security Techniques and Tips - Round
14631463

14641464
As there is no fixed point type in Solidity, developers are required to implement their own using the standard integer data types. There are a number of pitfalls developers can run into during this process. I will try to highlight some of these in this section.
14651465

1466-
Lets begin with a code example (lets ignore any over/under flow issues for simplicity).
1466+
Let's begin with a code example (let's ignore any over/under flow issues for simplicity).
14671467

14681468
```solidity
14691469
contract FunWithNumbers {
@@ -1497,7 +1497,7 @@ You should ensure that any ratios or rates you are using allow for large numerat
14971497

14981498
Another tactic to keep in mind, is to be mindful of order of operations. In the above example, the calculation to purchase tokens was `msg.value/weiPerEth*tokenPerEth`. Notice that the division occurs before the multiplication. This example would have achieved a greater precision if the calculation performed the multiplication first and then the division, i.e. `msg.value*tokenPerEth/weiPerEth`.
14991499

1500-
Finally, when defining arbitrary precision for numbers it can be a good idea to convert variables into higher precision, perform all mathematical operations, then finally when needed, convert back down to the precision for output. Typically `uint256`'s are used (as they are optimal for gas usage) which give approximately 60 orders of magnitude in their range, some which can be dedicated to the precision of mathematical operations. It may be the case that it is better to keep all variables in high precision in solidity and convert back to lower precisions in external apps (this is essentially how the `decimals` variable works in [ERC20 Token](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md) contracts). To see examples of how this can be done and the libraries to do this, I recommend looking at the [Maker DAO DSMath](https://github.com/dapphub/ds-math). They use some funky naming, `WAD`'s and `RAY`'s but the concept is useful.
1500+
Finally, when defining arbitrary precision for numbers it can be a good idea to convert variables into higher precision, perform all mathematical operations, then finally when needed, convert back down to the precision for output. Typically `uint256`'s are used (as they are optimal for gas usage) which give approximately 60 orders of magnitude in their range, some of which can be dedicated to the precision of mathematical operations. It may be the case that it is better to keep all variables in high precision in solidity and convert back to lower precisions in external apps (this is essentially how the `decimals` variable works in [ERC20 Token](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md) contracts). To see examples of how this can be done and the libraries to do this, I recommend looking at the [Maker DAO DSMath](https://github.com/dapphub/ds-math). They use some funky naming, `WAD`s and `RAY`s but the concept is useful.
15011501

15021502
<h3 id="precision-example">Real-World Example: Ethstick</h3>
15031503

0 commit comments

Comments
 (0)