Previous << Accounts
Next >> Scripts
トランザクションとは、暗号署名されたデータメッセージで、Flowの状態(ステート)を更新する一連の指示が含まれています。これらは実行ノードによって実行される計算の基本単位です。トランザクションをFlowブロックチェーンに含めるには、支払者からの手数料が必要になります。
TIP
Flow上でのトランザクションは、Ethereum上でのトランザクションとは根本的に異なります。トランザクションの主な目的は、送金ではなく、実行されるコードを含めることです。これにより、トランザクションは非常に柔軟かつ強力なものとなります。承認したアカウントのプライベート資産にアクセスできることに加え、トランザクションはパブリック・スマートコントラクトの読み取りや呼び出し、他のユーザーのアカウントのパブリックドメインへのアクセスも可能です。Flowのトランザクションには、サードパーティによる支払者アカウント、提案者アカウント、承認者アカウントの定義など、異なる役割も備わっています。これについては、後ほど詳しく説明します。
トランザクションが有効に実行されるためには、関係するアカウントの署名とその他の情報を含める必要があります。それでは、必要なすべてのフィールドを見ていきましょう。
Script
スクリプトのセクションには、トランザクション実行の指示が含まれています。これはソースコード形式(人間が読める形式)のCadenceプログラムで、UTF-8でエンコードされています。トランザクションプログラムには、transaction
宣言が含まれていなければなりません。
トランザクションは、任意の複数のフェーズ(prepare
、pre
、execute
、post
フェーズ)が含みます。詳細は、Cadenceのトランザクションに関するリファレンス文書を参照してください。各フェーズには目的があり、最も重要なフェーズはprepare
とexecute
です。
prepare
フェーズでは、&Account
オブジェクトにアクセスでき、それによりアカウントとやりとりできるようになります。アカウントはトランザクションのオーソライザー(authorizers)と呼ばれます。そのため、prepare
フェーズでやりとりしたい各アカウントは、オーソライザーとしてトランザクションに署名する必要があります。execute
フェーズは、その名の通り、トランザクションのメインロジックを実行します。このフェーズは任意ですが、メインのトランザクションロジックをこのセクションに追加するのがベストプラクティスです。
Cadenceのトランザクションに関するドキュメントを再度お読みください。
これはトランザクションスクリプトの一例です。
transaction(greeting: String) {
execute {
log(greeting.concat(", World!"))
}
}
Arguments
トランザクションは実行中に必要とするパラメータを宣言することができ、トランザクションを送信する際には、これらのパラメータを入力引数として渡す必要があります。これらは関数の引数として考えることができます。現在、Flowでは引数をJSON-Cadenceデータ交換フォーマットで渡しています。これは人間が読めるJSON形式です。上述のサンプルスクリプトは、単一のString
引数を受け入れます。
Reference Block
有効期限に使用される最新のブロックに対する参照です。トランザクションは、reference blockの高さ + N 以降に Flow に対して送信された場合、有効期限切れとみなされます。ここで、N はネットワークによって定義された定数です。メインネットでは、現在の N の設定は 600 で、有効期限切れまでの時間は約 10 分です(変更される可能性があることにご注意ください)。
Gas Limit
トランザクションが実行されると、各オペレーションで事前に定義された量の演算ユニットが消費されます(詳細はFeesのドキュメントで説明しています)。これにより、そのトランザクションで実行が許可される演算の最大量が定義されます。トランザクションが上限よりも少ない演算ユニットで実行を完了した場合、影響はありません。しかし、実行中にこの上限に達した場合、トランザクションは失敗し、変更は元に戻りますが、手数料は適用されます。Flowメインネットの計算リミットの最大値は現在9999ですが、今後変更される可能性があります。ネットワークの最大リミットは、無限に実行される可能性のあるトランザクションからネットワークを保護するために定義されています。
Proposal Key
各トランザクションは提案者キーを宣言する必要があり、これは任意のFlowアカウント(アプリ、ユーザー、またはウォレット)のアカウントキーとすることができます。提案者キーを所有するアカウントはproposerと呼ばれます。
Proposer は、トランザクションを提案する人物を定義するトランザクション内での役割です。提案者におけるトランザクションが送信されることの影響は、提案者キーのシーケンス番号をインクリメントすることです。これは、トランザクションが再送信(リプレイ攻撃)されないようにし、シーケンス処理を確実に行うために行われます。
提案者キーの定義では、アカウントキー持ち主のアドレス、キーID、最新のシーケンス番号を宣言します。一つのProposerは、使用するキーによってのみ制限される、多くのトランザクションを並行して実行させることができます。(補足: Proposerが複数のトランザクションの承認を並行して行う為にはシーケンス番号が異なることが必要です)
- Address は、このトランザクションの提案者となるアカウントを識別します。
- キーIDは、アドレスで指定されたアカウントのキーを識別するインデックス番号(0から始まる)です。
- シーケンス番号は、各キーに割り当てられた番号で、トランザクションごとに1ずつ増加します。これにより、各トランザクションは最大でも1回だけ実行されることが保証され、トランザクションリプレイ攻撃などの多くの望ましくない状況が防止されます。アカウント内の各キーには、専用のシーケンス番号が割り当てられています。Ethereumとは異なり、アカウント全体に共通のシーケンス番号はありません。
Authorizers
Authorizersは、トランザクションがそれらの状態(ステート)を読み取り変更することを承認するアカウントです。トランザクションは、アクセスする必要のあるアカウントの数に応じて、ゼロまたは複数のAuthorizersを指定できます。
トランザクションのAuthorizersの数は、Cadenceスクリプトのprepareステートメントで宣言された&Accountパラメータの数と一致する必要があります。
複数のAuthorizersを持つトランザクションの例:
transaction {
prepare(authorizer1: auth(Capabilities) &Account, authorizer2: auth(Storage) &Account) { }
}
承認者として定義された各アカウントは、自身のキーでトランザクションに署名する必要があります。そうすることで、署名したトランザクションがそのアカウントにアクセスし、変更できることを承認することになります。どのようにアカウントのデータを変更するかについては、prepare
の引数のリストで許可されたアカウントEntitlementのリストと、トランザクションスクリプトを読み取ることで理解できます。トランザクションでは、開発者はトランザクションを適切に実行するために必要な最小限のアカウントEntitlementのみを付与すべきです。これにより、トランザクションに署名するユーザーは、トランザクションがアカウントのどの部分にアクセスできるかを理解できます。
Payer
Payerとは、取引の料金を支払うアカウントです。取引では、Payerを正確に1つ指定する必要があります。Payerはネットワークフィーとガスフィーの支払いの責任を負うだけであり、トランザクションはPayerアカウントに保存されているリソースやコードへのアクセス権限を持っていません。
Payerを明示的に指定できるようにすることで、トランザクションはウォレットプロバイダーなどのサードパーティサービスによって支払われるようになります。
Transaction Lifecycle
AccessノードAPIを使用してトランザクションがFlowネットワークに送信されると、そのライフサイクルが開始され、最終的に確定します。送信された各トランザクションはIDで識別されます。
Transaction ID
トランザクションIDは、エンコードされたトランザクションペイロードのハッシュであり、いつでも計算することができます。トランザクションIDは、データから導出できるため、それはデータの重複を意味することになり、トランザクションペイロードの一部として送信することはありません。
Transaction Status
トランザクションステータスは、Flowブロックチェーン上のトランザクションの状態を表します。ステータスの一部はその後変化し、一部はそれ以後変化しません。通常、以下のようなタイムラインに従います。
- Unknown - トランザクションが通信先のネットワークのセクションでまだ確認されていません。
- Pending - トランザクションは収集(collection)ノードで受信済みですが、ブロックでまだ確定されていません。
- Finalized(確定) - コンセンサスノードがトランザクションをブロックに含めましたが、実行ノードで実行されていません。
- Executed - 実行ノードがトランザクションの結果を生成しました。
- Sealed(封印) - 検証(verification)ノードがトランザクションの結果を検証し、合意し、そしてコンセンサスノードが最新ブロックに封印した。
- Expired - トランザクションが有効期限を表すブロック高さを過ぎて送信された。
DANGER
重要なのは、トランザクションステータスとトランザクション結果を区別することです。 トランザクションステータスは、トランザクションがブロックチェーンに組み込まれたかどうかに関する情報のみを提供し、トランザクションが意図した通りに実行されたかどうかは提供しません。トランザクションは、意図した通りに実行されず、封印されてしまうこともあります。
Transaction Result
トランザクションが実行されると、その結果が利用可能になり、成功したか、実行中にエラーが発生したかなどの詳細が提供されます。また、トランザクションが発行したイベントも含まれます。
DANGER
開発者の観点では、トランザクションは以下の条件を満たした場合のみ成功したとみなされます。
- 封印されている
- エラーが発生していない
Transaction Time
異なるブロックチェーン間のトランザクション時間の仕組みを理解することは、開発者やユーザーが業務(operations)や期待値を最適化する上で極めて重要です。Flowのマルチノードアーキテクチャは、チェーン間で最速のトランザクション時間と確定時間を実現します。その仕組みと開発者やユーザーにとってのそれの意味について、さらに詳しく説明していきます。
Two Key Transaction Questions
トランザクションが処理される際には、常に2つの主な疑問が浮かびます。
- Inclusion:このトランザクションは最終的なチェーン(final chain)に含まれるか?
- Result:トランザクションの結果は何か?
異なるブロックチェーンでは、これらの疑問にさまざまな順序で取り組んでいます。例えば、ビットコインとイーサリアムは同時に回答を提供しています。レイヤー2ソリューション(L2)は、時として、Inclusionの確認前に結果に対処することができます。しかし、問題があります。これらの疑問に対する回答は、間違っている可能性があるのです。一方、FlowはInclusionの疑問を優先します。
Transaction Finality
従来の金融に例えると、Visaがトランザクションを承認したかどうかはベンダー(加盟店)が即座に把握できるかもしれませんが、チャージバック(支払い差戻し)の可能性は数週間残ることになります。この不確実性により、ブロックチェーントランザクションには"最終性(finality)"という概念が導入されます。
ビットコインを除いたほとんどのチェーンを含む支配的なProof-of-Stake(PoS)環境では、3つの主要な最終段階(finality stage)があります:
- Preliminary result(予備結果):前述の疑問に対する最初の回答です。予備結果は正確性を保証するものではなく、情報提供者が虚偽の情報を提供した場合でも、経済的なペナルティ(「スラッシング※」など)はありません。
- Soft economic finality(ソフトな経済的結論):この段階では、暗号学的証明に裏付けられた回答が提供されます。情報提供者が不正行為を行った場合、経済的な影響または「スラッシング※」が課されます。
- Hard economic finality(ハードな経済的結論):提供された回答が正しいか、またはブロックチェーン全体が再起動する必要があります。後者の場合、少なくともノードの3分の1が経済的なペナルティを受けます。
(※スラッシングとは Proof-of-Stake(PoS)ネットワークにおけるペナルティの仕組みであり、ルール違反をしたバリデータに罰を与えるもの)
Chain Comparisons
Chain | Preliminary | Soft finality | Hard finality |
---|---|---|---|
Solana | 100ms | n/a | ~30s |
Ethereum | 15s | n/a | ~15m |
Flow | bypass | 6s | ~14s |
Flow
Flowは予備結果を完全にバイパスします。約6秒でソフトファイナル("Executed")に達し、約14秒でハードファイナル("Sealed")に達します。もしFlowのアクセスノードがトランザクションが発生した(occurred)と報告した(states)場合、その報告は正しいか、ノードのスラッシングにつながる暗号学的証明が存在することを意味します。
Signing a Transaction
重みづけられたキーおよび分割された署名の役割が存在するため、Flowのトランザクションは、1つまたは複数の当事者によって複数回署名される必要があります。つまり、1つのトランザクションを承認するには、複数の固有の署名が必要になる場合があります。
トランザクションは、ペイロード署名とエンベロープ署名という2種類の署名を含めることができます。
Signer Roles
- Proposer:提案者キーを指定するアカウント。
- Payer:トランザクション手数料を支払うアカウント。
- Authorizers:状態(ステート)を変更するトランザクションを承認するゼロまたは複数のアカウント。
Payload
トランザクションペイロードはトランザクションの最も内側の部分であり、上記で定義したように、トランザクションによって適用された操作(operations)を一意に識別するデータを含んでいます。Flowでは、同じペイロードを持つ2つのトランザクションが同時に実行されることは決してありません。
WARNING
⚠️ トランザクションの提案者および承認者は、トランザクションペイロードに署名するだけでよい。これらの署名がペイロード署名です。
Authorization Envelope
トランザクション承認封筒(transaction authorization envelope)には、トランザクションペイロードとペイロード署名の両方が含まれています。
トランザクションの支払者は、承認封筒に署名することが求められます。これらの署名がエンベロープ署名です。
DANGER
特別なケース(Special case):もしアカウントが支払者であり、かつ提案者または承認者のいずれかである場合、封筒に署名することだけが求められる。
Payment Envelope
トランザクションの最も外側の部分で、ペイロードとエンベロープ署名を含む部分は、ペイメント・エンベロープと呼ばれます。
Payer Signs Last
Payerはペイロード署名を含むトランザクション部分に署名する必要があります。つまり、Payerは常に最後に署名しなければなりません。これにより、Payerは必要なペイロード署名がすべて揃った有効なトランザクションに署名していることが保証されます。
DANGER
特別なケース(Special case):もしアカウントが支払者であり、かつ提案者または承認者のいずれかである場合、封筒に署名することだけが求められる。
Signature Structure
トランザクション署名は、以下の3つのフィールドを含む複合構造です。
- Address
- Key ID
- Signature Data
アドレス およびkey ID フィールドは、署名を生成したアカウントのキーを宣言します。これは、正しい公開鍵に対して署名を検証するために必要です。
Sequence Numbers
Flowは、各トランザクションが最大でも1回だけ実行されるように、シーケンス番号を使用しています。これにより、トランザクションリプレイ攻撃などの多くの望ましくない状況を防止することができます。
シーケンス番号は、イーサリアムのトランザクションノンス(nonce)と類似した機能を持ちますが、いくつかの重要な違いがあります。
- アカウント内の各キーには、専用のシーケンス番号が関連付けられています。イーサリアムとは異なり、アカウント全体に共通のシーケンス番号はありません。
- トランザクションを作成する際、Proposerのみがシーケンス番号を指定する必要があります。支払者および承認者は、その必要はありません。
TIP
トランザクション提案者は、複数の鍵で署名する場合でも、単一のアカウントの鍵に対応するシーケンス番号のみ指定する必要があります。この鍵は提案者鍵と呼ばれます。
アカウントの鍵が提案者キーとして使用されるたびに、そのシーケンス番号は1ずつインクリメントされます。トランザクションが実行中に失敗(差し戻し)した場合でも、シーケンス番号は実行後に更新されます。
トランザクションは、その提案者キーが実行時 にアカウントに保存されているシーケンス番号と等しいシーケンス番号を指定しなかった場合、失敗します。
Common Signing Scenarios
以下は、トランザクションを承認するために異なる署名の組み合わせが必要となるいくつかのシナリオです。
Single party, single signature
最もシンプルなFlowのトランザクションでは、単一のアカウントを提案者、支払者、および承認者として宣言します。この場合、アカウントは単一の署名でトランザクションに署名できます。
このシナリオは、署名の重みがfull(=1000である)なキーによって署名が生成された場合のみ可能です。
Account | Key ID | Weight |
---|---|---|
0x01 | 1 | 1000 |
{
"payload": {
"proposalKey": {
"address": "0x01",
"keyId": 1,
"sequenceNumber": 42
},
"payer": "0x01",
"authorizers": [ "0x01" ]
},
"payloadSignatures": [], /* 0x01 is the payer, so only needs to sign envelope */
"envelopeSignatures": [
{
"address": "0x01",
"keyId": 1,
"sig": "0xabc123"
}
]
}
Single party, multiple signatures
単一のアカウントを提案者、支払者、承認者として指定するトランザクションであっても、そのアカウントが加重されたキーを使用してマルチシグ機能を実現している場合、複数の署名を指定する場合があります。
Account | Key ID | Weight |
---|---|---|
0x01 | 1 | 500 |
0x01 | 2 | 500 |
{
"payload": {
"proposalKey": {
"address": "0x01",
"keyId": 1,
"sequenceNumber": 42
},
"payer": "0x01",
"authorizers": [ "0x01" ]
},
"payloadSignatures": [], /* 0x01 is the payer, so only needs to sign envelope */
"envelopeSignatures": [
{
"address": "0x01",
"keyId": 1,
"sig": "0xabc123"
},
{
"address": "0x01",
"keyId": 2,
"sig": "0xdef456"
}
]
}
Multiple parties
署名する役割ごとに異なるアカウントを宣言するトランザクションでは、各アカウントから少なくとも1つの署名が必要となります。
Account | Key ID | Weight |
---|---|---|
0x01 | 1 | 1000 |
0x02 | 1 | 1000 |
{
"payload": {
"proposalKey": {
"address": "0x01",
"keyId": 1,
"sequenceNumber": 42
},
"payer": "0x02",
"authorizers": [ "0x01" ]
},
"payloadSignatures": [
{
"address": "0x01", /* 0x01 is not payer, so only signs payload */
"keyId": 1,
"sig": "0xabc123"
}
],
"envelopeSignatures": [
{
"address": "0x02",
"keyId": 1,
"sig": "0xdef456"
},
]
}
Multiple parties, multiple signatures
もしマルチシグ機能を実現するために加重キーを使用しているアカウントの場合、署名の役割ごとに異なるアカウントを宣言するトランザクションは、アカウントごとに複数の署名が必要になる場合があります。
Account | Key ID | Weight |
---|---|---|
0x01 | 1 | 500 |
0x01 | 2 | 500 |
0x02 | 1 | 500 |
0x02 | 2 | 500 |
{
"payload": {
"proposalKey": {
"address": "0x01",
"keyId": 1,
"sequenceNumber": 42
},
"payer": "0x02",
"authorizers": [ "0x01" ]
},
"payloadSignatures": [
{
"address": "0x01", /* 0x01 is not payer, so only signs payload */
"keyId": 1,
"sig": "0xabc123"
},
{
"address": "0x01", /* 0x01 is not payer, so only signs payload */
"keyId": 2,
"sig": "0x123abc"
}
],
"envelopeSignatures": [
{
"address": "0x02",
"keyId": 1,
"sig": "0xdef456"
},
{
"address": "0x02",
"keyId": 2,
"sig": "0x456def"
},
]
}
Transaction Submission and Retrieval
Flow CLI を使用して、ID から既存のトランザクションを取得することができます。
flow transactions get 1ec90051e3bc74fc36cbd16fc83df08e463dda8f92e8e2193e061f9d41b2ad92 -n mainnet
コマンドに関する詳細情報は、CLIのドキュメントを参照してください。
ユーザーは独自のトランザクションを定義することも、FLIX(Flow Interaction templates)サービスを使用して見つかるコントラクト作者によってすでに定義されたトランザクションを使用することもできます。
トランザクションはアクセスノードAPIから送信および取得でき、現在、gRPCとRESTの2つのAPIがあります。それらに関する詳細情報は、こちらでご覧いただけます。
上記APIを実装した複数のSDKが、さまざまな言語向けに提供されています。
全SDKの一覧は こちらからご確認いただけます。
Last updated on Dec 14, 2024 by BT.Wood(Tang Bo Hao)
翻訳元
Flow BlockchainのCadence version1.0ドキュメント (Transactions)