Bagikan melalui


Fungsi sederhana yang ditentukan pengguna di Azure Confidential Ledger (pratinjau)

Fungsi yang ditentukan pengguna sederhana (UDF) di Azure Confidential Ledger memungkinkan Anda membuat fungsi JavaScript kustom yang dapat dijalankan di dalam batas kepercayaan ledger. Fitur ini dirancang agar sederhana dan mudah digunakan, memungkinkan Anda memperluas fungsionalitas API ledger tanpa perlu pengembangan aplikasi yang kompleks.

Dengan menggunakan JavaScript API bawaan, Anda dapat menjalankan kode kustom untuk mencapai berbagai tugas, seperti kueri dan komputasi kustom, pemeriksaan bersyarat, tugas pasca-pemrosesan, dan banyak lagi. Fitur ini cocok untuk skenario di mana Anda memerlukan integrasi langsung dengan API ledger yang ada atau menjalankan logika kustom ringan di lingkungan rahasia.

Untuk gambaran umum singkat dan demo UDF, tonton video berikut:

Penting

Fungsi yang ditentukan pengguna sedang dalam PRATINJAU di bawah versi API 2024-12-09-preview. Anda dapat meminta akses untuk pratinjau ini melalui formulir pendaftaran ini. Lihat Ketentuan Penggunaan Tambahan untuk Pratinjau Microsoft Azure untuk mengetahui ketentuan hukum yang berlaku untuk fitur Azure yang dalam tahap beta, pratinjau, atau belum dirilis ke ketersediaan umum.

Petunjuk / Saran

Untuk skenario yang lebih canggih, seperti Role-Based Access Control (RBAC) kustom atau integrasi dengan beban kerja rahasia eksternal, lihat fungsi lanjutan yang ditentukan pengguna di Azure Confidential Ledger.

Kasus penggunaan

Fungsi UDF pada Azure Confidential Ledger memungkinkan Anda untuk memperluas fungsionalitas ledger dengan menjalankan logika khusus. Beberapa contoh penggunaan umum untuk UDF meliputi:

  • Komputasi dan kueri kustom: jalankan UDF mandiri untuk membaca atau menulis data ke dalam tabel aplikasi ledger apa pun sesuai dengan logika bisnis Anda.

  • Validasi data dan pemeriksaan input: gunakan UDF sebagai pra-kait untuk menjalankan tindakan pra-pemrosesan sebelum entri ledger ditulis ke ledger, misalnya untuk membersihkan data input atau memeriksa pra-kondisi.

  • Pengayaan data dan kontrak pintar: gunakan UDF sebagai pasca-kait untuk menjalankan tindakan pasca-pemrosesan setelah entri ledger ditulis, misalnya untuk menambahkan metadata kustom ke dalam ledger atau memicu alur kerja pasca-tulis.

Menulis UDF

Azure Confidential Ledger UDF adalah entitas yang disimpan dalam ledger dengan ID unik dan berisi kode JavaScript yang dijalankan saat UDF dipanggil. Bagian ini menjelaskan cara menulis kode UDF dan menggunakan API JavaScript untuk mencapai tugas yang berbeda.

Struktur fungsi

Kode UDF memerlukan fungsi yang diekspor yang merupakan titik masuk skrip pada saat eksekusi. Templat kode UDF dasar terlihat seperti ini:

export function main() {
    // Your JavaScript code here
}

Nota

Nama fungsi titik masuk yang diekspor yang dipanggil selama eksekusi dapat dimodifikasi dengan exportedFunctionName argumen saat menjalankan UDF. Jika tidak ditentukan, nama defaultnya adalah main.

Nota

Fungsi Lambda didukung, tetapi memerlukan nama fungsi yang diekspor untuk didefinisikan secara eksplisit dan cocok dengan nama fungsi entrypoint. Contohnya:

export const main = () => { 
    // Your JavaScript code here 
};

Argumen fungsi

Anda dapat menentukan argumen runtime opsional yang diterima oleh UDF. Nilai argumen dapat diteruskan pada runtime saat menjalankan UDF menggunakan arguments parameter .

Argumen selalu diteruskan sebagai array string. Pengguna bertanggung jawab untuk memastikan bahwa argumen yang ditentukan dalam kode UDF cocok dengan argumen yang diteruskan saat menjalankan UDF. Pengguna juga harus memastikan bahwa argumen diurai dengan benar ke jenis data yang diharapkan saat runtime.

export function main(arg1, arg2) {
    // Your JavaScript code here
}

JavaScript API

