🇵🇹
DAO Lunes Labs - PT
  • 👋Seja Bem-vindo a DAO Lunes Labs
  • Overview
    • 🚪Introdução
    • 🚀Manifesto DAO Lunes Labs
    • 🍕Tokenomics
    • 👑Proposta de Valor
    • 🧑‍🚀Comunidade e Participação
    • 🗺️Roadmap
    • 💼Compromisso com Ética e Responsabilidade
    • 👾Programa de Bug Bounty: Lunes Security Initiative (LSI)
  • Developers
    • 💽Para Nodes
      • 🥾Instalar Node
        • 🏗️Rust toolchain
        • 🐧Linux
    • 🖥️Para Desenvolvedores
      • 📑Smart Contract Ink! 4.x
        • ✨Configuração
          • 📐Criando um projeto com ink!
          • 🖥️Compile Seu Contrato
          • ⛓️Executar um nó Lunes
          • ⬆️Implante seu contrato
          • 🔛Chame Seu Contrato
          • 🛠️Solução de problemas
        • 🪄Fundamentos
          • 👾Modelo de Contrato
          • 👨‍🚀Armazenando Valores
          • 🔭Lendo Valores do Armazenamento
          • 🦸‍♂️Alterando os Valores de Armazenamento
          • 🎡Eventos
          • 👨‍🔧Seletores
          • 🪶Definições de Trait
          • 🗣️Chamadas entre Contratos (Cross-Contract Calls)
          • 🦸Contratos Atualizáveis
          • 🤺Funções do Ambiente
          • 🏟️Tipos de Ambiente de Cadeia
          • 💠Metadados
          • 🧪Testes de Contrato
          • 🕵️‍♂️Depuração de Contratos
          • 🔬Verificação de Contrato
        • 🎙️Macros e Atributos
          • 📇#[ink::contract]
          • 👽#[ink(anonymous)]
          • 👷#[ink(constructor)]
          • 📏#[ink(default)]
          • 🎢#[ink(event)]
          • 🛩️#[ink(impl)]
          • 📧#[ink(message)]
          • 👨‍💼#[ink(namespace = "…")]
          • 💸#[ink(payable)]
          • ⚡#[ink(selector = S:u32)]
          • 💽#[ink(storage)]
          • 💣#[ink(topic)]
          • ⛓️#[ink::chain_extension]
        • 💽Storege & Data Structires
          • Working with Mapping
          • Storage Layout
          • Custom Data Structures
          • Metadata Format
        • 👾Frontend Development
          • Getting Started
          • Connect Wallet
          • Hooks
            • All Hooks
            • Contracts
              • useCall
              • useCallSubscription
              • useContract
              • useDryRun
              • useEventSubscription
              • useEvents
              • useTx
              • useTxPaymentInfo
            • Wallets
              • useWallet
              • useAllWallets
              • useInstalledWallets
              • useUninstalledWallets
            • API
              • useApi
              • useBalance
              • useBlockHeader
          • Configuration
          • useink / core
            • Contracts
              • Call
              • decodeCallResult
              • decodeError
              • getRegistryError
              • toAbiMessage
          • useink / chains
            • Getting Started
            • Chain Configurations
            • ChainId
          • useink / notifications
            • Getting Started
            • Configuration
            • useNotifications
            • toNotificationLevel
          • useink / utils
            • Getting Started
            • Pick Helpers
            • tx Helpers
            • Types
        • 💡Examples
          • 📔Smart Contracts
          • 📱Dapps
        • 🛠️Tools
          • 🖌️OpenBrush
      • 📒Smart Contract - EVM
        • Create ERC-20 Ink Token!
      • 💰Desenvolvendo uma Wallet Lunes
        • 👾Transações de Tokens PSP22
    • 🎨Para Designers
      • 🖌️Brand Lunes
Powered by GitBook
On this page
  • Estrutura​
  • Uso​
  • Atributos​
  • Detalhes: handle_status​
  • Uso: handle_status + Tipo de retornoResult<T, E> ​
  • Combinações​
  • Código de Erro​
  • Example: Definition​
  • Example: Environment​
  • Example: Usage​
  • Technical Limitations​

