第1课

Introdução e Contratos Multisig

Os contratos multiassinatura (multisig), também conhecidos como contratos “M-of-N”, são um mecanismo crucial usado para aumentar a segurança e flexibilidade das transações no ambiente blockchain. Estes contratos alteram a forma como o controlo é exercido sobre os activos, exigindo a aprovação de múltiplas partes antes que as transacções possam ser executadas. O termo "M-de-N" refere-se ao requisito de que M de um total de N partes devem aprovar a transação para que ela seja válida.

Teoria dos Contratos Multisig

Os contratos Multisig fornecem um meio de criar controle compartilhado sobre os ativos. Os casos de uso típicos envolvem serviços de garantia, gerenciamento de contas corporativas, assinatura conjunta de acordos financeiros e muito mais. Estes contratos são excepcionalmente benéficos para organizações ou grupos onde é necessária uma tomada de decisão colectiva.

Por definição, os contratos multisig são invioláveis e evitam pontos únicos de falha. Mesmo que as chaves de uma das partes sejam comprometidas, o invasor não poderá executar transações sem a aprovação das outras partes. Isso adiciona uma camada extra de segurança.

Os contratos Multisig podem ser considerados um equivalente digital de um cofre que requer múltiplas chaves para ser aberto. O número total de chaves (N) e o número mínimo de chaves necessárias para abrir a caixa (M) são acordados no momento da criação do contrato.

Os contratos Multisig podem ter muitas configurações diferentes dependendo dos valores de M e N:

  • 1 de N: Uma única parte do total pode aprovar a transação. Esta configuração é igual a uma transação normal sem multisig. Pode ser usado onde existem várias chaves por conveniência, mas qualquer uma delas pode aprovar transações.
  • N-de-N: Todas as partes devem aprovar a transação. Esta configuração fornece o mais alto nível de segurança, mas pode tornar-se problemática se uma parte perder a sua chave ou se recusar a aprovar transações.
  • M-de-N (onde M < N): Um subconjunto do total de partes deve aprovar a transação. Essa configuração é frequentemente usada na prática porque equilibra segurança com flexibilidade.

Contratos Multisig em Blockchain

No contexto do blockchain, os contratos multisig são amplamente utilizados para aumentar a segurança das transações, apoiar mecanismos complexos de governança ou manter o controle flexível sobre os ativos do blockchain. aqui estão alguns exemplos:

  • Carteiras: Carteiras Multisig são usadas para proteger ativos. Eles exigem que várias partes aprovem as transações, proporcionando assim segurança adicional contra roubo, hacks externos e ameaças internas.
  • Organizações Autônomas Descentralizadas (DAOs): Os DAOs costumam usar contratos multisig para fazer cumprir suas regras de governança. As votações nas propostas são implementadas como transações multisig, com os membros do DAO atuando como signatários. A proposta só é executada se receber votos suficientes.
  • Operações entre cadeias: Nas operações entre cadeias, os contratos multisig podem atuar como custodiantes de ativos. Quando os ativos são movidos de uma blockchain para outra, um contrato multisig na cadeia de origem pode garantir que os ativos sejam bloqueados com segurança até que a operação na outra cadeia seja confirmada.
    Embora a implementação de contratos multisig possa variar de uma blockchain para outra, o conceito central permanece o mesmo – a necessidade de múltiplas partes aprovarem uma transação antes de ela ser executada. Essa camada adicional de segurança torna os contratos multisig uma ferramenta essencial no blockchain e no espaço criptográfico.

Exemplo de codificação: escrevendo e implantando contratos Multisig com SmartPy

Quanto aos nossos exemplos de código, veremos três implementações diferentes de contratos com múltiplas assinaturas:

O Contrato Lambda

É bastante versátil e permite uma ampla gama de utilizações. Requer múltiplas assinaturas para executar funções lambda arbitrárias.

