WEB3DEV

Cover image for Solicitação de Herança Incorreta em Contratos Inteligentes
Fatima Lima
Fatima Lima

Posted on

Solicitação de Herança Incorreta em Contratos Inteligentes

O que é herança múltipla em Solidity?

O Solidity permite múltiplas heranças, incluindo polimorfismo (N.T. permite que o desenvolvedor use o mesmo elemento de formas diferentes).

Uma função call, interna ou externa, executará sempre a função com o mesmo nome (e conjunto de tipos de parâmetros) no contrato mais derivado da árvore da herança. Cada função na hierarquia deve ser explicitamente habilitada usando as palavras-chave virtual e override.

Ao definir explicitamente o contrato utilizando ContractName.functionName() ou utilizando super.functionName() se você quiser chamar a função em um nível acima na hierarquia de herança achatada, é possível chamar funções internamente, mais acima na hierarquia de herança.

Quando um contrato herda de outro contrato, o código de todos os contratos base é compilado no contrato recém-formado, que é o único contrato criado na blockchain. Como resultado, quaisquer chamadas internas para funções do contrato base também empregarão apenas chamadas internas de funções (super.f(...) usará JUMP em vez de uma chamada de mensagem).

Aqui está um exemplo de como os contratos herdam uns dos outros:

pragma solidity ^0.8.0;
contract X {}
contract Y is X {}
contract C is Z, X {}
Enter fullscreen mode Exit fullscreen mode

Ordem Incorreta de Herança:

Diamond Problem (Problema de Diamante) na Herança:

A herança múltipla é suportada pelo Solidity, permitindo que um contrato herde vários contratos. A herança múltipla apresenta uma ambiguidade conhecida como o Problema de Diamante: qual contrato base deve ser chamado no contrato filho se dois ou mais definem uma função idêntica?

A Solidity resolve este dilema empregando a Linearização C3 reversa, que estabelece uma hierarquia entre os contratos base e é usada principalmente para determinar a ordem em que os métodos devem ser herdados na presença de herança múltipla.

Isto significa que nos contratos mostrados abaixo, o contrato "Filho" herdará dos outros contratos na ordem Filho -> Parte2 -> Parte1.

contract Part1 {
 constructor() public {}
}
contract Part2 {
 constructor() public {}
}
contract Child is Part1, Part2 {
 constructor() public {}
}
Enter fullscreen mode Exit fullscreen mode

Cenário de caso:

pragma solidity 0.5.17;

contract adminChecker {

   address admin = msg.sender;
   function roleCheck() internal view returns (bool) {
       return msg.sender == admin;
   }
}

contract guestChecker {

   address guest = msg.sender;
   function roleCheck() internal view returns (bool) {
       return msg.sender == guest;
   }
}

contract ownersCanKill is adminChecker, guestChecker {

   function kill() external{
       require(roleCheck(), "Not an Admin");
       selfdestruct(msg.sender);
   }
}
Enter fullscreen mode Exit fullscreen mode

No exemplo mostrado acima, há dois contratos de verificação de papéis (funções). Um deles verifica se o chamador tem uma função de administrador e o outro verifica se ele tem uma função de convidado. Ambas as funções retornam como verdadeiras se a afirmação for verdadeira.

Esses verificadores são herdados pelo contrato "ownersCanKill". A ordem de herança é importante aqui, pois será ela que decidirá qual função "roleCheck()" chamar.

O ideal é que os hóspedes não possam chamar a função "kill()", mas de acordo com a Linearização C3, a chamada de função seguirá o seguinte caminho:

ownersCanKill.kill() -> guestChecker.roleCheck() -> adminChecker.roleCheck()

Isto causará um comportamento inesperado e permitirá aos convidados selfdestruct (autodestruir) o contrato e transferir todos os fundos para suas contas.

Isto, entretanto, não funcionará a partir da Solidity 0.6.0+. O compilador irá lançar um erro para as funções ambíguas.

As melhores práticas para herança em Solidity:

Linearização C3:

Quando há herança múltipla, o algoritmo de linearização de superclasse C3 é amplamente utilizado para determinar a ordem na qual os métodos devem ser herdados.

Os desenvolvedores devem ter em mente a ordem enquanto especificam os nomes dos contratos ao herdá-los. Este será um fator decisivo para determinar de onde vêm as funções definidas.

A função pai é invocada:

Se o desenvolvedor quiser chamar especificamente uma função definida no contrato pai (parent), ele pode utilizar a palavra-chave "super" com a chamada da função: super.func().

Isto instruirá a EVM a utilizar a func() definida no contrato dos pais, ignorando completamente qualquer definição no filho.

Uma função, múltiplas formas:

Assim como os contratos podem ter numerosas formas, as funções também podem ter numerosas formas, que é o que o polimorfismo implica. A capacidade de ter inúmeras funções com o mesmo nome, desde que suas assinaturas sejam diferentes, é uma das melhores coisas do polimorfismo. Se eles utilizam entradas diferentes, ou seja, na verdade, isto é equivalente a ter apenas uma função, mas esta função tem múltiplas formas.

Conclusão:

Os desenvolvedores podem herdar funções, variáveis de estado e modificadores de função usando Solidity. O polimorfismo também é suportado pelo Solidity através da sobreposição de funções. A ambiguidade pode surgir quando múltiplos contratos herdados definem funções com os mesmos nomes e tipos de parâmetros. Ao implementar a substituição de funções em seus aplicativos, não se esqueça de usar as palavras virtual e override.

Você pode contar com o SolidityScan, um scanner de vulnerabilidade de contrato inteligente baseado em nuvem, para garantir que sejam tomadas as medidas adequadas para alcançar o mais alto nível de segurança de contrato inteligente. Inscreva-se para um teste gratuito em https://solidityscan.com/signup

Esse artigo foi escrito por Shashank e traduzido por Fátima Lima. O original pode ser lido aqui.

Latest comments (0)