Was this helpful?

  1. Developers
  2. Para Desenvolvedores
  3. Smart Contract Ink! 4.x
  4. Macros e Atributos

#[ink::chain_extension]

Last updated 1 year ago

Was this helpful?

Na configuração padrão do contracts-pallet, um contrato inteligente só pode interagir com a runtime por meio de sua interface básica bem definida. Essa API já permite uma variedade de interações entre o contracts-pallet e o contrato inteligente em execução. Por exemplo, é possível chamar e instanciar outros contratos inteligentes na mesma cadeia, emitir eventos, consultar informações de contexto ou executar procedimentos de hash criptográfico incorporados.

Se esse conjunto básico de recursos não for suficiente para atender às necessidades de uma determinada blockchain construída com Substrate, é possível estender facilmente essa API usando o recurso chamado chain extension.

Com as chain extensions, você pode expor partes da lógica da sua runtime aos desenvolvedores de contratos inteligentes.

A interface consiste em um código de erro que indica erros leves, bem como a definição de alguns métodos de chain extension.

A estrutura geral segue a de uma simples definição de trait em Rust. O código de erro é definido como uma definição de tipo associado da definição do trait. Os métodos são definidos como métodos associados do trait sem implementação.

Os métodos de chain extension não devem ter um receptor self, como &self ou &mut self, e devem ter entradas e saída que implementam o codec SCALE. O valor de retorno segue regras específicas que podem ser alteradas usando o atributo handle_status e a alternância entre tipos Result e Non-Result, que são descritos com mais detalhes abaixo.

Normalmente, a definição de chain extension usando essa macro é fornecida pelo autor da chain extension em uma crate separada. Contratos inteligentes ink! que utilizam essa chain extension simplesmente dependem dessa crate e usam sua definição de ambiente associada para utilizar os métodos fornecidos pela chain extension.

Existem dois atributos diferentes com os quais os métodos de chain extension podem ser marcados:

Atributo
Obrigatório
Valor Padrão
Description

ink(extension = N: u32)

Yes

-

Determines the unique function ID of the chain extension method.

ink(handle_status = flag: bool)

Optional

true

Assumes that the returned status code of the chain extension method always indicates success and therefore always loads and decodes the output buffer of the call.

Como acontece com todos os atributos do ink!, vários deles podem aparecer em uma lista contígua:

type Access = i32;

#[ink::chain_extension]
pub trait MyChainExtension {
    type ErrorCode = i32;

    #[ink(extension = 5, handle_status = false)]
    fn key_access_for_account(key: &[u8], account: &[u8]) -> Access;
}

...ou como vários atributos individuais do ink! aplicados ao mesmo item:

type Access = i32;

#[ink::chain_extension]
pub trait MyChainExtension {
  type ErrorCode = i32;

  #[ink(extension = 5)]
  #[ink(handle_status = false)]
  fn key_access_for_account(key: &[u8], account: &[u8]) -> Access;
}

Valor padrão: true

Por padrão, todos os métodos de chain extension devem retornar um Result<T, E> onde E: From<Self::ErrorCode>. O Self::ErrorCode representa o código de erro da chain extension. Isso significa que um contrato inteligente que chama um método de chain extension primeiro consulta o código de status retornado pelo método de chain extension e só carrega e decodifica a saída se o código de status indicar uma chamada bem-sucedida. Essa abordagem foi escolhida por ser mais eficiente quando nenhuma saída além do código de erro é necessária para uma chamada de chain extension. Ao projetar uma chain extension, tente utilizar o código de erro para retornar erros e use o buffer de saída apenas para informações que não se encaixam em um único valor u32.

Um método de chain extension que é marcado com handle_status = false assume que o código de erro retornado sempre indicará sucesso. Portanto, ele sempre carregará e decodificará o buffer de saída e perderá a restrição E: From<Self::ErrorCode para a chamada.

Observe que se um método de chain extension não retornar Result<T, E> onde E: From<Self::ErrorCode>, mas handle_status = true, ele ainda retornará um valor do tipo Result<T, Self::ErrorCode>.

Utilize tanto handle_status = false quanto um tipo de retorno non-Result para o mesmo método de chain extension se uma chamada a ele nunca pode falhar e nunca retorna um tipo Result.

