Azure Confidential Ledger の書き込みトランザクション レシート

Azure Confidential Ledger は、トランザクションの整合性保証を適用するために、マークル ツリー データ構造を使用して、変更不可能な台帳に追加されるすべてのトランザクション ブロックのハッシュを記録します。 書き込みトランザクションがコミットされた後、Azure Confidential Ledger ユーザーは Confidential Ledger で生成されたエントリに対して暗号化されたマークル証明 (レシート) を取得して、書き込み操作が正しく保存されたことを確認できます。 書き込みトランザクション レシートは、対応するトランザクションがシステムでコミットされたことの証明であり、エントリが実質的に台帳に追加されたことを確認するために使用できます。

Confidential Ledger でマークル ツリーがどのように使われるかについて詳しくは、CCF ドキュメントを参照してください。

書き込みトランザクション レシートを取得する

セットアップと前提条件

Azure Confidential Ledger ユーザーは、Azure Confidential Ledger クライアント ライブラリを使用して、特定のトランザクションのレシートを取得できます。 次の例は、Python 用クライアント ライブラリを使用して書き込みレシートを取得する方法を示していますが、Azure Confidential Ledger でサポートされている他の SDK でも手順は同じです。

ここでは、Confidential Ledger リソースが Azure Confidential Ledger Management ライブラリを使用して既に作成されていることを前提としています。 既存の台帳リソースがまだない場合は、以下の手順に従って作成します。

コードのチュートリアル

まず、Python プログラムのインポートを設定します。

import json 

# Import the Azure authentication library 
from azure.identity import DefaultAzureCredential 

# Import the Confidential Ledger Data Plane SDK 
from azure.confidentialledger import ConfidentialLedgerClient 
from azure.confidentialledger.certificate import ConfidentialLedgerCertificateClient 

Azure Confidential Ledger クライアントの設定に使用する定数値を次に示します。 ledger_name 定数は、Confidential Ledger リソースの一意の名前を使って更新してください。

# Constants for our program 
ledger_name = "<your-unique-ledger-name>" 
identity_url = "https://identity.confidential-ledger.core.azure.com" 
ledger_url = "https://" + ledger_name + ".confidential-ledger.azure.com" 

DefaultAzureCredential クラスを使用して Azure に対して認証を行います。

# Setup authentication 
credential = DefaultAzureCredential() 

次に、証明書クライアントを使用して Confidential Ledger ID URL から Confidential Ledger サービス証明書を取得し、保存します。 サービス証明書は、TLS サーバー認証の信頼のルートとして使用されるネットワーク ID 公開キー証明書です。 つまり、それは CCF ネットワーク内のいずれかのノードとの TLS 接続を確立するための証明機関 (CA) として使用されます。

# Create a Certificate client and use it to 
# get the service identity for our ledger 
identity_client = ConfidentialLedgerCertificateClient(identity_url) 
network_identity = identity_client.get_ledger_identity( 
     ledger_id=ledger_name 
)

# Save network certificate into a file for later use 
ledger_tls_cert_file_name = "network_certificate.pem" 

with open(ledger_tls_cert_file_name, "w") as cert_file: 
    cert_file.write(network_identity["ledgerTlsCertificate"]) 

次に、資格情報、取得されたネットワーク証明書、一意の台帳 URL を使用して、Confidential Ledger クライアントを作成できます。

# Create Confidential Ledger client 
ledger_client = ConfidentialLedgerClient( 
     endpoint=ledger_url,  
     credential=credential, 
     ledger_certificate_path=ledger_tls_cert_file_name 
) 

Confidential Ledger クライアントを使用して、Azure Confidential Ledger インスタンスでサポートされている任意の操作を実行できます。 たとえば、新しいエントリを台帳に追加し、対応する書き込みトランザクションがコミットされるのを待つことができます。

# The method begin_create_ledger_entry returns a poller that  
# we can use to wait for the transaction to be committed 
create_entry_poller = ledger_client.begin_create_ledger_entry( 
    {"contents": "Hello World!"} 
)

