Esse artigo é uma tradução do Solana Labs feita por Fatima Lima. Você pode encontrar o original aqui.
Como contratos funcionam na blockchain com Prova de Histórico da Solana.
Principais Características
- Bytecode de alto desempenho projetado para verificação e compilação rápida para código nativo
- Gerenciamento de memória projetado para análise rápida de dependências de dados
- Execução de contratos inteligentes paralelizáveis em tantos núcleos quantos o sistema possa fornecer
Nossa abordagem para a execução de contratos inteligentes é baseada em como os sistemas operacionais carregam e executam código dinâmico no kernel.
Na Figura 1. um cliente não confiável, ou Espaço do usuário nos termos dos Sistemas Operacionais, cria um programa na linguagem front-end de sua escolha, (como C/C++/Rust/Lua), e o compila com LLVM para o objeto Solana Bytecode. Este arquivo objeto é um ELF padrão.
O trabalho computacionalmente caro de converter linguagens frontend em programas é feito localmente pelo cliente.
- Frontend para LLVM recebe um programa fornecido pelo usuário em uma linguagem de nível superior, como C/C++/Rust/Lua, ou é chamado como uma biblioteca de Javascript ou qualquer outra linguagem
- A cadeia de ferramentas LLVM faz o trabalho real de converter o programa para um ELF.
A saída é um ELF com um bytecode específico, projetado para verificação rápida e conversão para o conjunto de instruções da máquina local em que a Solana está rodando.
Do lado do kernel, o ELF é verificado, carregado e executado.
- O verificador examina se o bytecode é válido e seguro para execução e o converte no conjunto de instruções da máquina local.
- O carregador prepara a memória necessária para carregar o código e marcar o segmento como executável.
- O tempo de execução na verdade chama o programa com argumentos e gerencia as mudanças na máquina virtual.
Bytecode Solana
A maior parte do foco no desempenho de contratos inteligentes tem sido a mudança para WASM. O bytecode não importa! Embora nosso bytecode seja baseado no Berkley Packet Filter, podemos usar qualquer coisa que seja fácil para JIT para x86 (ou SPIRV!). A razão pela qual estamos baseando nosso bytecode em BPF é porque o que o kernel faz com código não confiável coincide quase exatamente com os requisitos que temos:
- Tempo determinístico para executar o código
- Bytecode portátil entre conjuntos de instruções de máquina
- Acessos de memória verificados
- Tempo determinístico e curto para carregar o objeto e verificar o bytecode e JIT para o conjunto de instruções da máquina local
Queremos a maneira mais simples, rápida e fácil de verificar o conjunto de instruções.
O gerenciamento de memória é o que realmente importa
Esta é a parte mais crítica do desempenho do mecanismo. Se todos os contratos programados não tiverem dependências de dados, todos podem ser executados simultaneamente. Se tivermos sucesso, isso significa que o desempenho do mecanismo será dimensionado com o número de núcleos disponíveis para executar os contratos. A produção dobrará a cada 2 anos com a lei de Moores.
O gerenciamento de Memória começa com o próprio ELF. Inicialmente, estamos restringindo os contratos a um único código e segmento de dados somente leitura. Isso significa que é composto de código executável somente leitura e dados somente leitura — nenhuma variável global mutável ou variável estática mutável. Podemos flexibilizar esse requisito no futuro, mas por agora, ele fornece uma solução simples para o requisito 4 acima.
Uma vez que os contratos inteligentes em si não têm estado, como você realmente administra o estado do contrato? Nosso tempo de execução fornecerá uma interface para a criação de estado. Essa interface é chamada por meio de uma transação, como qualquer outro método de contrato.
Alocar Memória
Se a chave pública page_address não tiver nenhuma região de memória atual associada a ela, a região é alocada e a memória alocada é definida como 0.
Se a chave pública page_address estiver sem atribuição, atribua-a à chave pública contract. O único código que pode modificar a memória que é atribuída a ele é o código que é fornecido pelo contract. Assim, todas as transições de estado nessa memória foram feitas pelo contrato.
Se qualquer dessas condições falham, a chamada falha. Essa interface é chamada com uma simples transação.
Execução
A estrutura de chamada é nossa transação básica. Essa estrutura descreve o contexto das transações:
As duas partes mais importantes dessa estrutura para o contrato são keys e user_data _. O tempo de execução traduz as _keys para os locais de memória associados a elas com a memória que foi criada com allocate_memory. Os user_data são bits de memória randomizados que foram fornecidos pelo usuário para a call. É assim que os usuários podem adicionar um estado externo dinâmico ao tempo de execução.
Contratos inteligentes também podem examinar o vetor required_sigs para verificar quais das ChavesPúblicas na call têm uma assinatura válida. No momento em que a Call chega no método dos contratos,as assinaturas já foram verificadas.
Regras
Um contrato implementa a seguinte interface:
Com a seguinte definição para Page:
Desde que o chamador possa pagar a fee para executar o contrato, ele será chamado. Mas, o tempo de execução somente armazenará a Page modificada se uma destas regras forem atendidas:
- Para páginas atribuídas ao contrato, a soma total dos tokens nessas páginas não pode aumentar.
- Para páginas não atribuídas ao contrato, o saldo individual de cada página não pode diminuir.
- Para páginas não atribuídas ao contrato, a memória não pode mudar.
- A soma total dos tokens em todas as páginas não pode mudar.
Assim, um contrato pode mover saldos livremente em qualquer uma das páginas que lhe foram atribuídas. Ele pode modificar a memória nessas páginas livremente. Para páginas atribuídas a outros contratos, o contrato pode adicionar tokens a páginas individuais e pode ler seus vetores de memória.
Sinais
Sinais são chamadas assíncronas, mas garantidas. Eles são criados assim como as páginas alocadas e atribuídas a um contrato. A memória que pertence à página do sinal armazena uma estrutura _Call _ que pode ser examinada pelo tempo de execução.
create_signal(page_address: PublicKey, contract: PublicKey)
Um sinal é uma forma do contrato se agendar ou chamar outros contratos no futuro. Assim que um sinal é definido, o tempo de execução garante sua eventual execução.
Um cliente pode chamar um contrato com qualquer número de sinais, o que pode modificar a memory do sinal e construir qualquer método assíncrono que o contrato precise chamar, incluindo allocate_memory ou create_signal
Notas
- Um contrato não pode alocar memória de forma síncrona. O cliente primeiro faz uma transação para allocate_memory, e em seguida, chama o método de contrato com a página que possui alguma memória alocada para ela.
- Um contrato pode agendar um sinal para alocar memória e chamar a si mesmo no futuro.
- O tempo de execução pode executar, em paralelo, todas as chamadas de contratos não sobrepostos. Nossos resultados preliminares mostram que mais de 500.000 chamadas por segundo são possíveis - com espaço para otimização.
Próximos Passos
Estamos prestes a fundir os detalhes básicos da parte de gerenciamento de memória do mecanismo de contratos inteligentes. O que se seguirá imediatamente é SDK, sinais, JIT, carregador, conjunto de ferramentas.Mas há muito mais! Essas primitivas básicas podem ser usadas para implementar todos os recursos regulares dos sistemas operacionais.
- allocate_memory pode ser usado para criar stack frames para threads, e segmentos graváveis para processos elfs com estado persistente.
- Sinais podem ser usados como um trampolim do processo de execução para um serviço OS que faz alocação de memória dinâmica, cria threads adicionais e cria outros sinais.
Essa estrutura pode, eventualmente, oferecer suporte a todos os sistemas operacionais primitivos de que você desfruta em um sistema operacional moderno, como chamadas síncronas simples, sem comprometer o desempenho.
Se implementar um JIT ou um vinculador dinâmico em Rust parece divertido, entre em contato comigo pelo e-mail [email protected].
O que mais aprender sobre Solana?
Confira o que temos em Github e encontre maneiras de se conectar em nossa página da community.
Solana
Agradecimentos a Rob Walker, Greg Fitzgerald, Shivani Bhargava e Michael Vines.
Oldest comments (0)