Vamos analisar uma característica importante de segurança que você deve estar ciente ao escrever contratos inteligentes: Prevenção de transbordos e subfluxos.
Sumário
1 - O que é Overflow?
2 - Usando o SafeMath
..... . Vamos dar uma olhada no código por trás da SafeMath
3 - Usando a SafeMath em nosso código
O Que é Overflow?
Digamos que temos um uint8
, que só pode ter 8 bits. Isso significa que o maior número que podemos armazenar é o binário 11111111
(ou em decimal, 2^8 - 1 = 255).
Dê uma olhada no seguinte código. O que é igual ao number
no final?
uint8 number = 255;
number++;
Neste caso, fizemos um overflow - então number
agora é contraintuitivamente igual a 0
, embora o tenhamos aumentado. (Se você acrescentar 1 ao binário 11111111
, ele volta a 00000000
, como um relógio que vai das 23:59
às 00:00
).
Um underflow é semelhante, onde se você subtrair 1 de um uint8
que é igual a 0
, ele agora será igual a 255
(porque os uints
não são assinados, e não podem ser negativos).
Embora não estejamos usando uint8
aqui, e parece improvável que um uint256
overflow ao incrementar por 1
cada vez (2^256 é um número realmente grande), ainda é bom colocar proteções em nosso contrato para que nosso DApp nunca tenha um comportamento inesperado no futuro.
Usando o SafeMath
Para evitar isto, o OpenZeppelin criou uma biblioteca chamada SafeMath que evita estes problemas por padrão.
Mas antes de entrarmos nisso... O que é uma biblioteca?
Uma biblioteca é um tipo específico de contrato em Solidity. Uma das coisas para as quais ela é útil é anexar funções a tipos de dados nativos.
Por exemplo, com a biblioteca SafeMath, usaremos a sintaxe using SafeMath para uint256
. A biblioteca SafeMath tem 4 funções - add, sub, mul
, e div
. E agora podemos acessar estas funções a partir do uint256
da seguinte forma:
using SafeMath for uint256;
uint256 a = 5;
uint256 b = a.add(3); // 5 + 3 = 8
uint256 c = a.mul(2); // 5 * 2 = 10
Em breve veremos o que essas funções fazem.
Vamos dar uma olhada no código por trás da SafeMath:
library SafeMath {
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
uint256 c = a * b;
assert(c / a == b);
return c;
}
function div(uint256 a, uint256 b) internal pure returns (uint256) {
// assert(b > 0); // Solidity automatically throws when dividing by 0
uint256 c = a / b;
// assert(a == b * c + a % b); // There is no case in which this doesn't hold
return c;
}
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
assert(b <= a);
return a - b;
}
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
assert(c >= a);
return c;
}
}
Primeiro temos a palavra-chave library
- bibliotecas são semelhantes aos contracts
, mas com algumas diferenças. Para os nossos propósitos, as bibliotecas nos permitem usar a palavra-chave using
, que automaticamente se aplica a todos os métodos da biblioteca para outro tipo de dados:
using SafeMath for uint;
// now we can use these methods on any uint
uint test = 2;
test = test.mul(3); // test now equals 6
test = test.add(5); // test now equals 11
Observe que as funções mul
e add
requerem 2 argumentos cada, mas quando declaramos using SafeMath for uint
, o uint
que chamamos da função em (test
) é automaticamente passado como o primeiro argumento.
Vamos olhar para o código por trás de add
para ver o que a SafeMath faz:
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
assert(c >= a);
return c;
}
Basicamente, add
adiciona apenas 2 uints
como +
, mas também contém uma declaração assert
para garantir que a soma seja maior que a
. Isto nos protege de overflows.
assert
é semelhante à require
, onde ela lançará um erro se for falsa. A diferença entre assert
e require
é que require
restituirá ao usuário o resto de seu gas quando uma função falhar, enquanto que assert
não o fará. Portanto, na maioria das vezes você quer usar require
em seu código; assert
é tipicamente usada quando algo mau correu com o código (como um overflow de uint
).
Portanto, simplesmente coloque as funções da SafeMath, add, sub, mul
e div
, que são funções que fazem as 4 operações básicas da matemática, mas que mostram um erro se um overflow ou um underflow ocorrer.
Usando a SafeMath em nosso código.
Para evitar overflows e underflows, podemos procurar lugares em nosso código onde usamos +
, -
, *
, ou /
, e substituí-los por add, sub, mul, div
.
Este artigo foi escrito por Alexandr Kumancev e traduzido por Marcelo Panegali. O artigo original pode ser encontrado aqui.
Latest comments (0)