create_entry_result = create_entry_poller.result() 

トランザクションがコミットされた後、クライアントを使用して、それぞれのトランザクション ID を使用して、前の手順で台帳に追加したエントリに対するレシートを取得できます。

# The method begin_get_receipt returns a poller that  
# we can use to wait for the receipt to be available by the system 
get_receipt_poller = ledger_client.begin_get_receipt( 
    create_entry_result["transactionId"] 
)

get_receipt_result = get_receipt_poller.result() 

サンプル コード

コードのチュートリアルで使用したサンプル コードの全体を示します。

import json 

# Import the Azure authentication library 
from azure.identity import DefaultAzureCredential 

# Import the Confidential Ledger Data Plane SDK 
from azure.confidentialledger import ConfidentialLedgerClient 
from azure.confidentialledger.certificate import ConfidentialLedgerCertificateClient 

from receipt_verification import verify_receipt 

# Constants 
ledger_name = "<your-unique-ledger-name>" 
identity_url = "https://identity.confidential-ledger.core.azure.com" 
ledger_url = "https://" + ledger_name + ".confidential-ledger.azure.com" 

# Setup authentication 
credential = DefaultAzureCredential() 

# Create Ledger Certificate client and use it to 
# retrieve the service identity for our ledger 
identity_client = ConfidentialLedgerCertificateClient(identity_url) 
network_identity = identity_client.get_ledger_identity(ledger_id=ledger_name) 

# Save network certificate into a file for later use 
ledger_tls_cert_file_name = "network_certificate.pem" 

with open(ledger_tls_cert_file_name, "w") as cert_file: 
    cert_file.write(network_identity["ledgerTlsCertificate"]) 

# Create Confidential Ledger client 
ledger_client = ConfidentialLedgerClient( 
    endpoint=ledger_url, 
    credential=credential, 
    ledger_certificate_path=ledger_tls_cert_file_name, 
) 

# The method begin_create_ledger_entry returns a poller that 
# we can use to wait for the transaction to be committed 
create_entry_poller = ledger_client.begin_create_ledger_entry( 
    {"contents": "Hello World!"} 
) 
create_entry_result = create_entry_poller.result() 

# The method begin_get_receipt returns a poller that 
# we can use to wait for the receipt to be available by the system 
get_receipt_poller = ledger_client.begin_get_receipt( 
    create_entry_result["transactionId"] 
) 
get_receipt_result = get_receipt_poller.result() 

# Save fetched receipt into a file
with open("receipt.json", "w") as receipt_file: 
    receipt_file.write(json.dumps(get_receipt_result, sort_keys=True, indent=2)) 

書き込みトランザクション レシートの内容

GET_RECEIPT エンドポイントを呼び出すときに Azure Confidential Ledger インスタンスによって返される JSON 応答ペイロードの例を次に示します。

