トランザクションは、1つまたは複数のアカウントの鍵で署名されたオブジェクトであり、チェーンに送信されてチェーンとやり取りし、状態(ステート, state)の変更を実行します。
トランザクションは、import構文を使用して、任意のアカウントから任意の型を好きなだけimportすることができます。
import FungibleToken from 0x01
トランザクションはtransactionキーワードを使用して宣言され、その内容は波括弧内に含まれます。
トランザクションの本体では、トランザクション全体で有効なローカル変数を宣言することができます。
transaction {
// transaction contents
let localVar: Int
// ...
}
Transaction parameters
トランザクションにはパラメータを指定することができます。 トランザクションのパラメータは、関数(function)のパラメータと同じように宣言します。 トランザクションの引数はトランザクション実行時に一緒に渡します。
このトランザクションのパラメータには、トランザクション内のどこからでもアクセスが可能です。
// Declare a transaction which has one parameter named `amount`
// that has the type `UFix64`
//
transaction(amount: UFix64) {
}
Transaction phases
トランザクションは、prepare、pre、execute、postの4つのフェーズで順番に実行されます。prepareとexecuteのフェーズは、順番にシーケンス実行されるコードのブロックです。pre-condition(事前条件)とpost-condition(事後条件)は、関数の条件のものと似ています。
以下の空のCadenceトランザクションにはロジックがありませんが、各フェーズのシンタックスがこのフェーズの順番で実行されることをデモンストレーションしています。
transaction {
prepare(signer1: &Account, signer2: &Account) {
// ...
}
pre {
// ...
}
execute {
// ...
}
post {
// ...
}
}
オプショナル(必須ではない)ではあるものの、トランザクションを実行する際には各フェーズが特定の目的を果たしており、開発者はトランザクションを作成する際にこれらのフェーズを使用することが推奨されます。
Prepare phase
prepareのフェーズは、トランザクションがそのトランザクションに署名(承認)したアカウントへのアクセスを必要とする場合に利用されます。
署名アカウントへのアクセスは、prepareフェーズ内でのみ可能です。
prepareフェーズの引数には、トランザクションに署名したアカウントの数だけ、署名するアカウントに対する参照(reference)が渡されます。参照は認証済みであり、アカウントに対する、あるアクセスを要求できます。
例えば、トランザクションに2人の署名者がいる場合、prepareには&Account型のパラメータが2つ必要です。
prepare(signer1: &Account, signer2: &Account) {
// ...
}
例えば、アカウントのストレージへの書き込みアクセスを要求する場合、トランザクションは権限を持った参照(reference)をリクエストできます。
prepare(signer: auth(Storage) &Account) {
// ...
}
ベストプラクティスとしては、署名を行うアカウントに対する書き込みアクセスを必要とするロジックだけをprepareフェーズに書き、それ以外のロジックは他の場所へ移動させるのです。
アカウントへの変更は重大な影響を及ぼす可能性があるため、このフェーズには無関係なロジックを入れないようにしてください。これにより、ユーザーはトランザクションのロジックを簡単に読み取り、理解し、それがアカウントにどのような影響を与えるかを簡単に把握することができます。
prepareフェーズは、イニシャライザー(init関数)と同様の目的を果たします。
例えば、トランザクションでトークンの転送を行う場合は、アカウントのストレージに対するアクセスが必要なため、withdrawロジックをprepareフェーズに置き、depositロジックはexecuteフェーズに置きます。
Pre-conditions
トランザクションの事前条件(pre)は、関数(function)の事前条件(pre)と同じです。
事前条件はオプションであり、preブロックで宣言されます。事前条件は、prepareフェーズの後に実行され、トランザクションの残りの部分を実行する前に、明示的な条件が満たされているかどうかを確認するために使用されます。ブロックには、0個以上の条件を含めることができます。
例えば、事前条件では、アカウント間でトークンを転送する前に残高を確認することができます。
pre {
sendingAccount.balance > 0
}
事前条件のいずれかが満たされない場合、その後のトランザクションは実行されず、完全に元に戻されます。
Execute phase
executeブロックは、トランザクションの主要なロジックを実行します。このフェーズはオプションですが、主要なトランザクションロジックをこのセクションに追加するのがベストプラクティスです。
executeフェーズでは、トランザクションの署名アカウントへの参照にアクセスすることはできません。
transaction {
prepare(signer: auth(LoadValue) &Account) {}
execute {
// Invalid: Cannot access the `signer` account reference, as it is not in scope
let resource <- signer.storage.load<@Resource>(from: /storage/resource)
destroy resource
// Valid: Can obtain an unauthorized reference to any account
let otherAccount = getAccount(0x3)
}
}
Post-conditions
トランザクションの事後条件(post)は、関数(function)の事後条件(post)と同じです。
事後条件は任意であり、postブロックで宣言されます。これらは実行フェーズの後に実行され、トランザクションロジックが適切に実行されたことを確認するために使用されます。ブロックには、0個以上の条件を指定できます。
例えば、トークン転送トランザクションでは、最終的な残高が特定の値になることを保証できます。
post {
signer.balance == 30.0: "Balance after transaction is incorrect!"
}
事後条件のいずれかが満たされない場合、トランザクションは失敗し、完全に元に戻されます。
Pre-conditions and post-conditions
preとpostのもう一つの役割は、関連するアカウントに対するトランザクションの効果を記述することです。これらは、ユーザーがトランザクションを発信する前にその内容を確認するために不可欠です。preやpostなどの条件は、トランザクションが実行される前にその内容を簡単に確認できる方法です。
例えば、ユーザーがトランザクションに署名して送信する際に使用するソフトウェアは、トランザクションを分析し、解釈して、人間が読める形式で説明することができます。「このトランザクションは、AからBに30トークンを転送します。Aの残高は30トークン減少し、Bの残高は30トークン増加します」といった具合です。
Summary
トランザクションはフェーズを利用することで、トランザクションのコードや意図をより読みやすくします。 また、開発者がトランザクションロジックを分離する手段も提供します。 トランザクションは、トランザクション実行の前後における状態(ステート, state)を確認する方法も提供し、必要に応じてトランザクションの実行を防止したり、トランザクションによって行われた変更を元に戻したりすることもできます。
以下は、トランザクションのフェーズを実装するために、トランザクション内のprepare, pre, execute および postブロックを使用する方法の簡単なまとめです。
transaction {
prepare(signer1: &Account) {
// Access signing accounts of the transaction.
//
// Avoid logic that does not need access to the signing accounts.
//
// The signing accounts can't be accessed anywhere else in the transaction.
}
pre {
// Define conditions that must be true
// for the transaction to execute.
//
// Define the expected state of things
// as they should be before the transaction is executed.
}
execute {
// The main transaction logic goes here, but you can access
// any public information or resources published by any account.
}
post {
// Define conditions that must be true
// for the transaction to be committed.
//
// Define the expected state of things
// as they should be after the transaction executed.
//
// Also used to provide information about what changes
// the transaction will make to the signing accounts.
}
}
翻訳元->https://cadence-lang.org/docs/language/transactions