Python 
 importa smartpy como sp 


 @sp.module 
 def main(): 
 operation_lambda: type = sp.lambda_(sp.unit, sp.unit, with_operations=True) 

 class MultisigLambda(sp.Contract): 
 """Vários membros votam na execução de lambdas.

        Este contrato pode ser originado com uma lista de endereços e um número de 
 votos necessários. Qualquer membro pode enviar quantos lambdas quiser e votar 
 para propostas ativas. Quando um lambda atinge os votos necessários, seu código é chamado 
 e as operações de saída são executadas. Isso permite que este contrato 
 faça tudo o que um contrato pode fazer: transferir tokens, gerenciar ativos, 
 administrar outro contrato...

        Quando um lambda é aplicado, todos os lambdas enviados até agora são inativados.
        Os membros ainda podem enviar novos lambdas.
        """ 

 def __init__(self, member, require_votes): 
 """Construtor 

 Args: 
 membros (sp.set de sp.address): pessoas que podem enviar e votar 
 para lambda.
                requerimentos_votos (sp.nat): número de votos necessários 
 """ 
 assert requerimentos_votos <= sp.len( 
 membros 
 ), "required_votes devem ser <= len(membros)" 
 self.data.lambdas = sp.cast (
                sp.big_map(), sp.big_map[sp.nat, operação_lambda] 
 ) 
 self.data.votes = sp.cast(
                sp.big_map(), sp.big_map[sp.nat, sp.set[sp.address]] 
 ) 
 self.data.nextId = 0 
 self.data.inactiveBefore = 0 
 self.data.members = sp.cast(membros, sp.set[sp.endereço])
            self.data.required_votes = sp.cast(votos_obrigatórios, sp.nat) 

 @sp.entrypoint 
 def submit_lambda(self, lambda_): 
 """Envie um novo lambda para votação.

            Apresentar uma proposta não implica votar a favor da mesma.

            Args: 
 lambda_(sp.lambda com operações): lambda propôs votar.
            Aumenta: 
 `Você não é um membro` 
 """ 
 assert self.data.members.contains(sp.sender), "Você não é membro" 
 self.data.lambdas[self.data.nextId] = lambda_ 
 self.data.votes[self.data.nextId] = sp.set()
            self.data.nextId += 1 

 @sp.entrypoint 
 def vote_lambda(self, id): 
 """Vote em um lambda.

            Args: 
 id(sp.nat): id do lambda para votar.
            Aumenta: 
 `Você não é membro`, `O lambda está inativo`, `Lambda não encontrado` 

 Não há voto contra ou aprovação. Se alguém discordar de um lambda 
 poderá evitar votar.
            """ 
 afirmação self.data.members.contains(sp.sender), "Você não é membro" 
 assert id >= self.data.inactiveBefore, "O lambda está inativo" 
 afirmação self.data.lambdas.contains(id), "Lambda não encontrado" 
 self.data.votes[id].add(sp.sender)
            se sp.len(self.data.votes[id]) >= self.data.required_votes:
                self.data.lambdas[id]()
                self.data.inactiveBefore = self.data.nextId 

 @sp.onchain_view() 
 def get_lambda(self, id): 
 """Retorna o lambda correspondente.

            Args: 
 id (sp.nat): id do lambda a ser obtido.

            Retorno: 
 par de lambda e um booleano mostrando se o lambda está ativo.
            """ 
 retorno (self.data.lambdas[id], id >= self.data.inactiveBefore)


# se "templates" não estiverem em __name__: 


 @sp.module 
 def test(): 
 class Administrated(sp.Contract): 
 def __init__(self, admin): 
 self.data.admin = admin 
 self.data.value = sp.int(0)

        @sp.entrypoint 
 def set_value(self, value): 
 assert sp.sender == self.data.admin 
 self.data.value = value 


 @sp.add_test(name="Cenário básico MultisigLambda", is_default=True ) 
 def basic_scenario(): 
 """Use o multisigLambda como administrador de um contrato de exemplo.

    Testes: 
 - Originação 
 - Submissão Lambda 
 - Votação Lambda 
 """ 
 sc = sp.test_scenario([main, teste]) 
 sc.h1("Cenário básico.")

    membro1 = sp.test_account("membro1")
    membro2 = sp.test_account("membro2")
    membro3 = sp.test_account("membro3")
    membros = sp.set([membro1.endereço, membro2.endereço, membro3.endereço])

    sc.h2("MultisigLambda: origem") 
 c1 = main.MultisigLambda(membros, 2) 
 sc += c1 

 sc.h2("Administrado: origem") 
 c2 = test.Administrado(c1.address)
    sc += c2 

 sc.h2("MultisigLambda: submit_lambda") 

 def set_42(params): 
 administrado = sp.contract(sp.TInt, c2.address, entrypoint="set_value") 
 sp.transfer(sp. interno(42), sp.tez(0), administrado.open_some())

    lambda_ = sp.build_lambda(conjunto_42, with_operations=True) 
 c1.submit_lambda(lambda_).run(sender=member1) 

 sc.h2("MultisigLambda: vote_lambda") 
 c1.vote_lambda(0).run(sender=member1)
    c1.vote_lambda(0).run(remetente=membro2)

    # Podemos verificar se o contrato administrado recebeu a transferência.
    sc.verify(c2.data.value == 42)

O Contrato MultisigAction

Introduz o conceito de votação de propostas. Neste contrato, os signatários podem votar para que determinadas ações sejam tomadas e, caso seja atingido o quórum, as ações propostas são executadas.