Devido à possibilidade de marcar um método de chain extension comhandle_status e a escolha entre (1) retorna apenas Result<T, E> ou (2) retornar apenas T, existem 4 casos diferentes com semânticas ligeiramente distintas:

handle_status

Returns Result<T, E>

Effects

true

true

O método de extensão de cadeia é obrigado a retornar um valor do tipoResult<T, E> onde E: From<Self::ErrorCode>. Uma chamada sempre verificará se o código de status retornado indica sucesso e somente então carregará e decodificará o valor no buffer de saída.

true

false

O método de extensão de cadeia pode retornar qualquer tipo non-Result. Uma chamada sempre verificará se o código de status retornado indica sucesso e somente então carregará e decodificará o valor no buffer de saída. O tipo de retorno real do método de extensão de cadeia ainda é Result<T, Self::ErrorCode> quando o método de extensão de cadeia foi definido para retornar um valor do tipo T.

false

true

O método de extensão de cadeia deve retornar um valor do tipo Result<T, E>. Uma chamada sempre assume que o código de status retornado indica sucesso e, portanto, sempre carrega e decodifica o buffer de saída diretamente.

false

false

O método de extensão de cadeia pode retornar qualquer tipo non-Result. Uma chamada sempre assume que o código de status retornado indica sucesso e, portanto, sempre carrega e decodifica diretamente o buffer de saída.

Cada chain extension define exatamente um ErrorCode usando a seguinte sintaxe:

#[ink::chain_extension]
pub trait MyChainExtension {
    type ErrorCode = MyErrorCode;

    // more definitions ...
}

The defined ErrorCode must implement FromStatusCode which should be implemented as a more or less trivial conversion from the u32 status code to a Result<(), Self::ErrorCode>. The Ok(()) value indicates that the call to the chain extension method was successful.

By convention an error code of 0 represents success. However, chain extension authors may use whatever suits their needs.

In the below example a chain extension is defined that allows its users to read and write from and to the runtime storage using access privileges:

/// Custom chain extension to read to and write from the runtime.
#[ink::chain_extension]
pub trait RuntimeReadWrite {
    type ErrorCode = ReadWriteErrorCode;

    /// Reads from runtime storage.
    ///
    /// # Note
    ///
    /// Actually returns a value of type `Result<Vec<u8>, Self::ErrorCode>`.
    /// #[ink(extension = 1, returns_result = false)]
    /// fn read(key: &[u8]) -> Vec<u8>;
    ///
    /// Reads from runtime storage.
    ///
    /// Returns the number of bytes read and up to 32 bytes of the
    /// read value. Unused bytes in the output are set to 0.
    ///
    /// # Errors
    ///
    /// If the runtime storage cell stores a value that requires more than
    /// 32 bytes.
    ///
    /// # Note
    ///
    /// This requires `ReadWriteError` to implement `From<ReadWriteErrorCode>`
    /// and may potentially return any `Self::ErrorCode` through its return value.
    #[ink(extension = 2)]
    fn read_small(key: &[u8]) -> Result<(u32, [u8; 32]), ReadWriteError>;

    /// Writes into runtime storage.
    ///
    /// # Note
    ///
    /// Actually returns a value of type `Result<(), Self::ErrorCode>`.
    #[ink(extension = 3)]
    fn write(key: &[u8], value: &[u8]);

    /// Returns the access allowed for the key for the caller.
    ///
    /// # Note
    ///
    /// Assumes to never fail the call and therefore always returns `Option<Access>`.
    #[ink(extension = 4, handle_status = false)]
    fn access(key: &[u8]) -> Option<Access>;

    /// Unlocks previously acquired permission to access key.
    ///
    /// # Errors
    ///
    /// If the permission was not granted.
    ///
    /// # Note
    ///
    /// Assumes the call to never fail and therefore does _NOT_ require `UnlockAccessError`
    /// to implement `From<Self::ErrorCode>` as in the `read_small` method above.
    #[ink(extension = 5, handle_status = false)]
    fn unlock_access(key: &[u8], access: Access) -> Result<(), UnlockAccessError>;
}

#[derive(scale::Encode, scale::Decode, scale_info::TypeInfo)]
pub enum ReadWriteErrorCode {
  InvalidKey,
  CannotWriteToKey,
  CannotReadFromKey,
}