{
    "receipt": {
        "cert": "-----BEGIN CERTIFICATE-----\nMIIB0jCCAXmgAwIBAgIQPxdrEtGY+SggPHETin1XNzAKBggqhkjOPQQDAjAWMRQw\nEgYDVQQDDAtDQ0YgTmV0d29yazAeFw0yMjA3MjAxMzUzMDFaFw0yMjEwMTgxMzUz\nMDBaMBMxETAPBgNVBAMMCENDRiBOb2RlMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcD\nQgAEWy81dFeEZ79gVJnfHiPKjZ54fZvDcFlntFwJN8Wf6RZa3PaV5EzwAKHNfojj\noXT4xNkJjURBN7q+1iE/vvc+rqOBqzCBqDAJBgNVHRMEAjAAMB0GA1UdDgQWBBQS\nwl7Hx2VkkznJNkVZUbZy+TOR/jAfBgNVHSMEGDAWgBTrz538MGI/SdV8k8EiJl5z\nfl3mBTBbBgNVHREEVDBShwQK8EBegjNhcGljY2lvbmUtdGVzdC1sZWRnZXIuY29u\nZmlkZW50aWFsLWxlZGdlci5henVyZS5jb22CFWFwaWNjaW9uZS10ZXN0LWxlZGdl\ncjAKBggqhkjOPQQDAgNHADBEAiAsGawDcYcH/KzF2iK9Ldx/yABUoYSNti2Cyxum\n9RRNKAIgPB/XGh/FQS3nmZLExgBVXkDYdghQu/NCY/hHjQ9AvWg=\n-----END CERTIFICATE-----\n",
        "leafComponents": {
            "claimsDigest": "0000000000000000000000000000000000000000000000000000000000000000",
            "commitEvidence": "ce:2.40:f36ffe2930ec95d50ebaaec26e2bec56835abd051019eb270f538ab0744712a4",
            "writeSetDigest": "8452624d10bdd79c408c0f062a1917aa96711ea062c508c745469636ae1460be"
        },
        "nodeId": "70e995887e3e6b73c80bc44f9fbb6e66b9f644acaddbc9c0483cfc17d77af24f",
        "proof": [
            {
                "left": "b78230f9abb27b9b803a9cae4e4cec647a3be1000fc2241038867792d59d4bc1"
            },
            {
                "left": "a2835d4505b8b6b25a0c06a9c8e96a5204533ceac1edf2b3e0e4dece78fbaf35"
            }
        ],
        "signature": "MEUCIQCjtMqk7wOtUTgqlHlCfWRqAco+38roVdUcRv7a1G6pBwIgWKpCSdBmhzgEdwguUW/Cj/Z5bAOA8YHSoLe8KzrlqK8="
    },
    "state": "Ready",
    "transactionId": "2.40"
}

JSON 応答には、ルート レベルで次のフィールドが含まれています。

  • receipt: 対応する書き込みトランザクションのレシートの有効性を確認するために使用できる値が含まれています。

  • state: 返された JSON 応答の状態。 次の値が使用される可能性があります。

    • Ready: 応答で返されたレシートが使用可能です
    • Loading: レシートはまだ取得できないため、要求を再試行する必要があります
  • transactionId: 書き込みトランザクション レシートに関連付けられているトランザクション ID。

receipt フィールドには、次のフィールドが含まれます。

  • cert: 書き込みトランザクションに署名した CCF ノードの PEM 公開キー証明書を含む文字列。 署名ノードの証明書は、サービス ID 証明書により、常に承認されている必要があります。 また、トランザクションが定期的に署名される方法と、署名トランザクションが CCF の台帳に追加されるしくみの詳細については、次のリンクを参照してください。

  • nodeId: 署名 CCF ノードの公開キーの SHA-256 ハッシュ ダイジェストを表す 16 進文字列。

  • leafComponents: マークル ツリー内の、指定されたトランザクションに関連付けられているリーフ ノード ハッシュのコンポーネント。 マークル ツリーは、すべてのトランザクションのハッシュを記録し、台帳の整合性を保証するツリー データ構造です。 CCF でマークル ツリーがどのように使われるかについて詳しくは、関連する CCF ドキュメントを参照してください。

  • proof: マークル ツリーのノード ハッシュを表すキーと値のペアのリスト。特定のトランザクションに対応するリーフ ノード ハッシュと組み合わせると、ツリーのルート ハッシュの再計算が可能になります。 マークル ツリーのプロパティにより、ノードのサブセットのみでツリーのルート ハッシュを再計算できます。 このリストの要素は、キーと値のペアの形式になっています。キーは、ツリー内の特定のレベルでの親ノードに対する相対位置を示します。値は、特定のノードの SHA-256 ハッシュ ダイジェストであり、16 進文字列です。

  • serviceEndorsements: 以前のサービス ID 証明書を表す、PEM でエンコードされた証明書文字列のリスト。 署名ノードを承認したサービス ID は、レシートを発行したサービス ID と同じではない可能性があります。 たとえば、Confidential Ledger のディザスター リカバリー後にサービス証明書が更新されます。 監査者は過去のサービス証明書のリストを使用して、CCF 署名ノードから現在のサービス証明書までの信頼チェーンを構築できます。

  • signature: 署名 CCF ノードによる、特定のトランザクションでのマークル ツリーのルートの署名を表す Base64 文字列。