Python 
 importar smartpy como sp 


 @sp.module 
 def main(): 
 # Especificação do tipo de ação de administração interna 
 InternalAdminAction: type = sp.variant(
        addSigners=sp.lista[sp.endereço],
        alterarQuorum=sp.nat,
        removeSigners=sp.list[sp.address],
    ) 

 classe MultisigAction(sp.Contract): 
 """Um contrato que pode ser usado por vários signatários para administrar outros 
 contratos. Os contratos administrados implementam 
 interface que permite explicitar o processo de administração para usuários não especialistas.

        Os signatários votam nas propostas. Uma proposta é uma lista de uma meta com uma lista de 
 ação. Uma ação é um byte simples, mas pretende ser um valor de pacote de 
 por variante. Este padrão simples permite construir uma interface UX 
 que mostra o conteúdo de uma proposta ou construir uma.
        """ 

 def __init__(self, quorum, signatários): 
 self.data.inactiveBefore = 0 
 self.data.nextId = 0 
 self.data.proposals = sp.cast(
                sp.big_map(), 
 sp.big_map[ 
 sp.nat, 
 sp.list[sp.record(target=sp.address, ações=sp.lista[sp.bytes])],
                ], 
 ) 
 self.data.quorum = sp.cast(quorum, sp.nat) 
 self.data.signers = sp.cast(signatários, sp.set[sp.endereço])
            self.data.votes = sp.cast(
                sp.big_map(), sp.big_map[sp.nat, sp.set[sp.address]] 
 ) 

 @sp.entrypoint 
 def send_proposal(self, proposta): 
 """Somente signatário. Envie uma proposta para votação.

            Args: 
 proposta (sp.list de sp.record de endereço alvo e ação): Lista\ 
 de alvo e ações de administração associadas.
            """ 
 afirmação self.data.signers.contains(sp.sender), "Somente signatários podem propor" 
 self.data.proposals[self.data.nextId] = proposta 
 self.data.votes[self.data.nextId] = sp.set()
            self.data.nextId += 1 

 @sp.entrypoint 
 def vote(self, pId): 
 """Vote em uma ou mais propostas 

 Args: 
 pId (sp.nat): ID da proposta.
            """ 
 afirmação self.data.signers.contains(sp.sender), "Somente signatários podem votar" 
 afirmação self.data.votes.contains(pId), "Proposta desconhecida" 
 assert pId >= self.data.inactiveBefore, "A proposta está inativa" 
 self.data.votes[pId].add(sp.sender)

            se sp.len(self.data.votes.get(pId, padrão=sp.set())) >= self.data.quorum:
                self._onAprovado(pId)

        @sp.private(with_storage="read-write", with_operations=True) 
 def _onApproved(self, pId): 
 """Função embutida. Lógica aplicada quando uma proposta é aprovada."""
            proposta = self.data.proposals.get(pId, padrão=[]) 
 para p_item na proposta: 
 contrato = sp.contract(sp.list[sp.bytes], p_item.target)
                sp.transfer( 
 p_item.actions,
                    sp.tez(0), 
 contrato.unwrap_some(error="InvalidTarget"),
                ) 
 # Desative todas as propostas que já foram enviadas.
            self.data.inactiveBefore = self.data.nextId 

 @sp.entrypoint 
 def administrate(self, actions): 
 """Somente autochamada. Administrar este contrato.

            Este ponto de entrada deve ser chamado através do sistema de propostas.

            Args: 
 ações (sp.list de sp.bytes): Lista de variante compactada de \ 
 `InternalAdminAction` (`addSigners`, `changeQuorum`, `removeSigners`).
            """ 
 assert ( 
 sp.sender == sp.self_address() 
 ), "Este ponto de entrada deve ser chamado através do sistema de proposta." 

 para pack_actions em ações: 
 action = sp.unpack(packed_actions, InternalAdminAction).unwrap_some( 
 error="Formato de ações incorretas" 
 ) 
 com sp.match(action): 
 com sp.case.changeQuorum como quorum: 
 self.data.quorum = quorum 
 com sp.case.addSigners como adicionado: 
 para signatário adicionado: 
 self.data.signers.add(signer)
                    com sp.case.removeSigners como removido: 
 para endereço removido: 
 self.data.signers.remove(address)
                # Certifique-se de que o contrato nunca exija mais quórum do que o total de signatários.
                assert self.data.quorum <= sp.len( 
 self.data.signers 
 ), "Mais quórum do que signatários."


se "modelos" não estiverem em __name__: 

 @sp.add_test(name="Cenário básico", is_default=True) 
 def test(): 
 signer1 = sp.test_account("signer1")
        signer2 = sp.test_account("signer2")
        signer3 = sp.test_account("signer3")

        s = sp.test_scenario(principal)
        s.h1("Cenário básico") 

 s.h2("Origem") 
 c1 = main.MultisigAction( 
 quorum=2, 
 signers=sp.set([signer1.address, signer2.address]), 
 ) 
 s += c1 

 s.h2("Proposta para adicionar um novo signatário") 
 target = sp.to_address(
            sp.contract(sp.TList(sp.TBytes), c1.address, "administrar").open_some() 
 ) 
 ação = sp.pack(
            sp.set_type_expr( 
 sp.variant("addSigners", [signer3.address]), main.InternalAdminAction 
 ) 
 ) 
 c1.send_proposal([sp.record(target=target, actions=[action])]).run( 
 sender=signer1 
 ) 

 s.h2("Signatário 1 vota na proposta") 
 c1.vote(0).run(sender=signer1)
        s.h2("Signatário 2 votos para a proposta") 
 c1.vote(0).run(sender=signer2)

        s.verify(c1.data.signers.contains(signer3.address))

O Contrato MultisigView

Ele também utiliza um mecanismo de votação. Este contrato permite que os membros enviem e votem em bytes arbitrários. Depois que uma proposta atinge o número necessário de votos, seu status pode ser confirmado por meio de uma visualização.

