A Move nasceu nos estágios iniciais do projeto Libra em 2018 - os dois fundadores da Mysten (Evan e eu) também fomos a equipe fundadora da Libra. Antes de decidirmos criar uma nova linguagem, a equipe inicial da Libra estudou intensamente os casos de uso e linguagens de contratos inteligentes existentes para entender o que os desenvolvedores queriam fazer e onde as linguagens existentes não estavam funcionando. A principal questão que descobrimos é que os contratos inteligentes tratam de ativos e controle de acesso, mas as primeiras linguagens de contratos inteligentes não tinham representação de tipo/valor para ambos. Nossa hipótese é que, se fornecermos abstrações de primeira classe para esses conceitos-chave, poderemos melhorar muito a segurança dos contratos inteligentes e a produtividade dos programadores de contratos inteligentes - ter o vocabulário certo para a tarefa em questão pode fazer toda a diferença. Ao longo dos anos, muitas pessoas contribuíram para o design e implementação do Move à medida que a linguagem evoluiu de uma ideia-chave para uma linguagem de contrato inteligente independente de plataforma, com o objetivo audacioso de se tornar o "JavaScript da web3".

Hoje, temos o prazer de anunciar um marco na integração do Move e do Sui. A funcionalidade do Sui Move é completa, suportada por ferramentas avançadas e possui extensa documentação e exemplos, incluindo as seguintes seções:

Uma série de tutoriais sobre programação usando objetos Sui Move Um documento de desenvolvimento sobre o conhecimento básico, padrões de design e amostras de Sui Move Um plug-in de aprimoramento VSCode desenvolvido pela equipe Mysten Move, que oferece suporte à análise de código e diagnóstico de erros e integra. a construção, teste e gerenciamento de pacotes do Move, geração de documentação e validador Move integrado ao sui CLI.

O que torna o Move único

Move é uma linguagem incorporada multiplataforma. A sintaxe central em si é muito simples: possui conceitos comuns, como estruturas, números inteiros e endereços, mas não possui conceitos específicos de blockchain, como contas e transações, tempo, criptografia, etc. Essas funções devem ser fornecidas pela plataforma blockchain integrada ao Move. É importante ressaltar que esses blockchains não exigem seus próprios forks Move - cada plataforma usa a mesma máquina virtual Move, validador de bytecode, compilador, validador, gerenciador de pacotes e CLI, mas construindo o código sobre esses componentes principais para adicionar funcionalidade específica do blockchain . Diem foi o primeiro blockchain a incorporar o Move, e os blockchains subsequentes baseados em Move (incluindo 0L, StarCoin e Aptos) adotaram principalmente uma abordagem no estilo Diem. Embora o Diem-style Move tenha algumas grandes qualidades, tanto a natureza permitida do Diem quanto certos detalhes de implementação do blockchain Diem (especialmente o modelo de armazenamento) tornam alguns casos básicos de uso de contratos inteligentes difíceis de implementar. Em particular, os designs originais de Move and Diem são anteriores à explosão na popularidade dos NFTs e têm algumas peculiaridades que tornam a implementação de casos de uso relacionados a NFT particularmente complicada. Nesta postagem, demonstraremos os problemas com a incorporação do Move no estilo Diem original por meio de três exemplos e descreveremos como resolvemos esse problema no Sui Move. Assumimos algum conhecimento básico do Move, mas esperamos que esses pontos-chave sejam compreensíveis para qualquer pessoa com experiência em programação.

Experiência suave ao criar ativos em escala

A capacidade de criar e distribuir ativos em massa é fundamental para a integração e o envolvimento dos usuários da web3. Talvez um streamer do Twitch queira distribuir NFTs comemorativos, um criador queira enviar ingressos para um evento especial ou um desenvolvedor de jogos queira lançar novos itens para todos os jogadores. Aqui está uma tentativa (fracassada) de escrever código para cunhagem em massa de ativos no estilo Diem Move. Este código toma como entrada um vetor de endereços de destinatários, gera um ativo para cada destinatário e tenta transferir o ativo.