leafComponents フィールドには、次のフィールドが含まれます。

  • claimsDigest: トランザクション実行時に Confidential Ledger アプリケーションによってアタッチされた、アプリケーション要求の SHA-256 ハッシュ ダイジェストを表す 16 進文字列。 アプリケーション要求は現在サポートされておらず、書き込みトランザクション実行時に Confidential Ledger アプリケーションで要求はアタッチされません。

  • commitEvidence: トランザクション ID と台帳シークレットから派生した、トランザクションごとに生成される一意の文字列。 コミットの証拠について詳しくは、関連する CCF ドキュメントを参照してください。

  • writeSetDigest: キーと値のストアの SHA-256 ハッシュ ダイジェストを表す 16 進文字列。トランザクション完了時に書き込まれたすべてのキーと値が含まれます。 書き込みセットの詳細については、関連する CCF ドキュメントを参照してください。

アプリケーション要求

Azure Confidential Ledger アプリケーションは、アプリケーション要求と呼ばれる任意のデータをアタッチすることで、トランザクションを書き込むことができます。 これらの要求は、書き込み操作中に実行されるアクションを表しています。 トランザクションにアタッチすると、要求オブジェクトの SHA-256 ダイジェストが台帳に追加され、書き込みトランザクションの一部としてコミットされます。 書き込みトランザクションに要求を追加すると、要求ダイジェストがインプレースで署名され、改ざんできないことが保証されます。

その後、アプリケーション要求は、追加されたのと同じトランザクションに対応するレシート ペイロードのプレーンな形式で表示することができます。 公開された要求を使用すると、トランザクション中、ユーザーは台帳によってインプレースでアタッチおよび署名されたのと同じ要求ダイジェストを再計算することができます。 要求ダイジェストは、書き込みトランザクションのレシート検証プロセスの一環として使用でき、ユーザーは記録された要求の信頼性に対する検証をオフラインで完全に行うことができます。

現在、アプリケーション要求はプレビュー API バージョン 2023-01-18-preview でサポートされています。

アプリケーション要求を使用してトランザクション レシートのコンテンツを書き込む

GET_RECEIPT エンドポイントを呼び出すときに、アプリケーション要求を記録した Azure Confidential Ledger インスタンスによって返される JSON 応答ペイロードの例を次に示します。