#[derive(scale::Encode, scale::Decode, scale_info::TypeInfo)]
pub enum ReadWriteError {
  ErrorCode(ReadWriteErrorCode),
  BufferTooSmall { required_bytes: u32 },
}

impl From<ReadWriteErrorCode> for ReadWriteError {
  fn from(error_code: ReadWriteErrorCode) -> Self {
    Self::ErrorCode(error_code)
  }
}

impl From<scale::Error> for ReadWriteError {
  fn from(_: scale::Error) -> Self {
    panic!("encountered unexpected invalid SCALE encoding")
  }
}

#[derive(scale::Encode, scale::Decode, scale_info::TypeInfo)]
pub struct UnlockAccessError {
  reason: String,
}

impl From<scale::Error> for UnlockAccessError {
  fn from(_: scale::Error) -> Self {
    panic!("encountered unexpected invalid SCALE encoding")
  }
}

#[derive(scale::Encode, scale::Decode, scale_info::TypeInfo)]
pub enum Access {
  ReadWrite,
  ReadOnly,
  WriteOnly,
}

impl ink::env::chain_extension::FromStatusCode for ReadWriteErrorCode {
  fn from_status_code(status_code: u32) -> Result<(), Self> {
    match status_code {
      0 => Ok(()),
      1 => Err(Self::InvalidKey),
      2 => Err(Self::CannotWriteToKey),
      3 => Err(Self::CannotReadFromKey),
      _ => panic!("encountered unknown status code"),
    }
  }
}

All the error types and other utility types used in the chain extension definition above are often required to implement various traits such as SCALE's Encode and Decode as well as scale-info's TypeInfo trait.

In order to allow ink! smart contracts to use the above defined chain extension it needs to be integrated into an Environment definition as shown below:

type RuntimeReadWrite = i32;

use ink::env::{Environment, DefaultEnvironment};

pub enum CustomEnvironment {}

impl Environment for CustomEnvironment {
    const MAX_EVENT_TOPICS: usize =
        <DefaultEnvironment as Environment>::MAX_EVENT_TOPICS;

    type AccountId = <DefaultEnvironment as Environment>::AccountId;
    type Balance = <DefaultEnvironment as Environment>::Balance;
    type Hash = <DefaultEnvironment as Environment>::Hash;
    type BlockNumber = <DefaultEnvironment as Environment>::BlockNumber;
    type Timestamp = <DefaultEnvironment as Environment>::Timestamp;

    type ChainExtension = RuntimeReadWrite;
}

Above we defined the CustomEnvironment which defaults to ink!'s DefaultEnvironment for all constants and types but the ChainExtension type which is assigned to our newly defined chain extension.

An ink! smart contract can use the above defined chain extension through the Environment definition defined in the last example section using the env macro parameter as shown below.

Note that chain extension methods are accessible through Self::extension() or self.extension(). For example as in Self::extension().read(..) or self.extension().read(..).

#[ink::contract(env = CustomEnvironment)]
mod read_writer {

    #[ink(storage)]
    pub struct ReadWriter {}

    impl ReadWriter {
        #[ink(constructor)]
        pub fn new() -> Self { Self {} }

        #[ink(message)]
        pub fn read(&self, key: Vec<u8>) -> Result<Vec<u8>, ReadWriteErrorCode> {
            self.env()
                .extension()
                .read(&key)
        }

        #[ink(message)]
        pub fn read_small(&self, key: Vec<u8>) -> Result<(u32, [u8; 32]), ReadWriteError> {
            self.env()
                .extension()
                .read_small(&key)
        }

        #[ink(message)]
        pub fn write(
            &self,
            key: Vec<u8>,
            value: Vec<u8>,
        ) -> Result<(), ReadWriteErrorCode> {
            self.env()
                .extension()
                .write(&key, &value)
        }

        #[ink(message)]
        pub fn access(&self, key: Vec<u8>) -> Option<Access> {
            self.env()
                .extension()
                .access(&key)
        }

        #[ink(message)]
        pub fn unlock_access(&self, key: Vec<u8>, access: Access) -> Result<(), UnlockAccessError> {
            self.env()
                .extension()
                .unlock_access(&key, access)
        }
    }