Python 
 import smartpy as sp 


 @sp.module 
 def main(): 
 class MultisigView(sp.Contract): 
 """Vários membros votam em bytes arbitrários.

        Este contrato pode ser originado com uma lista de endereços e um número de 
 votos necessários. Qualquer membro pode enviar quantos bytes quiser e votar 
 nas propostas ativas.

        Quaisquer bytes que atingiram os votos necessários podem ser confirmados por meio de uma visualização.
        """ 

 def __init__(self, member, require_votes): 
 """Construtor 

 Args: 
 membros (sp.set de sp.address): pessoas que podem enviar e votar em 
 lambda.
                requerimentos_votos (sp.nat): número de votos necessários 
 """ 
 assert requerimentos_votos <= sp.len( 
 membros 
 ), "required_votes devem ser <= len(membros)" 
 self.data.proposals = sp.cast (sp.big_map(), sp.big_map[sp.bytes, sp.bool]) 
 self.data.votes = sp.cast(
                sp.big_map(), sp.big_map[sp.bytes, sp.set[sp.address]] 
 ) 
 self.data.members = sp.cast(membros, sp.set[sp.endereço])
            self.data.required_votes = sp.cast(votos_obrigatórios, sp.nat) 

 @sp.entrypoint 
 def submit_proposal(self, bytes): 
 """Envie uma nova proposta para votação.

            Apresentar uma proposta não implica votar a favor da mesma.

            Args: 
 byte (sp.bytes): bytes propostos para votação.
            Aumenta: 
 `Você não é um membro` 
 """ 
 assert self.data.members.contains(sp.sender), "Você não é membro" 
 self.data.proposals[bytes] = Falso 
 self.data.votes[bytes] = sp.set()

        @sp.entrypoint 
 def vote_proposal(self, bytes): 
 """Vote em uma proposta.

            Não há voto contra ou aprovação. Se alguém discordar de uma proposta 
 poderá evitar votar. Atenção: propostas antigas não votadas nunca se tornam 
 .

            Args: 
 id (sp.bytes): bytes da proposta.
            Aumenta: 
 `Você não é membro`, `Proposta não encontrada` 
 """ 
 assert self.data.members.contains(sp.sender), "Você não é membro" 
 afirmação self.data.proposals.contains(bytes), "Proposta não encontrada" 
 self.data.votes[bytes].add(sp.sender)
            se sp.len(self.dados.votos[bytes]) >= self.data.required_votes:
                self.data.proposals[bytes] = True 

 @sp.onchain_view() 
 def is_voted(self, id): 
 """Retorna um booleano indicando se a proposta foi votada.

            Args: 
 id (sp.bytes): bytes da proposta 
 Retorno: 
 (sp.bool): Verdadeiro se a proposta foi votada; caso contrário, falso.
            """ 
 retorna self.data.proposals.get(id, error="Proposta não encontrada") 


 se "modelos" não estiverem em __name__: 

 @sp.add_test(name="Cenário básico MultisigView", is_default=True) 
 def basic_scenario(): 
 """Um cenário com uma votação sobre o contrato

        Testes: 
 - Originação 
 - Envio da proposta 
 - Votação da proposta 
 """ 
 sc = sp.test_scenario(main)
        sc.h1("Cenário básico.")

        membro1 = sp.test_account("membro1")
        membro2 = sp.test_account("membro2")
        membro3 = sp.test_account("membro3")
        membros = sp.set([membro1.endereço, membro2.endereço, membro3.endereço])

        sc.h2("Origem") 
 c1 = main.MultisigView(membros, 2) 
 sc += c1 

 sc.h2("submit_proposal") 
 c1.submit_proposal(sp.bytes("0x42")).run( remetente=membro1) 

 sc.h2("vote_proposal") 
 c1.vote_proposal(sp.bytes("0x42")).run(sender=member1) 
 c1.vote_proposal(sp.bytes("0x42")).run (sender=member2) 

 # Podemos verificar se a proposta foi validada.
        sc.verify(c1.is_voted(sp.bytes("0x42")))

Cada contrato fornece um mecanismo diferente para obter controle de múltiplas assinaturas, oferecendo flexibilidade dependendo das necessidades específicas do seu caso de uso de blockchain.

Guia passo a passo para experimentar o contrato Multisig no SmartPy Online

Para experimentar os contratos multisig que escrevemos no SmartPy, você pode seguir estas etapas:

  1. Acesse o IDE SmartPy em https://smartpy.io/ide.

  2. Cole o código do contrato no editor. Você pode substituir o código existente.

  3. Para executar o contrato, clique no botão “Executar” localizado no painel superior.

  4. Após executar o contrato, você pode visualizar a execução do cenário no painel “Output” à direita. Aqui você pode ver detalhes de cada ação, incluindo propostas, votações e aprovações.

  5. Para implantar seu contrato na rede Tezos, primeiro você precisa compilá-lo. Clique no botão “Compile” no painel superior.

  6. Após a compilação, você pode implantar o contrato na testnet clicando em “Implementar Contrato Michelson”. Você precisará fornecer uma chave secreta para uma conta Tezos com fundos suficientes para pagar os custos de implantação do gás.

  7. Assim que o contrato for implantado, você receberá o endereço do contrato no blockchain. Você pode usar esse endereço para interagir com o contrato por meio de transações.

  8. Para enviar propostas ou votar nos contratos, você pode utilizar os pontos de entrada definidos no código do contrato, como submit_proposal ou vote_proposal. Eles podem ser chamados diretamente das transações que você cria.

Lembre-se, embora o IDE SmartPy permita testar seu contrato em um blockchain simulado, a implantação do contrato na rede Tezos real incorrerá em custos de gás, que devem ser pagos em XTZ, a criptomoeda nativa da rede Tezos.

免责声明
* 投资有风险,入市须谨慎。本课程不作为投资理财建议。
* 本课程由入驻Gate Learn的作者创作,观点仅代表作者本人,绝不代表Gate Learn赞同其观点或证实其描述。
目录
第1课