{
  "applicationClaims": [
    {
      "kind": "LedgerEntry",
      "ledgerEntry": {
        "collectionId": "subledger:0",
        "contents": "Hello world",
        "protocol": "LedgerEntryV1",
        "secretKey": "Jde/VvaIfyrjQ/B19P+UJCBwmcrgN7sERStoyHnYO0M="
      }
    }
  ],
  "receipt": {
    "cert": "-----BEGIN CERTIFICATE-----\nMIIBxTCCAUygAwIBAgIRAMR89lUNeIghDUfpyHi3QzIwCgYIKoZIzj0EAwMwFjEU\nMBIGA1UEAwwLQ0NGIE5ldHdvcmswHhcNMjMwNDI1MTgxNDE5WhcNMjMwNzI0MTgx\nNDE4WjATMREwDwYDVQQDDAhDQ0YgTm9kZTB2MBAGByqGSM49AgEGBSuBBAAiA2IA\nBB1DiBUBr9/qapmvAIPm1o3o3LRViSOkfFVI4oPrw3SodLlousHrLz+HIe+BqHoj\n4nBjt0KAS2C0Av6Q+Xg5Po6GCu99GQSoSfajGqmjy3j3bwjsGJi5wHh1pNbPmMm/\nTqNhMF8wDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQUCPaDohOGjVgQ2Lb8Pmubg7Y5\nDJAwHwYDVR0jBBgwFoAU25KejcEmXDNnKvSLUwW/CQZIVq4wDwYDVR0RBAgwBocE\nfwAAATAKBggqhkjOPQQDAwNnADBkAjA8Ci9myzieoLoIy+7mUswVEjUG3wrEXtxA\nDRmt2PK9bTDo2m3aJ4nCQJtCWQRUlN0CMCMOsXL4NnfsSxaG5CwAVkDwLBUPv7Zy\nLfSh2oZ3Wn4FTxL0UfnJeFOz/CkDUtJI1A==\n-----END CERTIFICATE-----\n",
    "leafComponents": {
      "claimsDigest": "d08d8764437d09b2d4d07d52293cddaf40f44a3ea2176a0528819a80002df9f6",
      "commitEvidence": "ce:2.13:850a25da46643fa41392750b6ca03c7c7d117c27ae14e3322873de6322aa7cd3",
      "writeSetDigest": "6637eddb8741ab54cc8a44725be67fd9be390e605f0537e5a278703860ace035"
    },
    "nodeId": "0db9a22e9301d1167a2a81596fa234642ad24bc742451a415b8d653af056795c",
    "proof": [
      {
        "left": "bcce25aa51854bd15257cfb0c81edc568a5a5fa3b81e7106c125649db93ff599"
      },
      {
        "left": "cc82daa27e76b7525a1f37ed7379bb80f6aab99f2b36e2e06c750dd9393cd51b"
      },
      {
        "left": "c53a15cbcc97e30ce748c0f44516ac3440e3e9cc19db0852f3aa3a3d5554dfae"
      }
    ],
    "signature": "MGYCMQClZXVAFn+vflIIikwMz64YZGoH71DKnfMr3LXkQ0lhljSsvDrmtmi/oWwOsqy28PsCMQCMe4n9aXXK4R+vY0SIfRWSCCfaADD6teclFCkVNK4317ep+5ENM/5T/vDJf3V4IvI="
  },
  "state": "Ready",
  "transactionId": "2.13"
}

前のセクションで示したレシート例と比較すると、JSON 応答には、書き込みトランザクション中に台帳によって記録されたアプリケーション要求の一覧を表す別の applicationClaims フィールドが含まれています。 applicationClaims リスト内の各オブジェクトには、次のフィールドが含まれています。

  • kind: アプリケーション要求の種類を表します。 値は、指定された型のアプリケーション要求オブジェクトを解析する方法を示します。

  • ledgerEntry: 台帳エントリ データから派生したアプリケーション要求を表します。 要求には、書き込みトランザクション中にアプリケーションによって記録されたデータ (コレクション ID やユーザーによって提供されたコンテンツなど) と、単一の要求オブジェクトに対応するダイジェストを計算するために必要な情報が含まれます。

  • digest: ダイジェスト形式のアプリケーション要求を表します。 この要求オブジェクトには、アプリケーションによって事前計算されたダイジェストと、その計算に使用されたプロトコルが含まれます。

ledgerEntry フィールドには、次のフィールドが含まれます。

  • protocol: 指定された要求データから要求のダイジェストを計算するために使用されるプロトコルを表します。

  • collectionId: 対応する書き込みトランザクション中に書き込まれたコレクションの識別子。

  • contents: 対応する書き込みトランザクション中に書き込まれた台帳のコンテンツ。

  • secretKey: base64 でエンコードされた秘密キー。 このキーは、要求ダイジェストを取得するために、アプリケーション要求で指定された値と共に HMAC アルゴリズムで使用されます。

digest フィールドには、次のフィールドが含まれます。

  • protocol: 指定された要求のダイジェストを計算するために使用されるプロトコルを表します。

  • value: アプリケーション要求のダイジェスト (16 進数形式)。 この値は、アプリケーション要求の完全なダイジェストを計算するために、protocol 値でハッシュする必要があります。

その他のリソース

書き込みトランザクション レシートと、各トランザクションの整合性が CCF で保証されるしくみの詳細については、次のリンクを参照してください。

次のステップ