Contratos inteligentes são, por definição, imutáveis, não podem ser removidos e/ou modificados, mas os dapps precisam ser atualizados para adicionar novas funcionalidades, corrigir bugs etc…
Existem muitas estratégias para atualizar contratos inteligentes, mas as mais comuns envolvem o uso de algum tipo de contrato Proxy que atuará como o “intermediário” do contrato real (chamado de contrato inteligente lógico), delegando chamadas para ele. Sempre que um contrato inteligente lógico precisa ser atualizado, simplesmente implantamos o novo contrato inteligente lógico e fazemos os pontos de proxy para ele.
Já existem algumas bibliotecas muito boas implementadas pelo Open Zeppelin fornecendo essa funcionalidade. A que eu tenho usado é a “Transparent Proxy” que pode ser encontrada aqui.
Em poucas palavras, a estratégia do Proxy Transparente é implantar um “Proxy Atualizável Transparente” para cada contrato inteligente lógico. Este Proxy Atualizável terá uma conta de administrador que é a única com o direito de executar métodos de administração (métodos que pertencem ao próprio proxy como alterar a conta de administrador, alterar o endereço lógico do contrato inteligente, …), qualquer invocação recebida de outra conta será delegada ao contrato inteligente lógico (invocações provenientes do administrador nunca são delegadas). Quando um contrato inteligente lógico precisa ser atualizado, o administrador do Proxy atualizável terá que atualizar o endereço do contrato inteligente lógico para o qual o Proxy atualizável está apontando, bem simples….
O problema que tive foi que queria implementar um aplicativo totalmente descentralizado, no qual a governança seria atribuída aos proprietários de token. Qualquer atualização para qualquer contrato inteligente teria que ser validado por eles, o que significa que os administradores de proxies teriam que ser um contrato inteligente e esses contratos inteligentes seriam controlados pelos proprietários do token (mais sobre isso aqui)…
Mestre-Escravo
A abordagem mais simples foi usar o que é conhecido como arquitetura “mestre-escravo”:
Um contrato inteligente “mestre” atua como administrador de todos os Proxies Atualizáveis Transparentes (um para cada contrato inteligente lógico).
Sempre que os contratos inteligentes lógicos precisarem se comunicar entre si, eles se referirão ao contrato inteligente mestre para recuperar os outros endereços de contratos inteligentes.
Isso parece relativamente simples até agora.
No entanto, o contrato inteligente principal também contém alguma lógica (principalmente relacionada à governança)... Isso cria dois problemas.
- O contrato inteligente mestre provavelmente precisará invocar outras funcionalidades de contratos inteligentes. O problema é que ele não pode invocar a lógica de outro contrato inteligente, pois é o administrador do Proxy atualizável (o que significa que suas invocações não serão encaminhadas para o contrato inteligente lógico pelos proxies)…
- O contrato inteligente mestre também precisa ser atualizável.
Proxy Admin
A resposta para a primeira questão é bem simples e já foi resolvida pelo padrão “Proxy Transparente” oferecido pelo Open Zeppelin: Proxy Admin.
O contrato inteligente mestre não será realmente o administrador de Proxies atualizáveis, ele implantará um contrato de administrador de proxy que atuará como o administrador. O contrato Proxy Admin também terá um administrador, o contrato inteligente mestre. Dessa forma, se precisarmos executar métodos de administração em nossos Proxies Atualizáveis, o contrato mestre fará com que o administrador do proxy os execute, se precisarmos executar a lógica por trás do proxy atualizável, os invocaremos diretamente do contrato mestre.
A resposta para a segunda questão é um pouco mais complicada. Essas são as diferentes soluções que encontrei.
Solução 1: Implantar Novo Mestre
Sempre que o contrato mestre precisar ser atualizado, um novo contrato será implantado e inicializado com todos os endereços de proxy atualizáveis. O antigo então transferiria a propriedade do Proxy Admin para ele.
Solução 2: Mestre, Um Escravo De Si Mesmo
Poderíamos olhar para o contrato Mestre como outro contrato “lógico” e implantar um proxy atualizável na frente dele. Assim como para todos os outros proxies atualizáveis, o administrador do contrato Mestre Proxy pode controlá-lo.
Dessa forma, sempre que o Mestre precisar ser atualizado, cuidaremos da atualização da mesma forma que lidamos com todas as outras atualizações de contratos.
Importante observar que, se estivermos atualizando vários contratos ao mesmo tempo, e o contrato mestre for um deles, seria uma boa ideia atualizá-lo no final, apenas para evitar comportamentos inesperados que podem advir do fato de o contrato estar sendo atualizado enquanto executado…
Solução 3: Contrato Admin
Outra solução poderia ser implantar um proxy atualizável na frente do contrato mestre, mas desta vez o contrato mestre não o controlará. Em vez disso, um “Contrato Admin” será implantado e atuará como o administrador do contrato principal da mesma forma que o contrato principal atua como um administrador para todos os outros contratos inteligentes (usando também um administrador proxy). O contrato mestre armazenará o endereço do contrato de admin (mais sobre isso mais tarde)
Até aí tudo bem, tendo um proxy atualizável na frente do Mestre e um contrato Admin gerenciando-o, podemos atualizar a lógica do contrato Mestre a qualquer momento.
O problema agora é que o próprio contrato Admin também precisa ser atualizável, pois conterá alguma lógica… Clássico catch-22….
A solução que pensei foi a seguinte:
Sempre que o contrato Admin precisar ser atualizado, um novo contrato Admin será implantado, uma proposta de atualização será enviada para o contrato Admin e se os proprietários do token o validarem:
- O antigo contrato Admin transferirá a propriedade de seu Admin proxy para o novo.
- O antigo contrato Admin solicitará ao Mestre que altere seu endereço Admin (Nota: Alterações no endereço Admin só podem ser feitas pelo próprio Admin).
- O novo contrato Admin agora está no controle do Admin Proxy (ele pode gerenciar as atualizações dos contratos principais) e também da funcionalidade de administração do contrato principal. Agora podemos considerar a atualização do contrato Admin concluída.
Esta solução é uma espécie de combinação das soluções 1 e 2.
Conclusão
Vamos comparar as três soluções com base em nossos requisitos.
- Os proprietários de tokens devem ter controle total da governança do dapp.
✔️Solução 1 ✔️Solução 2 ✔️Solução 3
- O contrato Mestre deve atuar como referência central do sistema, deve armazenar os endereços de todos os outros contratos.
✔️Solução 1 ✔️Solução 2 ✔️Solução 3
- O endereço do contrato mestre não deve mudar, o que significa que os front-ends que interagem com nosso dapp precisam apenas se lembrar dele para ter acesso à nossa funcionalidade, uma espécie de ponto de “âncora”.
❌Solução 1 ✔️Solução 2 ✔️Solução 3
As soluções 2 e 3 são as únicas que atendem aos nossos requisitos. Se tivermos que compará-los, os pontos mais relevantes que vejo são:
A solução 3 envolve a implantação de mais contratos inteligentes, o que significa que as implantações serão mais caras e teremos que gerenciar mais contratos inteligentes.
A solução 2 envolve a própria atualização do contrato mestre. Isso deve funcionar, mas é verdade que pode levar a mal-entendidos e/ou erros dos desenvolvedores… mas isso é apenas uma opinião e não um fato real…
No meu caso particular, decidi optar pela solução 2, pois não queria implantar em muitos contratos e minha lógica atualizável é relativamente simples.
Esse artigo é uma tradução feita por @bananlabs. Você pode encontrar o artigo original aqui
Latest comments (0)