Introdução e Contratos Multisig

Os contratos multiassinatura (multisig), também conhecidos como contratos “M-of-N”, são um mecanismo crucial usado para aumentar a segurança e flexibilidade das transações no ambiente blockchain. Estes contratos alteram a forma como o controlo é exercido sobre os activos, exigindo a aprovação de múltiplas partes antes que as transacções possam ser executadas. O termo "M-de-N" refere-se ao requisito de que M de um total de N partes devem aprovar a transação para que ela seja válida.

Teoria dos Contratos Multisig

Os contratos Multisig fornecem um meio de criar controle compartilhado sobre os ativos. Os casos de uso típicos envolvem serviços de garantia, gerenciamento de contas corporativas, assinatura conjunta de acordos financeiros e muito mais. Estes contratos são excepcionalmente benéficos para organizações ou grupos onde é necessária uma tomada de decisão colectiva.

Por definição, os contratos multisig são invioláveis e evitam pontos únicos de falha. Mesmo que as chaves de uma das partes sejam comprometidas, o invasor não poderá executar transações sem a aprovação das outras partes. Isso adiciona uma camada extra de segurança.

Os contratos Multisig podem ser considerados um equivalente digital de um cofre que requer múltiplas chaves para ser aberto. O número total de chaves (N) e o número mínimo de chaves necessárias para abrir a caixa (M) são acordados no momento da criação do contrato.

Os contratos Multisig podem ter muitas configurações diferentes dependendo dos valores de M e N:

  • 1 de N: Uma única parte do total pode aprovar a transação. Esta configuração é igual a uma transação normal sem multisig. Pode ser usado onde existem várias chaves por conveniência, mas qualquer uma delas pode aprovar transações.
  • N-de-N: Todas as partes devem aprovar a transação. Esta configuração fornece o mais alto nível de segurança, mas pode tornar-se problemática se uma parte perder a sua chave ou se recusar a aprovar transações.
  • M-de-N (onde M < N): Um subconjunto do total de partes deve aprovar a transação. Essa configuração é frequentemente usada na prática porque equilibra segurança com flexibilidade.

Contratos Multisig em Blockchain

No contexto do blockchain, os contratos multisig são amplamente utilizados para aumentar a segurança das transações, apoiar mecanismos complexos de governança ou manter o controle flexível sobre os ativos do blockchain. aqui estão alguns exemplos:

  • Carteiras: Carteiras Multisig são usadas para proteger ativos. Eles exigem que várias partes aprovem as transações, proporcionando assim segurança adicional contra roubo, hacks externos e ameaças internas.
  • Organizações Autônomas Descentralizadas (DAOs): Os DAOs costumam usar contratos multisig para fazer cumprir suas regras de governança. As votações nas propostas são implementadas como transações multisig, com os membros do DAO atuando como signatários. A proposta só é executada se receber votos suficientes.
  • Operações entre cadeias: Nas operações entre cadeias, os contratos multisig podem atuar como custodiantes de ativos. Quando os ativos são movidos de uma blockchain para outra, um contrato multisig na cadeia de origem pode garantir que os ativos sejam bloqueados com segurança até que a operação na outra cadeia seja confirmada.
    Embora a implementação de contratos multisig possa variar de uma blockchain para outra, o conceito central permanece o mesmo – a necessidade de múltiplas partes aprovarem uma transação antes de ela ser executada. Essa camada adicional de segurança torna os contratos multisig uma ferramenta essencial no blockchain e no espaço criptográfico.

Exemplo de codificação: escrevendo e implantando contratos Multisig com SmartPy

Quanto aos nossos exemplos de código, veremos três implementações diferentes de contratos com múltiplas assinaturas:

O Contrato Lambda

É bastante versátil e permite uma ampla gama de utilizações. Requer múltiplas assinaturas para executar funções lambda arbitrárias.

