Nota
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
Las funciones definidas por el usuario (UDF) simples en El libro de contabilidad confidencial de Azure permiten crear funciones personalizadas de JavaScript que se pueden ejecutar dentro del límite de confianza del libro de contabilidad. Esta característica está diseñada para ser sencilla y fácil de usar, lo que le permite ampliar la funcionalidad de la API de libro de contabilidad sin necesidad de desarrollo complejo de aplicaciones.
Con la API de JavaScript integrada, puede ejecutar código personalizado para lograr diversas tareas, como consultas personalizadas y cálculos, comprobaciones condicionales, tareas posteriores al procesamiento, etc. Esta característica es adecuada para escenarios en los que necesita una integración directa con la API de libro de contabilidad existente o ejecutar lógica personalizada ligera en un entorno confidencial.
Para obtener información general rápida y demostración de las UDF, vea el vídeo siguiente:
Importante
Las funciones definidas por el usuario se encuentran actualmente en versión preliminar en versión 2024-12-09-preview
de API.
Puede solicitar acceso a esta versión preliminar a través de este formulario de registro.
Consulte los Términos de uso complementarios de las versiones preliminares de Microsoft Azure para conocer los términos legales que se aplican a las características de Azure que se encuentran en versión beta, versión preliminar o, de lo contrario, aún no se han publicado en disponibilidad general.
Sugerencia
Para escenarios más avanzados, como custom Role-Based Access Control (RBAC) o integración con cargas de trabajo confidenciales externas, consulte funciones avanzadas definidas por el usuario en Azure Confidential Ledger.
Casos de uso
Las UDF de Libro de contabilidad confidencial de Azure permiten ampliar la funcionalidad del libro de contabilidad mediante la ejecución de lógica personalizada. Algunos casos de uso comunes para las UDF incluyen:
Cálculos y consultas personalizados: ejecute UDF independientes para leer o escribir datos en cualquier tabla de aplicación del libro mayor de acuerdo con su lógica empresarial.
Validación de datos y comprobaciones de entrada: utilice las UDF como enlaces previos para ejecutar acciones de preprocesamiento antes de que una entrada del libro mayor se escriba en el libro mayor, por ejemplo, para sanear los datos de entrada o comprobar condiciones previas.
Enriquecimiento de datos y contratos inteligentes: utilice UDF como enlaces posteriores para ejecutar acciones de post procesamiento después de que se escriba una entrada en el libro mayor, por ejemplo, para agregar metadatos personalizados en el libro mayor o desencadenar flujos de trabajo posteriores a la escritura.
Escribir UDFs
Una UDF de libro de contabilidad confidencial de Azure es una entidad almacenada en el libro de contabilidad con un identificador único y contiene el código JavaScript que se ejecuta cuando se llama a la UDF. En esta sección se describe cómo escribir código UDF y usar la API de JavaScript para lograr diferentes tareas.
Estructura de la función
El código de una UDF requiere una función exportada que es el punto de entrada del script en el momento de la ejecución. Una plantilla de código UDF básica tiene este aspecto:
export function main() {
// Your JavaScript code here
}
Nota:
El nombre de la función de punto de entrada exportada a la que se llama durante la ejecución se puede modificar con el exportedFunctionName
argumento al ejecutar la UDF. Si no se especifica, el nombre predeterminado es main
.
Nota:
Se admiten funciones lambda, pero requieren que el nombre de la función exportada se defina explícitamente y coincida con el nombre de la función de punto de entrada. Por ejemplo:
export const main = () => {
// Your JavaScript code here
};
Argumentos de función
Puede especificar los argumentos de tiempo de ejecución opcionales aceptados por la UDF. Los valores de los argumentos se pueden pasar en tiempo de ejecución al ejecutar la UDF mediante el arguments
parámetro .
Los argumentos se pasan siempre como una matriz de cadenas. Es responsabilidad del usuario asegurarse de que los argumentos especificados en el código UDF coinciden con los argumentos pasados al ejecutar la UDF. El usuario también debe asegurarse de que los argumentos se analizan correctamente en el tipo de datos esperado en tiempo de ejecución.
export function main(arg1, arg2) {
// Your JavaScript code here
}
API de JavaScript
El código JavaScript de una UDF se ejecuta dentro de un entorno de espacio aislado que proporciona un conjunto limitado de API.
Se pueden usar todas las funciones globales, objetos y valores estándar de JavaScript . Un objeto global denominado ccf
se puede usar para acceder a funcionalidades y utilidades específicas proporcionadas por confidential Consortium Framework (CCF) ( por ejemplo, funciones auxiliares de criptografía, descriptores de acceso de tablas de libro de contabilidad, etc.). La API completa del ccf
objeto global se documenta aquí.
También puede acceder a la información contextual de la solicitud actual mediante el context
objeto global. Este objeto proporciona acceso a los metadatos de solicitud que originaron la ejecución de la función (context.request
) y el identificador de usuario del llamador de función (context.userId
). En el caso de los enlaces de transacción, el identificador de colección y el contenido de la transacción asociado a la operación de escritura también se agregan al context
objeto (context.collectionId
y context.contents
respectivamente).
En el fragmento de código siguiente se muestran algunos ejemplos básicos del uso de la API de 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");
}
Sugerencia
Para obtener más información sobre cómo se pueden usar los mapas de libro de contabilidad para almacenar y recuperar datos, consulte la documentación de CCF en la API de Key-Value Store.
Nota:
No se admite la importación de módulos en UDF. El código JavaScript debe ser independiente y no puede basarse en bibliotecas o módulos externos. Las API web tampoco se admiten en este momento.
Administrar UDFs
Las aplicaciones de Libro de contabilidad confidencial de Azure proporcionan una API CRUD dedicada para crear, leer, actualizar y eliminar entidades UDF. Las UDF se almacenan de forma segura en el libro de contabilidad y solo son accesibles para la aplicación de libro de contabilidad.
Nota:
Las funciones sencillas definidas por el usuario y las funciones avanzadas definidas por el usuario son características mutuamente excluyentes. No puede crear ni ejecutar UDF simples si se definen UDF avanzadas y viceversa. Para cambiar entre los dos, siga las instrucciones de la página de información general de UDF.
Crear o actualizar una UDF
PUT /app/userDefinedFunctions/myFunction
{
"code": "export function main() { return "Hello World"; }",
}
Importante
El rol de administrador es necesario para crear o actualizar una UDF.
Obtener una UDF
GET /app/userDefinedFunctions/myFunction
Enumerar UDFs
GET /app/userDefinedFunctions
Eliminar una UDF
DELETE /app/userDefinedFunctions/myFunction
Importante
El rol de administrador es necesario para eliminar una UDF.
Nota:
La eliminación de una UDF solo elimina la entidad del estado actual del libro mayor. Cualquier UDF eliminada se conserva siempre en el historial inmutable del libro mayor (como cualquier transacción comprometida).
Ejecución de UDFs
Una vez creado, los usuarios de Azure Confidential Ledger pueden ejecutar una UDF como una función independiente o como un enlace de transacciones asociado a una operación de escritura. Cada ejecución de UDF se ejecuta en un entorno de tiempo de ejecución y un espacio aislado independientes, lo que significa que la ejecución de UDF está aislada de otras UDF u otras operaciones de libro de contabilidad.
La ejecución de UDF se puede controlar mediante propiedades opcionales que se pueden especificar en el cuerpo de la solicitud. Las propiedades admitidas actualmente son:
arguments
: matriz de cadenas que representan los argumentos que se van a pasar a la UDF. Los argumentos se pasan en el mismo orden que se definen en el código UDF. El valor predeterminado es una matriz vacía.exportedFunctionName
: nombre de la función exportada a la que se va a llamar durante la ejecución. Si no se especifica, el valor predeterminado esmain
.runtimeOptions
: un objeto que especifica las opciones en tiempo de ejecución para la ejecución de UDF. Las siguientes opciones están disponibles:max_heap_bytes
: el tamaño máximo del montón en bytes. El valor predeterminado es 10 485 760 (10 MB).max_stack_bytes
: el tamaño máximo de la pila en bytes. El valor predeterminado es 1048 576 (1 MB).max_execution_time_ms
: el tiempo máximo de ejecución en milisegundos. El valor predeterminado es 1000 (1 segundo).log_exception_details
: un valor booleano que especifica si se deben registrar los detalles de la excepción. El valor predeterminado estrue
.return_exception_details
: un valor booleano que especifica si se devuelven los detalles de excepción en la respuesta. El valor predeterminado estrue
.
Funciones independientes
Una UDF se puede ejecutar directamente mediante la POST /app/userDefinedFunctions/{functionId}:execute
API.
POST /app/userDefinedFunctions/myFunction:execute
{}
El cuerpo de la solicitud se puede usar para especificar parámetros de ejecución opcionales, como argumentos de función y propiedades en tiempo de ejecución de 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
}
}
La respuesta indica el resultado de la ejecución de la UDF (exitosa o fallida). Si la UDF se realizó correctamente, la respuesta incluye el valor devuelto de la función en formato de cadena (si existe).
{
"result":
{
"returnValue": "MyReturnValue"
},
"status": "Succeeded"
}
Si se produjo un error en la UDF, la respuesta incluye el mensaje de error con el rastro detallado de la pila.
{
"error": {
"message": "Error while executing function myFunction: Error: MyCustomError\n at myMainFunction (myFunction)\n"
},
"status": "Failed"
}
Importante
El rol de colaborador es necesario para ejecutar una UDF.
Enlaces de transacción
Una UDF puede ejecutarse alternativamente como enlace previo (pre enlace) o después (post enlace) de que se escriba una entrada en el libro mayor como parte de la API de escritura del libro mayor (POST /app/transactions
). Los enlaces se ejecutan en el mismo contexto que la operación de escritura; esto significa que cualquier dato escrito en el libro mayor por los enlaces se incluye automáticamente en la misma transacción de escritura.
El cuerpo de la solicitud de escritura se puede utilizar para especificar cualquier id. UDF que se ejecutará como enlaces previos y posteriores respectivamente.
POST /app/transactions?collectionId=myCollection
{
"contents": "myValue",
"preHooks": [
{
"functionId": "myPreHook"
}
],
"postHooks": [
{
"functionId": "myPostHook"
}
]
}
Importante
Los enlaces deben definirse explícitamente en el cuerpo de la solicitud de la operación de escritura. En general, las UDF no se pueden ejecutar automáticamente para cada operación de escritura después de crearse.
Para cada gancho, es posible especificar cualquier propiedad de ejecución opcional. Por ejemplo:
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
}
}
}
]
}
Puede especificar hasta 5 enlaces previos y posteriores en el cuerpo de la solicitud, con cualquier combinación. Los enlaces se ejecutan siempre en el orden en que se proporcionan en el cuerpo de la solicitud.
Si falla un enlace previo o posterior, se cancela toda la transacción. En ese caso, la respuesta contiene el mensaje de error por el motivo del error:
{
"error": {
"code": "InternalError",
"message": "Error while executing function myPreHook: Error: MyCustomError\n at myMainFunction (myPreHook)\n",
}
}
Nota:
Incluso si varios enlaces tienen éxito, la transacción puede fallar si alguno de los enlaces previos o posteriores definidos no se ejecuta correctamente hasta su finalización.
Sugerencia
Una UDF puede reutilizarse como enlace previo y posterior en la misma solicitud y llamarse varias veces.
Ejemplos
En esta sección se describen algunos ejemplos prácticos de cómo usar UDF en El libro de contabilidad confidencial de Azure. En los escenarios de ejemplo siguientes, se supone que se usa Azure Confidential Ledger para almacenar transacciones bancarias para distintos usuarios bancarios.
Contexto
Para almacenar una transacción bancaria para un usuario, se puede usar la API de escritura del libro de contabilidad existente: el valor de la transacción es el contenido de la entrada del libro de contabilidad y el identificador de usuario puede ser la colección o clave, donde se escribe el contenido.
POST /app/transactions?collectionId=John
{
"contents": "10"
}
HTTP/1.1 200 OK
Puesto que no hay ninguna validación en el contenido de entrada, es posible escribir un valor no numérico como contenido. Por ejemplo, esta solicitud se realiza correctamente incluso si el valor de contenido no es un número:
POST /app/transactions?collectionId=Mark
{
"contents": "This is not a number"
}
HTTP/1.1 200 OK
Ganchos previos para la validación de datos
Para asegurarse de que el contenido de la transacción siempre es un número, se puede crear una UDF para comprobar el contenido de entrada. El siguiente enlace previo comprueba si el valor del contenido es un número y arroja un error en caso contrario.
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
Utilizando el enlace previo en la solicitud de escritura, es posible garantizar que los datos de entrada se corresponden con el formato esperado. Ahora la solicitud anterior falla como se esperaba.
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"
}
}
En cambio, las solicitudes válidas que contienen valores numéricos tendrán éxito como se esperaba:
POST /app/transactions?collectionId=Mark
{
"contents": "30",
"preHooks": [
{
"functionId": "validateTransaction"
}
]
}
HTTP/1.1 200 OK
Enlaces posteriores para el enriquecimiento de datos
A medida que los usuarios realizan nuevas transacciones bancarias, queremos registrar cuando una transacción es mayor que un umbral determinado por motivos de auditoría. Se puede utilizar un enlace posterior para escribir metadatos personalizados en un libro mayor después de una operación de escritura para indicar si la transacción fue superior a un determinado umbral.
Por ejemplo, se puede crear una UDF para comprobar el valor de la transacción y escribir un mensaje ficticio ("Alerta" para valores altos, "Normal" en caso contrario) en una tabla de libro de contabilidad personalizada bajo el usuario de entrada (payment_metadata
) si el valor es mayor de 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
Una vez que la UDF se ha creado correctamente, el enlace posterior puede utilizarse en nuevas solicitudes de escritura:
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
UDF independientes para consultas personalizadas
Para inspeccionar los últimos valores escritos en la tabla personalizada payment_metadata
utilizando el enlace posterior, se puede crear una UDF para leer los valores de la tabla dado un id. de usuario 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
Al ejecutar la UDF directamente, es posible comprobar el valor más reciente registrado en la tabla de metadatos personalizados para un usuario determinado.
Para los usuarios con una transacción alta reciente, el UDF devuelve el valor "Alerta" como se esperaba.
POST /app/userDefinedFunctions/checkPaymentMetadataTable:execute
{
"arguments": [
"Mark"
]
}
HTTP/1.1 200 OK
{
"result": {
"returnValue": "Alert"
},
"status": "Succeeded"
}
Para los usuarios con recientes transacciones bajas, la UDF devuelve en su lugar el valor "Normal".
POST /app/userDefinedFunctions/checkPaymentMetadataTable:execute
{
"arguments": [
"John"
]
}
HTTP/1.1 200 OK
{
"result": {
"returnValue": "Normal"
},
"status": "Succeeded"
}
Para los usuarios que no tienen ninguna entrada en la tabla personalizada, la UDF produce un error tal como se define en el 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"
}
Consideraciones
Actualmente, los enlaces de transacción solo se admiten para la
POST /app/transactions
API al agregar una nueva entrada al libro de contabilidad.Las UDF y los enlaces se ejecutan siempre en la réplica primaria del libro mayor, para garantizar el orden de las transacciones y una coherencia sólida.
La ejecución del código UDF siempre se envuelve en una única transacción atómica. Si la lógica de JavaScript de una UDF se completa sin ninguna excepción, todas las operaciones dentro de la UDF se confirman en el libro de contabilidad. Si se produce alguna excepción, se anulan todas las transacciones. Del mismo modo, los enlaces previos y posteriores se ejecutan en el mismo contexto de la operación de escritura en la que están registrados. Si falla un gancho previo o un gancho posterior, se anula la transacción completa y no se agrega ninguna entrada al libro de contabilidad.
Las UDF solo pueden acceder a las tablas de aplicaciones CCF y no pueden acceder a las tablas internas y de gobernanza del libro de contabilidad u otras tablas integradas por motivos de seguridad. Las tablas de libro de contabilidad en las que las entradas se escriben en (
public:confidentialledger.logs
para los libros de contabilidad públicos yprivate:confidentialledger.logs
para los libros de contabilidad privados) son de solo lectura.El número máximo de enlaces previos y posteriores que se pueden registrar para una única transacción de escritura es de 5.
La ejecución de UDF y enlaces está limitada a 5 segundos. Si una función tarda más de 5 segundos en ejecutarse, se anula la operación y se devuelve un error.