Kode JavaScript dari UDF dijalankan di dalam lingkungan kotak pasir yang menyediakan sekumpulan API terbatas.

Semua fungsi, objek, dan nilai global standar JavaScript dapat digunakan. Objek global yang disebut ccf dapat digunakan untuk mengakses fungsionalitas dan utilitas tertentu yang disediakan oleh Confidential Consortium Framework (CCF) ( misalnya, fungsi pembantu kriptografi, pengakses tabel ledger, dll.). API ccf lengkap objek global didokumentasikan di sini.

Anda juga dapat mengakses informasi kontekstual permintaan saat ini dengan menggunakan context objek global. Objek ini menyediakan akses ke metadata permintaan yang berasal dari eksekusi fungsi (context.request) dan ID pengguna pemanggil fungsi (context.userId). Untuk pengait transaksi, ID koleksi dan isi transaksi yang berhubungan dengan operasi penulisan juga ditambahkan ke objek context (context.collectionId dan context.contents masing-masing).

Cuplikan berikut menunjukkan beberapa contoh dasar penggunaan JAVAScript API:

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");
}

Petunjuk / Saran

Untuk informasi selengkapnya tentang bagaimana peta ledger dapat digunakan untuk menyimpan dan mengambil data, lihat dokumentasi CCF pada Key-Value Store API.

Nota

Mengimpor modul tidak didukung di UDF. Kode JavaScript harus mandiri dan tidak dapat mengandalkan pustaka atau modul eksternal. API Web juga tidak didukung saat ini.

Mengelola UDF

Aplikasi Azure Confidential Ledger menyediakan CRUD API khusus untuk membuat, membaca, memperbarui, dan menghapus entitas UDF. UDF disimpan dengan aman di ledger dan hanya dapat diakses oleh aplikasi ledger.

Nota

Fungsi yang ditentukan pengguna sederhana dan fungsi yang ditentukan pengguna tingkat lanjut adalah fitur yang saling eksklusif. Anda tidak dapat membuat atau menjalankan UDF sederhana jika UDF tingkat lanjut ditentukan, dan sebaliknya. Untuk beralih di antara keduanya, ikuti instruksi di halaman gambaran umum UDF.

Membuat atau memperbarui UDF

PUT /app/userDefinedFunctions/myFunction
{
    "code": "export function main() { return "Hello World"; }",
}

Penting

Peran administrator diperlukan untuk membuat atau memperbarui UDF.

Dapatkan UDF

GET /app/userDefinedFunctions/myFunction

Daftar UDF

GET /app/userDefinedFunctions

Menghapus UDF

DELETE /app/userDefinedFunctions/myFunction

Penting

Peran administrator diperlukan untuk menghapus UDF.

Nota

Menghapus UDF hanya menghapus entitas dari status ledger saat ini. Setiap UDF yang dihapus selalu dipertahankan dalam riwayat buku besar yang tidak dapat diubah, seperti halnya transaksi yang telah dicatat.

Menjalankan UDF

Setelah dibuat, pengguna Azure Confidential Ledger dapat menjalankan UDF baik sebagai fungsi mandiri atau sebagai pengait transaksi yang berhubungan dengan operasi tulis. Setiap eksekusi UDF berjalan pada lingkungan runtime dan sandbox terpisah, yang berarti bahwa eksekusi UDF diisolasi dari UDF lain atau dari operasi ledger lainnya.

Eksekusi UDF dapat dikontrol menggunakan properti opsional yang dapat ditentukan dalam isi permintaan. Properti yang saat ini didukung adalah:

  • arguments: array string yang mewakili argumen yang akan diteruskan ke UDF. Argumen diteruskan dalam urutan yang sama seperti yang ditentukan dalam kode UDF. Nilai defaultnya adalah array kosong.

  • exportedFunctionName: nama fungsi yang diekspor untuk dipanggil selama eksekusi. Jika tidak ditentukan, nilai defaultnya adalah main.

  • runtimeOptions: objek yang menentukan opsi runtime untuk eksekusi UDF. Opsi berikut ini tersedia:

    • max_heap_bytes: ukuran timbunan maksimum dalam byte. Nilai defaultnya adalah 10.485.760 (10 MB).

    • max_stack_bytes: ukuran tumpukan maksimum dalam byte. Nilai defaultnya adalah 1.048.576 (1 MB).

    • max_execution_time_ms: waktu eksekusi maksimum dalam milidetik. Nilai defaultnya adalah 1000 (1 detik).

    • log_exception_details: nilai boolean yang menentukan apakah akan mencatat detail pengecualian. Nilai defaultnya adalah true.

    • return_exception_details: nilai boolean yang menentukan apakah akan mengembalikan detail pengecualian dalam respons. Nilai defaultnya adalah true.