Python 
 importa smartpy como sp 


 @sp.module 
 def main(): 
 operation_lambda: type = sp.lambda_(sp.unit, sp.unit, with_operations=True) 

 class MultisigLambda(sp.Contract): 
 """Vários membros votam na execução de lambdas.

        Este contrato pode ser originado com uma lista de endereços e um número de 
 votos necessários. Qualquer membro pode enviar quantos lambdas quiser e votar 
 para propostas ativas. Quando um lambda atinge os votos necessários, seu código é chamado 
 e as operações de saída são executadas. Isso permite que este contrato 
 faça tudo o que um contrato pode fazer: transferir tokens, gerenciar ativos, 
 administrar outro contrato...

        Quando um lambda é aplicado, todos os lambdas enviados até agora são inativados.
        Os membros ainda podem enviar novos lambdas.
        """ 

 def __init__(self, member, require_votes): 
 """Construtor 

 Args: 
 membros (sp.set de sp.address): pessoas que podem enviar e votar 
 para lambda.
                requerimentos_votos (sp.nat): número de votos necessários 
 """ 
 assert requerimentos_votos <= sp.len( 
 membros 
 ), "required_votes devem ser <= len(membros)" 
 self.data.lambdas = sp.cast (
                sp.big_map(), sp.big_map[sp.nat, operação_lambda] 
 ) 
 self.data.votes = sp.cast(
                sp.big_map(), sp.big_map[sp.nat, sp.set[sp.address]] 
 ) 
 self.data.nextId = 0 
 self.data.inactiveBefore = 0 
 self.data.members = sp.cast(membros, sp.set[sp.endereço])
            self.data.required_votes = sp.cast(votos_obrigatórios, sp.nat) 

 @sp.entrypoint 
 def submit_lambda(self, lambda_): 
 """Envie um novo lambda para votação.

            Apresentar uma proposta não implica votar a favor da mesma.

            Args: 
 lambda_(sp.lambda com operações): lambda propôs votar.
            Aumenta: 
 `Você não é um membro` 
 """ 
 assert self.data.members.contains(sp.sender), "Você não é membro" 
 self.data.lambdas[self.data.nextId] = lambda_ 
 self.data.votes[self.data.nextId] = sp.set()
            self.data.nextId += 1 

 @sp.entrypoint 
 def vote_lambda(self, id): 
 """Vote em um lambda.

            Args: 
 id(sp.nat): id do lambda para votar.
            Aumenta: 
 `Você não é membro`, `O lambda está inativo`, `Lambda não encontrado` 

 Não há voto contra ou aprovação. Se alguém discordar de um lambda 
 poderá evitar votar.
            """ 
 afirmação self.data.members.contains(sp.sender), "Você não é membro" 
 assert id >= self.data.inactiveBefore, "O lambda está inativo" 
 afirmação self.data.lambdas.contains(id), "Lambda não encontrado" 
 self.data.votes[id].add(sp.sender)
            se sp.len(self.data.votes[id]) >= self.data.required_votes:
                self.data.lambdas[id]()
                self.data.inactiveBefore = self.data.nextId 

 @sp.onchain_view() 
 def get_lambda(self, id): 
 """Retorna o lambda correspondente.

            Args: 
 id (sp.nat): id do lambda a ser obtido.

            Retorno: 
 par de lambda e um booleano mostrando se o lambda está ativo.
            """ 
 retorno (self.data.lambdas[id], id >= self.data.inactiveBefore)


# se "templates" não estiverem em __name__: 


 @sp.module 
 def test(): 
 class Administrated(sp.Contract): 
 def __init__(self, admin): 
 self.data.admin = admin 
 self.data.value = sp.int(0)

        @sp.entrypoint 
 def set_value(self, value): 
 assert sp.sender == self.data.admin 
 self.data.value = value 


 @sp.add_test(name="Cenário básico MultisigLambda", is_default=True ) 
 def basic_scenario(): 
 """Use o multisigLambda como administrador de um contrato de exemplo.

    Testes: 
 - Originação 
 - Submissão Lambda 
 - Votação Lambda 
 """ 
 sc = sp.test_scenario([main, teste]) 
 sc.h1("Cenário básico.")

    membro1 = sp.test_account("membro1")
    membro2 = sp.test_account("membro2")
    membro3 = sp.test_account("membro3")
    membros = sp.set([membro1.endereço, membro2.endereço, membro3.endereço])

    sc.h2("MultisigLambda: origem") 
 c1 = main.MultisigLambda(membros, 2) 
 sc += c1 

 sc.h2("Administrado: origem") 
 c2 = test.Administrado(c1.address)
    sc += c2 

 sc.h2("MultisigLambda: submit_lambda") 

 def set_42(params): 
 administrado = sp.contract(sp.TInt, c2.address, entrypoint="set_value") 
 sp.transfer(sp. interno(42), sp.tez(0), administrado.open_some())

    lambda_ = sp.build_lambda(conjunto_42, with_operations=True) 
 c1.submit_lambda(lambda_).run(sender=member1) 

 sc.h2("MultisigLambda: vote_lambda") 
 c1.vote_lambda(0).run(sender=member1)
    c1.vote_lambda(0).run(remetente=membro2)

    # Podemos verificar se o contrato administrado recebeu a transferência.
    sc.verify(c2.data.value == 42)

O Contrato MultisigAction

Introduz o conceito de votação de propostas. Neste contrato, os signatários podem votar para que determinadas ações sejam tomadas e, caso seja atingido o quórum, as ações propostas são executadas.