    /// Custom chain extension to read to and write from the runtime.
    #[ink::chain_extension]
    pub trait RuntimeReadWrite {
          type ErrorCode = ReadWriteErrorCode;
          #[ink(extension = 1)]
          fn read(key: &[u8]) -> Vec<u8>;
          #[ink(extension = 2)]
          fn read_small(key: &[u8]) -> Result<(u32, [u8; 32]), ReadWriteError>;
          #[ink(extension = 3)]
          fn write(key: &[u8], value: &[u8]);
          #[ink(extension = 4, handle_status = false)]
          fn access(key: &[u8]) -> Option<Access>;
          #[ink(extension = 5, handle_status = false)]
          fn unlock_access(key: &[u8], access: Access) -> Result<(), UnlockAccessError>;
    }

    #[derive(scale::Encode, scale::Decode, scale_info::TypeInfo)]
    pub enum ReadWriteErrorCode {
          InvalidKey,
          CannotWriteToKey,
          CannotReadFromKey,
    }

    #[derive(scale::Encode, scale::Decode, scale_info::TypeInfo)]
    pub enum ReadWriteError {
          ErrorCode(ReadWriteErrorCode),
          BufferTooSmall { required_bytes: u32 },
    }
    impl From<ReadWriteErrorCode> for ReadWriteError {
         fn from(error_code: ReadWriteErrorCode) -> Self {
             Self::ErrorCode(error_code)
         }
    }
    impl From<scale::Error> for ReadWriteError {
         fn from(_: scale::Error) -> Self {
             panic!("encountered unexpected invalid SCALE encoding")
         }
    }

    #[derive(scale::Encode, scale::Decode, scale_info::TypeInfo)]
    pub struct UnlockAccessError {
         reason: String,
    }
    impl From<scale::Error> for UnlockAccessError {
         fn from(_: scale::Error) -> Self {
             panic!("encountered unexpected invalid SCALE encoding")
         }
    }
    #[derive(scale::Encode, scale::Decode, scale_info::TypeInfo)]
    pub enum Access {
         ReadWrite,
         ReadOnly,
         WriteOnly,
    }
    impl ink::env::chain_extension::FromStatusCode for ReadWriteErrorCode {
         fn from_status_code(status_code: u32) -> Result<(), Self> {
             match status_code {
                 0 => Ok(()),
                 1 => Err(Self::InvalidKey),
                 2 => Err(Self::CannotWriteToKey),
                 3 => Err(Self::CannotReadFromKey),
                 _ => panic!("encountered unknown status code"),
             }
         }
    }
    pub enum CustomEnvironment {}
    impl ink::env::Environment for CustomEnvironment {
         const MAX_EVENT_TOPICS: usize =
             <ink::env::DefaultEnvironment as ink::env::Environment>::MAX_EVENT_TOPICS;

         type AccountId = <ink::env::DefaultEnvironment as ink::env::Environment>::AccountId;
         type Balance = <ink::env::DefaultEnvironment as ink::env::Environment>::Balance;
         type Hash = <ink::env::DefaultEnvironment as ink::env::Environment>::Hash;
         type BlockNumber = <ink::env::DefaultEnvironment as ink::env::Environment>::BlockNumber;
         type Timestamp = <ink::env::DefaultEnvironment as ink::env::Environment>::Timestamp;

         type ChainExtension = RuntimeReadWrite;
    }
}
  • Due to technical limitations it is not possible to refer to the ErrorCode associated type using Self::ErrorCode anywhere within the chain extension and its defined methods. Instead chain extension authors should directly use the error code type when required. This limitation might be lifted in future versions of ink!.

  • It is not possible to declare other chain extension traits as super traits or super chain extensions of another.

NOTA: O repositório do ink! contém o . Este é um exemplo completo de uma chain extension implementada tanto em ink! quanto no Substrate.

Estrutura

Uso

Atributos

Detalhes: handle_status

Uso: handle_status + Tipo de retornoResult<T, E>

Combinações

Código de Erro

Example: Definition

A full example of the above chain extension definition can be seen .

Example: Environment

Example: Usage

Technical Limitations

🖥️
📑
🎙️
⛓️
exemplo rand-extension
​
​
​
​
​
​
​
​
here
​
​
​