Fungsi mandiri

UDF dapat langsung dijalankan menggunakan POST /app/userDefinedFunctions/{functionId}:execute API.

POST /app/userDefinedFunctions/myFunction:execute
{}

Isi permintaan dapat digunakan untuk menentukan parameter eksekusi opsional, seperti argumen fungsi dan properti runtime 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
    }
}

Respons menunjukkan hasil eksekusi UDF (berhasil atau gagal). Jika UDF berhasil, respons menyertakan nilai yang dikembalikan fungsi dalam format string (jika ada).

{
    "result": 
        {
            "returnValue": "MyReturnValue"
        }, 
    "status": "Succeeded"
}

Jika UDF gagal, respons akan menyertakan pesan kesalahan beserta detail pelacakan tumpukan.

{
    "error": {
        "message": "Error while executing function myFunction: Error: MyCustomError\n    at myMainFunction (myFunction)\n"
    }, 
    "status": "Failed"
}

Penting

Peran sebagai kontributor diperlukan untuk menjalankan fungsi UDF.

Pengait transaksi

UDF dapat dieksekusi sebagai kait sebelum (pra-kait) atau setelah (pasca-kait) entri ditulis ke ledger sebagai bagian dari API tulis ledger (POST /app/transactions). Kait berjalan dalam konteks operasi tulis yang sama; ini berarti bahwa setiap data yang ditulis ke ledger oleh kait secara otomatis disertakan dalam transaksi tulis yang sama.

Isi permintaan permintaan tulis dapat digunakan untuk menentukan ID UDF apa pun yang akan dijalankan sebagai pra-kait dan pasca-kait masing-masing.

POST /app/transactions?collectionId=myCollection
{
  "contents": "myValue", 
  "preHooks": [ 
    { 
        "functionId": "myPreHook"
    } 
  ], 
  "postHooks": [ 
    { 
        "functionId": "myPostHook" 
    }
  ] 
} 

Penting

Kait harus didefinisikan secara eksplisit dalam isi permintaan operasi tulis. Secara umum, UDF tidak dapat berjalan secara otomatis untuk setiap operasi tulis setelah dibuat.

Untuk setiap kait, Anda dapat menentukan properti eksekusi opsional apa pun. Contohnya:

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
            } 
        } 
    }
  ] 
} 

Anda dapat menentukan hingga 5 pra-kait dan pasca-kait dalam isi permintaan, dengan kombinasi apa pun. Hooks selalu dijalankan sesuai urutan yang disediakan dalam badan permintaan.

Jika sebuah pra-hook atau pasca-hook gagal, maka transaksi secara keseluruhan dibatalkan. Dalam hal ini, respons berisi pesan kesalahan dengan alasan kegagalan:

{
    "error": {
        "code": "InternalError",
        "message": "Error while executing function myPreHook: Error: MyCustomError\n    at myMainFunction (myPreHook)\n",
    }
}

Nota

Bahkan jika banyak hook berhasil, transaksi masih dapat gagal jika salah satu pra-hook atau pasca-hook yang ditentukan tidak berhasil selesai.

Petunjuk / Saran

UDF dapat digunakan kembali sebagai pra-hook dan pasca-hook dalam permintaan yang sama dan dapat dipanggil beberapa kali.

Contoh

Bagian ini menjelaskan beberapa contoh praktis tentang cara menggunakan UDF di Azure Confidential Ledger. Untuk contoh skenario berikut, kami berasumsi menggunakan Azure Confidential Ledger untuk menyimpan transaksi perbankan untuk pengguna bank yang berbeda.

Konteks

Untuk menyimpan transaksi perbankan bagi pengguna, API tulis ledger yang ada dapat digunakan: nilai transaksi adalah konten entri ledger dan ID pengguna dapat menjadi koleksi, atau kunci, tempat konten ditulis di bawah.

POST /app/transactions?collectionId=John
{
    "contents": "10"
}

HTTP/1.1 200 OK

Karena tidak ada validasi pada konten input, dimungkinkan untuk menulis nilai non-numerik sebagai konten. Misalnya, permintaan ini berhasil meskipun nilai konten bukan angka:

POST /app/transactions?collectionId=Mark
{
    "contents": "This is not a number"
}

HTTP/1.1 200 OK

Kait Pra-pemrosesan untuk validasi data