Python 
 importar smartpy como sp 


 @sp.module 
 def main(): 
 # Especificação do tipo de ação de administração interna 
 InternalAdminAction: type = sp.variant(
        addSigners=sp.lista[sp.endereço],
        alterarQuorum=sp.nat,
        removeSigners=sp.list[sp.address],
    ) 

 classe MultisigAction(sp.Contract): 
 """Um contrato que pode ser usado por vários signatários para administrar outros 
 contratos. Os contratos administrados implementam 
 interface que permite explicitar o processo de administração para usuários não especialistas.

        Os signatários votam nas propostas. Uma proposta é uma lista de uma meta com uma lista de 
 ação. Uma ação é um byte simples, mas pretende ser um valor de pacote de 
 por variante. Este padrão simples permite construir uma interface UX 
 que mostra o conteúdo de uma proposta ou construir uma.
        """ 

 def __init__(self, quorum, signatários): 
 self.data.inactiveBefore = 0 
 self.data.nextId = 0 
 self.data.proposals = sp.cast(
                sp.big_map(), 
 sp.big_map[ 
 sp.nat, 
 sp.list[sp.record(target=sp.address, ações=sp.lista[sp.bytes])],
                ], 
 ) 
 self.data.quorum = sp.cast(quorum, sp.nat) 
 self.data.signers = sp.cast(signatários, sp.set[sp.endereço])
            self.data.votes = sp.cast(
                sp.big_map(), sp.big_map[sp.nat, sp.set[sp.address]] 
 ) 

 @sp.entrypoint 
 def send_proposal(self, proposta): 
 """Somente signatário. Envie uma proposta para votação.

            Args: 
 proposta (sp.list de sp.record de endereço alvo e ação): Lista\ 
 de alvo e ações de administração associadas.
            """ 
 afirmação self.data.signers.contains(sp.sender), "Somente signatários podem propor" 
 self.data.proposals[self.data.nextId] = proposta 
 self.data.votes[self.data.nextId] = sp.set()
            self.data.nextId += 1 

 @sp.entrypoint 
 def vote(self, pId): 
 """Vote em uma ou mais propostas 

 Args: 
 pId (sp.nat): ID da proposta.
            """ 
 afirmação self.data.signers.contains(sp.sender), "Somente signatários podem votar" 
 afirmação self.data.votes.contains(pId), "Proposta desconhecida" 
 assert pId >= self.data.inactiveBefore, "A proposta está inativa" 
 self.data.votes[pId].add(sp.sender)

            se sp.len(self.data.votes.get(pId, padrão=sp.set())) >= self.data.quorum:
                self._onAprovado(pId)

        @sp.private(with_storage="read-write", with_operations=True) 
 def _onApproved(self, pId): 
 """Função embutida. Lógica aplicada quando uma proposta é aprovada."""
            proposta = self.data.proposals.get(pId, padrão=[]) 
 para p_item na proposta: 
 contrato = sp.contract(sp.list[sp.bytes], p_item.target)
                sp.transfer( 
 p_item.actions,
                    sp.tez(0), 
 contrato.unwrap_some(error="InvalidTarget"),
                ) 
 # Desative todas as propostas que já foram enviadas.
            self.data.inactiveBefore = self.data.nextId 

 @sp.entrypoint 
 def administrate(self, actions): 
 """Somente autochamada. Administrar este contrato.

            Este ponto de entrada deve ser chamado através do sistema de propostas.

            Args: 
 ações (sp.list de sp.bytes): Lista de variante compactada de \ 
 `InternalAdminAction` (`addSigners`, `changeQuorum`, `removeSigners`).
            """ 
 assert ( 
 sp.sender == sp.self_address() 
 ), "Este ponto de entrada deve ser chamado através do sistema de proposta." 

 para pack_actions em ações: 
 action = sp.unpack(packed_actions, InternalAdminAction).unwrap_some( 
 error="Formato de ações incorretas" 
 ) 
 com sp.match(action): 
 com sp.case.changeQuorum como quorum: 
 self.data.quorum = quorum 
 com sp.case.addSigners como adicionado: 
 para signatário adicionado: 
 self.data.signers.add(signer)
                    com sp.case.removeSigners como removido: 
 para endereço removido: 
 self.data.signers.remove(address)
                # Certifique-se de que o contrato nunca exija mais quórum do que o total de signatários.
                assert self.data.quorum <= sp.len( 
 self.data.signers 
 ), "Mais quórum do que signatários."


se "modelos" não estiverem em __name__: 

 @sp.add_test(name="Cenário básico", is_default=True) 
 def test(): 
 signer1 = sp.test_account("signer1")
        signer2 = sp.test_account("signer2")
        signer3 = sp.test_account("signer3")

        s = sp.test_scenario(principal)
        s.h1("Cenário básico") 

 s.h2("Origem") 
 c1 = main.MultisigAction( 
 quorum=2, 
 signers=sp.set([signer1.address, signer2.address]), 
 ) 
 s += c1 

 s.h2("Proposta para adicionar um novo signatário") 
 target = sp.to_address(
            sp.contract(sp.TList(sp.TBytes), c1.address, "administrar").open_some() 
 ) 
 ação = sp.pack(
            sp.set_type_expr( 
 sp.variant("addSigners", [signer3.address]), main.InternalAdminAction 
 ) 
 ) 
 c1.send_proposal([sp.record(target=target, actions=[action])]).run( 
 sender=signer1 
 ) 

 s.h2("Signatário 1 vota na proposta") 
 c1.vote(0).run(sender=signer1)
        s.h2("Signatário 2 votos para a proposta") 
 c1.vote(0).run(sender=signer2)

        s.verify(c1.data.signers.contains(signer3.address))

O Contrato MultisigView

Ele também utiliza um mecanismo de votação. Este contrato permite que os membros enviem e votem em bytes arbitrários. Depois que uma proposta atinge o número necessário de votos, seu status pode ser confirmado por meio de uma visualização.

