Nota
O acesso a esta página requer autorização. Podes tentar iniciar sessão ou mudar de diretório.
O acesso a esta página requer autorização. Podes tentar mudar de diretório.
As funções simples definidas pelo usuário (UDFs) no livro-razão confidencial do Azure permitem que você crie funções JavaScript personalizadas que podem ser executadas dentro do limite de confiança do livro-razão. Esse recurso foi projetado para ser simples e fácil de usar, permitindo que você estenda a funcionalidade da API do livro-razão sem a necessidade de desenvolvimento de aplicativos complexos.
Usando a API JavaScript integrada, você pode executar código personalizado para realizar várias tarefas, como consultas e cálculos personalizados, verificações condicionais, tarefas de pós-processamento e muito mais. Esse recurso é adequado para cenários em que você precisa de uma integração direta com a API do livro-razão existente ou executar uma lógica personalizada leve em um ambiente confidencial.
Para uma rápida visão geral e demonstração de UDFs, assista ao seguinte vídeo:
Importante
As funções definidas pelo usuário estão atualmente em PREVIEW na versão 2024-12-09-previewda API.
Pode solicitar acesso a esta pré-visualização através deste formulário de inscrição.
Consulte os Termos de Utilização Complementares das Visualizações Prévias do Microsoft Azure para obter os termos legais que se aplicam às funcionalidades do Azure que estão em beta, em pré-visualização ou que ainda não foram lançadas para disponibilidade geral.
Sugestão
Para cenários mais avançados, como RBAC (Controle de Acesso ao Role-Based) personalizado ou integração com cargas de trabalho confidenciais externas, consulte Funções avançadas definidas pelo usuário no livro-razão confidencial do Azure.
Casos de uso
"As UDFs do Azure Confidential Ledger permitem expandir a funcionalidade do ledger ao executar lógica personalizada." Alguns casos de uso comuns para UDFs (Funções Definidas pelo Usuário) incluem:
Consultas e cálculos personalizados: execute UDFs independentes para ler ou gravar dados em qualquer tabela de aplicativos contábeis de acordo com sua lógica de negócios.
Validação de dados e verificações de entrada: usar UDFs como ganchos prévios para executar ações de pré-processamento antes que uma entrada contábil seja gravada no livro, por exemplo, para limpar dados de entrada ou verificar pré-condições.
Enriquecimento de dados e contratos inteligentes: use UDFs como post-hooks para executar ações de pós-processamento depois que uma entrada contábil é gravada, por exemplo, para adicionar metadados personalizados ao livro-razão ou acionar fluxos de trabalho pós-gravação.
Escrevendo Funções Definidas pelo Utilizador (UDFs)
Um UDF do livro-razão confidencial do Azure é uma entidade armazenada no livro-razão com uma ID exclusiva e contém o código JavaScript que é executado quando o UDF é chamado. Esta seção descreve como escrever código UDF e usar a API JavaScript para realizar diferentes tarefas.
Estrutura da função
O código de um UDF requer uma função exportada que é o ponto de entrada do script no momento da execução. Um modelo de código UDF básico tem esta aparência:
export function main() {
// Your JavaScript code here
}
Observação
O nome da função de ponto de entrada exportada que é chamada durante a execução pode ser modificado com o exportedFunctionName argumento ao executar o UDF. Se não for especificado, o nome padrão será main.
Observação
As funções do Lambda são suportadas, mas exigem que o nome da função exportada seja explicitamente definido e corresponda ao nome da função do ponto de entrada. Por exemplo:
export const main = () => {
// Your JavaScript code here
};
Argumentos de função
Você pode especificar quaisquer argumentos de tempo de execução opcionais aceitos pela UDF. Os valores dos argumentos podem ser passados em tempo de execução ao executar o UDF usando o arguments parâmetro.
Os argumentos são sempre passados como uma matriz de cadeias de caracteres. É responsabilidade do usuário garantir que os argumentos especificados no código UDF correspondam aos argumentos passados ao executar o UDF. O usuário também deve garantir que os argumentos sejam analisados corretamente para o tipo de dados esperado em tempo de execução.
export function main(arg1, arg2) {
// Your JavaScript code here
}
API de JavaScript
O código JavaScript de um UDF é executado dentro de um ambiente de área restrita que fornece um conjunto limitado de APIs.
Todas as funções, objetos e valores globais padrão JavaScript podem ser usados. Um objeto global chamado ccf pode ser usado para acessar funcionalidades e utilitários específicos fornecidos pelo Confidential Consortium Framework (CCF) (por exemplo, funções auxiliares de criptografia, acessadores de tabelas contábeis, etc.). A API completa do ccf objeto global está documentada aqui.
Você também pode acessar informações contextuais da solicitação atual usando o context objeto global. Este objeto fornece acesso aos metadados de solicitação que originaram a execução da função (context.request) e o ID do usuário do chamador de função (context.userId). Para ganchos de transação, o ID da coleção e os conteúdos da transação associados à operação de escrita também são adicionados ao objeto context (context.collectionId e context.contents respectivamente).
O trecho a seguir mostra alguns exemplos básicos do uso da API JavaScript:
export function main(args) {
// Basic instructions
const a = 1 + 1;
// Basic statements
if (a > 0) {
console.log("a is positive");
} else {
console.log("a is negative or zero");
}
// Parse the string argument as a JSON object
JSON.parse(args);
// Logging utilities
console.log("Hello world");
// Math utilities
Math.random();
// CCF cryptography utilities
ccf.crypto.digest("SHA-256", ccf.strToBuf("Hello world"));
// Write to a custom ledger table
ccf.kv["public:mytable"].set(ccf.strToBuf("myKey"), ccf.strToBuf("myValue"));
// Read from a custom ledger table
ccf.bufToStr(ccf.kv["public:mytable"].get(ccf.strToBuf("myKey")));
// Read from the ledger entry table
ccf.kv["public:confidentialledger.logs"].get(ccf.strToBuf("subledger:0"));
// Get the request metadata that originated the function execution
const requestMetadata = context.request;
// Get the collection ID and transaction content (for transaction hooks only)
const collectionId = context.collectionId;
const contents = context.contents;
// Throw exceptions
throw new Error("MyCustomError");
}
Sugestão
Para obter mais informações sobre como os mapas contábeis podem ser usados para armazenar e recuperar dados, consulte a documentação do CCF na API do Key-Value Store.
Observação
A importação de módulos não é suportada em UDFs. O código JavaScript deve ser independente e não pode depender de bibliotecas ou módulos externos. As APIs da Web também não são suportadas no momento.
Gestão de UDFs
Os aplicativos de contabilidade confidencial do Azure fornecem uma API CRUD dedicada para criar, ler, atualizar e excluir entidades UDF. As UDFs são armazenadas com segurança no registo e são acessíveis apenas à aplicação do registo.
Observação
Funções simples definidas pelo usuário e funções avançadas definidas pelo usuário são recursos mutuamente exclusivos. Não é possível criar ou executar UDFs simples se UDFs avançadas estiverem definidas e vice-versa. Para alternar entre os dois, siga as instruções na página de visão geral do UDF.
Criar ou atualizar uma UDF
PUT /app/userDefinedFunctions/myFunction
{
"code": "export function main() { return "Hello World"; }",
}
Importante
A função de administrador é necessária para criar ou atualizar uma UDF.
Obtenha uma UDF
GET /app/userDefinedFunctions/myFunction
Listar UDFs
GET /app/userDefinedFunctions
Eliminar um UDF
DELETE /app/userDefinedFunctions/myFunction
Importante
A função de administrador é necessária para eliminar uma UDF.
Observação
A eliminação de uma função definida pelo utilizador (UDF) apenas remove a entidade do estado atual do livro de registos. Qualquer UDF excluído é sempre retido no histórico do livro de registos inalterável (como qualquer transação confirmada).
Executando UDFs
Uma vez criado, os utilizadores do ledger confidencial do Azure podem executar um UDF como uma função independente ou como um trilho de transação associado a uma operação de gravação. Cada execução UDF é executada em um ambiente de tempo de execução e sandbox separados, o que significa que a execução UDF é isolada de outras UDFs ou outras operações de contabilidade.
A execução UDF pode ser controlada usando propriedades opcionais que podem ser especificadas no corpo da solicitação. As propriedades atualmente suportadas são:
arguments: uma matriz de cadeias de caracteres que representam os argumentos a serem passados para a UDF. Os argumentos são passados na mesma ordem em que são definidos no código UDF. O valor padrão é uma matriz vazia.exportedFunctionName: o nome da função exportada a ser chamada durante a execução. Se não for especificado, o valor padrão serámain.runtimeOptions: um objeto que especifica as opções de tempo de execução para a execução UDF. As seguintes opções estão disponíveis:max_heap_bytes: o tamanho máximo de heap em bytes. O valor padrão é 10.485.760 (10 MB).max_stack_bytes: o tamanho máximo da pilha em bytes. O valor padrão é 1.048.576 (1 MB).max_execution_time_ms: o tempo máximo de execução em milissegundos. O valor padrão é 1000 (1 segundo).log_exception_details: um valor booleano que especifica se os detalhes da exceção devem ser registrados. O valor predefinido étrue.return_exception_details: um valor booleano que especifica se os detalhes da exceção devem ser retornados na resposta. O valor predefinido étrue.
Funções autónomas
Um UDF pode ser executado diretamente usando a POST /app/userDefinedFunctions/{functionId}:execute API.
POST /app/userDefinedFunctions/myFunction:execute
{}
O corpo da solicitação pode ser usado para especificar parâmetros de execução opcionais, como argumentos de função e propriedades de tempo de execução do JavaScript.
POST /app/userDefinedFunctions/myFunction:execute
{
"arguments": ["arg1", "arg2"],
"exportedFunctionName": "myMainFunction",
"runtimeOptions": {
"max_heap_bytes": 5,
"max_stack_bytes": 1024,
"max_execution_time_ms": 5000,
"log_exception_details": true,
"return_exception_details": true
}
}
A resposta indica o resultado da execução da função UDF (sucedida ou falhada). Se o UDF for bem-sucedido, a resposta incluirá o valor da função retornada no formato de cadeia de caracteres (se houver).
{
"result":
{
"returnValue": "MyReturnValue"
},
"status": "Succeeded"
}
Se o UDF falhar, a resposta incluirá a mensagem de erro com o detalhamento do rastreamento de pilha.
{
"error": {
"message": "Error while executing function myFunction: Error: MyCustomError\n at myMainFunction (myFunction)\n"
},
"status": "Failed"
}
Importante
A função de colaborador é necessária para executar um UDF.
Ganchos de transação
Uma UDF pode alternativamente ser executada como um gancho antes (pré-gancho) ou depois (pós-gancho) de uma entrada ser gravada no livro como parte da API de gravação (POST /app/transactions). Os hooks são executados no mesmo contexto da operação de gravação; isto significa que todos os dados gravados no livro-razão pelos hooks são automaticamente incluídos na mesma transação de gravação.
O corpo da solicitação de escrita pode ser usado para especificar quaisquer IDs UDF a serem executados como ganchos de pré-execução e ganchos de pós-execução, respectivamente.
POST /app/transactions?collectionId=myCollection
{
"contents": "myValue",
"preHooks": [
{
"functionId": "myPreHook"
}
],
"postHooks": [
{
"functionId": "myPostHook"
}
]
}
Importante
Os hooks devem ser explicitamente definidos no corpo da solicitação da operação de escrita. Em geral, as UDFs não podem ser executadas automaticamente para cada operação de gravação após serem criadas.
Para cada gancho, é possível especificar quaisquer propriedades de execução opcionais. Por exemplo:
POST /app/transactions?collectionId=myCollection
{
"contents": "myValue",
"preHooks": [
{
"functionId": "myPreHook",
"properties": {
"arguments": [
"arg1",
"arg2"
],
"exportedFunctionName": "myMainFunction",
"runtimeOptions": {
"max_heap_bytes": 5,
"max_stack_bytes": 1024,
"max_execution_time_ms": 5000,
"log_exception_details": true,
"return_exception_details": true
}
}
}
],
"postHooks": [
{
"functionId": "myPostHook",
"properties": {
"arguments": [
"arg1"
],
"exportedFunctionName": "myMainFunction",
"runtimeOptions": {
"max_heap_bytes": 5,
"max_stack_bytes": 1024,
"max_execution_time_ms": 5000,
"log_exception_details": true,
"return_exception_details": true
}
}
}
]
}
Você pode especificar até 5 pre-hooks e post-hooks no corpo da solicitação, com qualquer combinação. Os ganchos são sempre executados na ordem em que são fornecidos no corpo da solicitação.
Se um pré-gancho ou pós-gancho falhar, toda a transação será abortada. Nesse caso, a resposta contém a mensagem de erro com o motivo da falha:
{
"error": {
"code": "InternalError",
"message": "Error while executing function myPreHook: Error: MyCustomError\n at myMainFunction (myPreHook)\n",
}
}
Observação
Mesmo que vários ganchos sejam bem-sucedidos, a transação ainda pode falhar se qualquer um dos pré-ganchos ou pós-ganchos definidos não for executado com êxito até a conclusão.
Sugestão
Uma UDF pode ser reutilizada como pre-hook e post-hook na mesma solicitação e chamada várias vezes.
Exemplos
Esta seção apresenta alguns exemplos práticos de como usar UDFs no livro-razão confidencial do Azure. Para os cenários de exemplo a seguir, assumimos o uso do registo confidencial do Azure para armazenar transações de diferentes utilizadores bancários.
Contexto
Para armazenar uma transação bancária para um utilizador, a API existente para escrita no registo pode ser usada: o valor da transação é o conteúdo da entrada no registo e o ID do utilizador pode ser a coleção, ou chave, onde o conteúdo é registado.
POST /app/transactions?collectionId=John
{
"contents": "10"
}
HTTP/1.1 200 OK
Como não há validação no conteúdo de entrada, é possível escrever um valor não numérico como conteúdo. Por exemplo, essa solicitação será bem-sucedida mesmo se o valor do conteúdo não for um número:
POST /app/transactions?collectionId=Mark
{
"contents": "This is not a number"
}
HTTP/1.1 200 OK
Pré-ganchos para validação de dados
Para garantir que o conteúdo da transação seja sempre um número, um UDF pode ser criado para verificar o conteúdo de entrada. O pré-gancho a seguir verifica se o valor do conteúdo é um número e lança um erro se não.
PUT /app/userDefinedFunctions/validateTransaction
{
"code": "export function main() { if (isNaN(context.contents)) { throw new Error('Contents is not a number'); } }"
}
HTTP/1.1 201 CREATED
Usando o gancho prévio na solicitação de gravação, é possível impor que os dados de entrada correspondam ao formato esperado. A solicitação anterior agora falha como esperado:
POST /app/transactions?collectionId=Mark
{
"contents": "This is not a number",
"preHooks": [
{
"functionId": "validateTransaction"
}
]
}
HTTP/1.1 500 INTERNAL_SERVER_ERROR
{
"error": {
"code": "InternalError",
"message": "Error while executing function validateTransaction: Error: Contents is not a number\n at main (validateTransaction)\n"
}
}
Em vez disso, as solicitações válidas contendo valores numéricos teriam êxito conforme o esperado:
POST /app/transactions?collectionId=Mark
{
"contents": "30",
"preHooks": [
{
"functionId": "validateTransaction"
}
]
}
HTTP/1.1 200 OK
Mecanismos de pós-processamento para enriquecimento de dados
À medida que os usuários realizam novas transações bancárias, queremos registrar quando uma transação está acima de um determinado limite por motivos de auditoria. Um post-hook pode ser usado para gravar metadados personalizados em um livro-razão após uma operação de gravação para indicar se a transação foi maior do que um determinado limite.
Por exemplo, um UDF pode ser criado para verificar o valor da transação e escrever uma mensagem fictícia ("Alerta" para valores altos, "Normal" caso contrário) sob o usuário de entrada em uma tabela contábil personalizada (payment_metadata) se o valor for maior que 50.
PUT /app/userDefinedFunctions/detectHighTransaction
{
"code": "export function main() { let value = 'Normal'; if (context.contents > 50) { value = 'Alert' } ccf.kv['public:payment_metadata'].set(ccf.strToBuf(context.collectionId), ccf.strToBuf(value)); }"
}
HTTP/1.1 201 CREATED
Uma vez que o UDF é criado com êxito, o post-hook pode ser usado em novas solicitações de gravação:
POST /app/transactions?collectionId=Mark
{
"contents": "100",
"preHooks": [
{
"functionId": "validateTransaction"
}
],
"postHooks": [
{
"functionId": "detectHighTransaction"
}
]
}
HTTP/1.1 200 OK
POST /app/transactions?collectionId=John
{
"contents": "20",
"preHooks": [
{
"functionId": "validateTransaction"
}
],
"postHooks": [
{
"functionId": "detectHighTransaction"
}
]
}
HTTP/1.1 200 OK
UDFs independentes para consultas personalizadas
Para inspecionar os valores mais recentes gravados na tabela payment_metadata personalizada usando o post-hook, um UDF pode ser criado para ler os valores da tabela com um ID de usuário de entrada:
PUT /app/userDefinedFunctions/checkPaymentMetadataTable
{
"code": "export function main(user) { const value = ccf.kv['public:payment_metadata'].get(ccf.strToBuf(user)); if (value === undefined) { throw new Error('UnknownUser'); } return ccf.bufToStr(value); }"
}
HTTP/1.1 201 CREATED
Ao executar o UDF diretamente, é possível verificar o valor mais recente registrado na tabela de metadados personalizada para um determinado usuário.
Para utilizadores com uma transação elevada recente, o UDF retorna o valor "Alert" como esperado.
POST /app/userDefinedFunctions/checkPaymentMetadataTable:execute
{
"arguments": [
"Mark"
]
}
HTTP/1.1 200 OK
{
"result": {
"returnValue": "Alert"
},
"status": "Succeeded"
}
Para usuários com uma transação baixa recente, o UDF retorna o valor "Normal".
POST /app/userDefinedFunctions/checkPaymentMetadataTable:execute
{
"arguments": [
"John"
]
}
HTTP/1.1 200 OK
{
"result": {
"returnValue": "Normal"
},
"status": "Succeeded"
}
Para usuários que não têm nenhuma entrada na tabela personalizada, o UDF gera um erro conforme definido no código UDF.
POST /app/userDefinedFunctions/checkPaymentMetadataTable:execute
{
"arguments": [
"Jane"
]
}
HTTP/1.1 200 OK
{
"error": {
"message": "Error while executing function checkPaymentMetadataTable: Error: UnknownUser\n at main (checkPaymentMetadataTable)\n"
},
"status": "Failed"
}
Considerações
Atualmente, os ganchos de transação são suportados apenas pela API
POST /app/transactions, quando é adicionada uma nova entrada ao livro-razão.UDFs e ganchos são sempre executados na réplica principal do registo, para garantir a ordenação das transações e uma consistência robusta.
A execução de código UDF é sempre encapsulada em uma única transação atômica. Se a lógica JavaScript num UDF for concluída sem exceções, todas as operações dentro do UDF são registadas no livro-razão. Se qualquer exceção for lançada, todas as transações serão revertidas. Da mesma forma, pré-ganchos e pós-ganchos são executados no mesmo contexto da operação de gravação em que estão registrados. Se um gancho de pré ou um gancho de pós falhar, toda a transação será abortada e nenhuma entrada será adicionada ao livro de registos.
As UDFs só podem acessar tabelas de aplicativos CCF e não podem acessar as tabelas internas e de governança do sistema contabilístico ou outras tabelas incorporadas por motivos de segurança. As tabelas contábeis nas quais as entradas são gravadas (
public:confidentialledger.logspara livros contábeis públicos eprivate:confidentialledger.logspara livros contábeis privados) são de leitura apenas.O número máximo de pré-ganchos e pós-ganchos que podem ser registrados para uma única transação de gravação é 5.
A execução de funções definidas pelo usuário (UDF) e hooks é limitada a 5 segundos. Se uma função demorar mais de 5 segundos para ser executada, a operação será abortada e um erro será retornado.