Em uma mudança no estilo Diem, o armazenamento global é digitado por pares (endereço, nome do tipo) - ou seja, cada endereço pode armazenar no máximo um ativo de um tipo específico. Portanto, move_to(receiverient, CoolAsset { ...} tenta mover o CoolAsset armazenando-o no endereço do receptor. No entanto, esse código falha ao compilar na linha move_to(receiverient, ...). A questão principal é, em um movimento estilo Diem, você não pode enviar um valor do tipo CoolAsset para um endereço A, a menos que uma transação seja enviada de um endereço diferente de A, e o proprietário de uma conta criada em A envie uma transação e escolha explicitamente receber CoolAsset. tipo de objeto. São duas transações, apenas para receber um ativo. A decisão de fazer isso faz sentido para o Diem, que é um sistema autorizado que precisa limitar cuidadosamente a criação de contas e evitar que as contas sejam limitadas pelo sistema de armazenamento. Em vez de manter muitos ativos, no entanto, para um sistema aberto que deseja usar a alocação de ativos como um mecanismo de integração, ou apenas permitir que os ativos fluam livremente entre os usuários, como fazem no Ethereum e em blockchains semelhantes. .

O código para implementar a mesma função no Sui Move é o seguinte:

O armazenamento global do Sui Move é codificado por ID de objeto. Toda estrutura com pares chave-valor é um "objeto Sui", que deve ter um campo de identificação globalmente exclusivo. Sui Move introduz uma primitiva de transferência que pode ser usada em qualquer objeto Sui, em vez de usar a estrutura restritiva move_to. Nos bastidores, esta primitiva mapeia o id para um CoolAsset no armazenamento global e adiciona metadados para indicar que o valor pertence ao destinatário. Uma propriedade interessante da versão Sui do mass_mint é que ela é trocada com todas as outras transações (incluindo outras transações que chamam mass_mint!). O tempo de execução Sui perceberá isso e enviará transações chamando essa função por meio do "caminho rápido" de transmissão de consenso bizantino que não requer consenso. Essas transações podem ser confirmadas ou executadas em paralelo. Isso não requer nenhum esforço por parte do programador (eles apenas escrevem o código acima e o tempo de execução cuida do resto). Talvez sutilmente, este não é o caso da variante Diem deste código - embora o código acima funcione, existe e as chamadas guid::create criarão pontos de discórdia com outras transações que geram GUIDs ou tocam em recursos da conta. Em alguns casos, é possível reescrever o código do Move no estilo Diem para evitar o argumento, mas muitas das formas convencionais de escrever o Move no estilo Diem introduzem pequenos obstáculos que dificultam a execução paralela.

Propriedade e transferências de ativos locais

Vamos estender o código Move estilo Diem com uma solução alternativa que realmente compila e executa. A maneira habitual de fazer isso é o "padrão wrapper": como Bob não pode mover o CoolAsset diretamente para o endereço de Alice, pedimos a Alice que "aceite" receber o CoolAsset, primeiro publicando um tipo de wrapper CoolAssetStore, que contém um tipo Collection (mesa). Alice pode fazer isso chamando a função opt_in. Em seguida, adicionamos código que permite a Bob mover o CoolAsset de seu CoolAssetStore para o CoolAssetStore de Alice. Neste código, vamos adicionar uma ruga adicional: só permitiremos transferências de CoolAssets se eles forem criados há pelo menos 30 dias. Esse tipo de política é importante para criadores que (por exemplo) desejam impedir que especuladores comprem/exagerem ingressos para eventos, para que seja mais fácil para fãs reais conseguirem esses ingressos a um preço razoável.

Este código é válido. Mas esta é uma maneira bastante complicada de realizar a tarefa de transferir ativos de Alice para Bob! Vamos dar uma olhada em outra implementação do Sui Move

Este código é muito mais curto. O principal a ser observado aqui é que cool_transfer é uma função de entrada (o que significa que pode ser chamada diretamente pelo tempo de execução Sui por meio de uma transação), porém possui um parâmetro do tipo CoolAsset como entrada. Esta é a mágica do Sui Runtime novamente. Uma transação inclui um conjunto de IDs de objetos nos quais deseja operar e quando o Sui Runtime:

Analise o ID em um valor de objeto (não há necessidade das partes emprestado_global_mut e table_remove no código estilo Diem acima). Verifica se o objeto pertence ao remetente da transação (elimina a necessidade da seção signer::address_of e do código relacionado acima). Esta parte é particularmente interessante e iremos explicá-la em breve: No Sui, a verificação segura da propriedade do objeto faz parte do tempo de execução. Verifique o tipo do valor do objeto de acordo com o tipo de parâmetro da função chamada cool_transfer. para os parâmetros cool_transfer e chame a função.

Isso permitiu que os programadores do Sui Move pulassem o modelo da parte “Retirada” da lógica e pulassem diretamente para a parte divertida: verificar a política de expiração de 30 dias. Da mesma forma, a parte do “depósito” também é bastante simplificada com a estrutura de transferência do Sui Move explicada acima. Finalmente, não há necessidade de introduzir um tipo de wrapper com uma coleção interna como CoolAssetStore - o armazenamento global Sui indexado por id permite que um endereço armazene qualquer número de valores de um determinado tipo. Outra diferença a ser destacada é que o cool_transfer do estilo Diem tem 5 maneiras de abortar (ou seja, falha e cobra o gás do usuário sem concluir a transferência), enquanto o cool_transfer do Sui Move tem apenas 1 maneira de abortar: quando violado a política de 30 dias. Transferir as verificações de propriedade de objetos para o tempo de execução é uma grande vitória, não apenas em termos de ergonomia, mas também em termos de segurança. Implementar segurança no nível de tempo de execução evita erros na implementação dessas verificações na construção (ou esquecê-las completamente!). Por fim, observe que a assinatura da função de ponto de entrada cool_transfer(asset: CoolAsset, ...) do Sui Move nos fornece muitas informações sobre o que essa função fará (é mais opaca do que as assinaturas de função no estilo Diem). Podemos pensar que esta função está solicitando permissão para transferir CoolAsset, enquanto outra função f(asset: &mut CoolAsset, ...) está solicitando permissão para escrever (mas não transferir) CoolAsset, e g(asset: &CoolAsset, . ..) está apenas pedindo permissão de leitura.

Como essas informações estão disponíveis diretamente na assinatura da função (sem necessidade de execução ou análise estática!), elas podem ser usadas diretamente por carteiras e outras ferramentas de cliente. Na Sui Wallet, estamos trabalhando em solicitações de assinatura legíveis por humanos, aproveitando essas assinaturas de funções estruturadas para fornecer solicitações de permissão no estilo iOS/Android aos usuários. A carteira pode dizer algo como: "Esta transação requer permissão para ler seu CoolAsset, escrever em seu AssetCollection e transferir seu ConcertTicket. Continuar?".

As solicitações de assinatura legíveis por humanos resolvem um vetor de ataque em grande escala presente em muitas plataformas existentes, incluindo aquelas que usam o Move! estilo Diem, onde os usuários de carteiras devem assinar transações cegamente, sem compreender o impacto que elas podem ter. Acreditamos que tornar a experiência da carteira menos arriscada é um passo crítico na promoção da adoção generalizada de carteiras de criptomoedas e projetamos o Sui Move para apoiar esse objetivo, permitindo recursos como solicitações de assinatura legíveis por humanos.

Agrupar ativos diferentes

Finalmente, vamos considerar um exemplo de agrupamento de diferentes tipos de ativos. Este é um caso de uso bastante comum: os programadores podem querer empacotar diferentes tipos de NFTs em uma coleção, agrupar itens para vender em um mercado ou adicionar anexos a itens existentes. Suponha que tenhamos a seguinte situação:

Alice define um objeto de personagem para uso no jogo. Alice deseja apoiar a decoração de seu personagem com diferentes tipos de acessórios de terceiros que serão criados posteriormente. Qualquer um deve ser capaz de criar um acessório, mas o dono do personagem deve decidir se deseja adicionar um acessório. A transferência de um personagem deve transferir automaticamente todos os seus acessórios.

Desta vez, vamos começar com o código do Sui Move. Aproveitaremos outro aspecto da funcionalidade de propriedade de objeto incorporada ao tempo de execução Sui: um objeto pode pertencer a outro objeto. Cada objeto possui um proprietário único, mas um objeto pai pode ter qualquer número de objetos filhos. O relacionamento objeto pai/filho é criado usando a função transfer_to_object, que é o objeto associado da função de transferência apresentada acima.

Neste código, o módulo role inclui uma função accessorize que permite ao proprietário da função adicionar um objeto acessório de qualquer tipo como objeto filho. Isso permite que Bob e Clarissa criem seus próprios tipos de ornamentos com propriedades e funcionalidades diferentes que Alice não possui, mas baseiam-se no que Alice já fez. Por exemplo, a camisa de Bob só pode ser equipada se for da cor favorita do personagem, e a espada de Clarissa só pode ser empunhada se o personagem for poderoso o suficiente. A seguir está uma demonstração prática do Move estilo Diem, mas nenhum deles teve sucesso, ou seja, o Move estilo Diem não consegue atingir o cenário acima.

Pode-se ver que os problemas no movimento estilo Diem são os seguintes

Apenas coleções do mesmo tipo são suportadas (como mostrou o primeiro teste), mas acessórios não são fundamentalmente um tipo de objeto. A relação entre objetos só pode ser alcançada através de "embrulho" (ou seja, armazenamento de um objeto dentro de outro objeto). a coleção de objetos que podem ser agrupados deve ser predefinida (como na segunda tentativa) ou adicionada de maneira temporária, e a composição adicional de objetos não é suportada (como no terceiro teste).

Resumir

Sui é a primeira plataforma a se desviar significativamente do design original do Diem na forma como o Move é usado. Projetar uma combinação que aproveite ao máximo o Move e os recursos exclusivos da plataforma é uma arte e uma ciência, exigindo um conhecimento profundo da linguagem Move e dos recursos do blockchain subjacente. Estamos muito entusiasmados com o progresso que o Sui Move está fazendo e com os novos casos de uso que ele possibilitará! ". [1] Outro argumento a favor de uma política Move no estilo Diem é que "você deve aceitar antes de receber um tipo de ativo específico". Este é um bom mecanismo para evitar spam. No entanto, pensamos na prevenção de spam como pertencente à camada de aplicação, em vez de exigir que os usuários enviem transações que custam dinheiro real para aceitar o recebimento de ativos, o spam pode ser facilmente abordado (por exemplo) no nível da carteira com políticas ricas definidas pelo usuário e filtros de spam automatizados. .