Python 
 import smartpy as sp 


 @sp.module 
 def main(): 
 class MultisigView(sp.Contract): 
 """Vários membros votam em bytes arbitrários.

        Este contrato pode ser originado com uma lista de endereços e um número de 
 votos necessários. Qualquer membro pode enviar quantos bytes quiser e votar 
 nas propostas ativas.

        Quaisquer bytes que atingiram os votos necessários podem ser confirmados por meio de uma visualização.
        """ 

 def __init__(self, member, require_votes): 
 """Construtor 

 Args: 
 membros (sp.set de sp.address): pessoas que podem enviar e votar em 
 lambda.
                requerimentos_votos (sp.nat): número de votos necessários 
 """ 
 assert requerimentos_votos <= sp.len( 
 membros 
 ), "required_votes devem ser <= len(membros)" 
 self.data.proposals = sp.cast (sp.big_map(), sp.big_map[sp.bytes, sp.bool]) 
 self.data.votes = sp.cast(
                sp.big_map(), sp.big_map[sp.bytes, sp.set[sp.address]] 
 ) 
 self.data.members = sp.cast(membros, sp.set[sp.endereço])
            self.data.required_votes = sp.cast(votos_obrigatórios, sp.nat) 

 @sp.entrypoint 
 def submit_proposal(self, bytes): 
 """Envie uma nova proposta para votação.

            Apresentar uma proposta não implica votar a favor da mesma.

            Args: 
 byte (sp.bytes): bytes propostos para votação.
            Aumenta: 
 `Você não é um membro` 
 """ 
 assert self.data.members.contains(sp.sender), "Você não é membro" 
 self.data.proposals[bytes] = Falso 
 self.data.votes[bytes] = sp.set()

        @sp.entrypoint 
 def vote_proposal(self, bytes): 
 """Vote em uma proposta.

            Não há voto contra ou aprovação. Se alguém discordar de uma proposta 
 poderá evitar votar. Atenção: propostas antigas não votadas nunca se tornam 
 .

            Args: 
 id (sp.bytes): bytes da proposta.
            Aumenta: 
 `Você não é membro`, `Proposta não encontrada` 
 """ 
 assert self.data.members.contains(sp.sender), "Você não é membro" 
 afirmação self.data.proposals.contains(bytes), "Proposta não encontrada" 
 self.data.votes[bytes].add(sp.sender)
            se sp.len(self.dados.votos[bytes]) >= self.data.required_votes:
                self.data.proposals[bytes] = True 

 @sp.onchain_view() 
 def is_voted(self, id): 
 """Retorna um booleano indicando se a proposta foi votada.

            Args: 
 id (sp.bytes): bytes da proposta 
 Retorno: 
 (sp.bool): Verdadeiro se a proposta foi votada; caso contrário, falso.
            """ 
 retorna self.data.proposals.get(id, error="Proposta não encontrada") 


 se "modelos" não estiverem em __name__: 

 @sp.add_test(name="Cenário básico MultisigView", is_default=True) 
 def basic_scenario(): 
 """Um cenário com uma votação sobre o contrato

        Testes: 
 - Originação 
 - Envio da proposta 
 - Votação da proposta 
 """ 
 sc = sp.test_scenario(main)
        sc.h1("Cenário básico.")

        membro1 = sp.test_account("membro1")
        membro2 = sp.test_account("membro2")
        membro3 = sp.test_account("membro3")
        membros = sp.set([membro1.endereço, membro2.endereço, membro3.endereço])

        sc.h2("Origem") 
 c1 = main.MultisigView(membros, 2) 
 sc += c1 

 sc.h2("submit_proposal") 
 c1.submit_proposal(sp.bytes("0x42")).run( remetente=membro1) 

 sc.h2("vote_proposal") 
 c1.vote_proposal(sp.bytes("0x42")).run(sender=member1) 
 c1.vote_proposal(sp.bytes("0x42")).run (sender=member2) 

 # Podemos verificar se a proposta foi validada.
        sc.verify(c1.is_voted(sp.bytes("0x42")))

Cada contrato fornece um mecanismo diferente para obter controle de múltiplas assinaturas, oferecendo flexibilidade dependendo das necessidades específicas do seu caso de uso de blockchain.

Guia passo a passo para experimentar o contrato Multisig no SmartPy Online

Para experimentar os contratos multisig que escrevemos no SmartPy, você pode seguir estas etapas:

  1. Acesse o IDE SmartPy em https://smartpy.io/ide.

  2. Cole o código do contrato no editor. Você pode substituir o código existente.

  3. Para executar o contrato, clique no botão “Executar” localizado no painel superior.

  4. Após executar o contrato, você pode visualizar a execução do cenário no painel “Output” à direita. Aqui você pode ver detalhes de cada ação, incluindo propostas, votações e aprovações.

  5. Para implantar seu contrato na rede Tezos, primeiro você precisa compilá-lo. Clique no botão “Compile” no painel superior.

  6. Após a compilação, você pode implantar o contrato na testnet clicando em “Implementar Contrato Michelson”. Você precisará fornecer uma chave secreta para uma conta Tezos com fundos suficientes para pagar os custos de implantação do gás.

  7. Assim que o contrato for implantado, você receberá o endereço do contrato no blockchain. Você pode usar esse endereço para interagir com o contrato por meio de transações.

  8. Para enviar propostas ou votar nos contratos, você pode utilizar os pontos de entrada definidos no código do contrato, como submit_proposal ou vote_proposal. Eles podem ser chamados diretamente das transações que você cria.

Lembre-se, embora o IDE SmartPy permita testar seu contrato em um blockchain simulado, a implantação do contrato na rede Tezos real incorrerá em custos de gás, que devem ser pagos em XTZ, a criptomoeda nativa da rede Tezos.

免责声明
* 投资有风险,入市须谨慎。本课程不作为投资理财建议。
* 本课程由入驻Gate Learn的作者创作,观点仅代表作者本人,绝不代表Gate Learn赞同其观点或证实其描述。