Untuk memastikan konten transaksi selalu berupa angka, UDF dapat dibuat untuk memeriksa konten input. Pra-pengait berikut memeriksa apakah nilai konten adalah angka dan menghasilkan kesalahan jika tidak.

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

Dengan menggunakan hook pra-pemrosesan dalam permintaan tulis, dimungkinkan untuk memastikan bahwa data input sesuai dengan format yang diharapkan. Permintaan sebelumnya sekarang gagal seperti yang diharapkan:

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"
  }
}

Permintaan yang valid yang berisi nilai numerik akan berhasil seperti yang diharapkan:

POST /app/transactions?collectionId=Mark
{
    "contents": "30",
    "preHooks": [
        {
            "functionId": "validateTransaction"
        }
    ]
}

HTTP/1.1 200 OK

Pasca-kait untuk pengayaan data

Saat pengguna melakukan transaksi perbankan baru, kami ingin mencatat kapan transaksi lebih tinggi dari ambang batas tertentu karena alasan audit. Post-hook dapat digunakan untuk menulis metadata kustom dalam ledger setelah operasi tulis untuk menunjukkan apakah transaksi lebih tinggi dari ambang batas tertentu.

Misalnya, UDF dapat dibuat untuk memeriksa nilai transaksi dan menulis pesan dummy ("Pemberitahuan" untuk nilai tinggi, "Normal" jika tidak) di bawah pengguna input ke dalam tabel ledger kustom (payment_metadata) jika nilainya lebih tinggi dari 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

Setelah UDF berhasil dibuat, post-hook dapat digunakan dalam permintaan tulis baru:

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 yang berdiri sendiri untuk kueri khusus

Untuk memeriksa nilai terbaru yang ditulis ke tabel payment_metadata khusus menggunakan post-hook, UDF dapat dibuat untuk membaca nilai dari tabel dengan ID pengguna sebagai input.

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

Dengan menjalankan UDF secara langsung, dimungkinkan untuk memeriksa nilai terbaru yang dicatat dalam tabel metadata kustom untuk pengguna tertentu.

Untuk pengguna dengan transaksi tinggi baru-baru ini, UDF mengembalikan nilai "Peringatan" seperti yang diharapkan.

POST /app/userDefinedFunctions/checkPaymentMetadataTable:execute
{
    "arguments": [
        "Mark"
    ]
}

HTTP/1.1 200 OK
{
  "result": {
    "returnValue": "Alert"
  },
  "status": "Succeeded"
}

Untuk pengguna dengan transaksi rendah baru-baru ini, UDF mengembalikan nilai "Normal" sebagai gantinya.

POST /app/userDefinedFunctions/checkPaymentMetadataTable:execute
{
    "arguments": [
        "John"
    ]
}

HTTP/1.1 200 OK
{
  "result": {
    "returnValue": "Normal"
  },
  "status": "Succeeded"
}

Untuk pengguna yang tidak memiliki entri apa pun dalam tabel kustom, UDF melemparkan kesalahan seperti yang didefinisikan dalam kode 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"
}

Pertimbangan

  • Kait transaksi saat ini hanya didukung untuk POST /app/transactions API, saat menambahkan entri baru ke ledger.

  • UDF dan pengait selalu dijalankan pada replika utama dari ledger, untuk memastikan urutan transaksi dan konsistensi yang kuat.

  • Eksekusi kode UDF selalu dibungkus dalam satu transaksi atomik. Jika logika JavaScript dalam UDF selesai tanpa pengecualian apa pun, semua operasi dalam UDF diterapkan ke ledger. Jika ada pengecualian yang dilemparkan, seluruh transaksi akan digulung balik. Demikian pula, pra-kait dan pasca-kait dijalankan dalam konteks operasi tulis yang sama dengan yang didaftarkan. Jika pra-hook atau pasca-hook gagal, seluruh transaksi dibatalkan dan tidak ada entri yang ditambahkan ke ledger.

  • UDF hanya dapat mengakses tabel aplikasi CCF dan tidak dapat mengakses tabel internal dan tata kelola ledger atau tabel bawaan lainnya karena alasan keamanan. Tabel ledger tempat entri ditulis ke (public:confidentialledger.logs untuk ledger publik dan private:confidentialledger.logs untuk ledger privat) bersifat baca-saja.

  • Jumlah maksimum pre-hook dan post-hook yang dapat didaftarkan untuk satu transaksi tulis adalah 5.

  • Eksekusi UDF dan fungsi pengait dibatasi hingga 5 detik. Jika fungsi membutuhkan waktu lebih dari 5 detik untuk dijalankan, operasi dibatalkan dan kesalahan dikembalikan.