From 78108e52e9adbb97c98b3d5e74706a82d65d60fd Mon Sep 17 00:00:00 2001 From: Jeffrey Doyle Date: Mon, 24 Oct 2022 17:34:25 -0700 Subject: [PATCH 1/5] FLIP - Extended Transaction Format --- flips/20221024-extended-transaction-format.md | 111 ++++++++++++++++++ 1 file changed, 111 insertions(+) create mode 100644 flips/20221024-extended-transaction-format.md diff --git a/flips/20221024-extended-transaction-format.md b/flips/20221024-extended-transaction-format.md new file mode 100644 index 00000000..b34631ed --- /dev/null +++ b/flips/20221024-extended-transaction-format.md @@ -0,0 +1,111 @@ +# Extended Transaction Format [DRAFT] + +| Status | Draft | +| :------------ | :------------------------------------------------------------- | +| **FLIP #** | TBD | +| **Forum** | TBD | +| **Author(s)** | Jeffrey Doyle (jeffrey.doyle@dapperlabs.com) | +| **Sponsor** | Jeffrey Doyle (jeffrey.doyle@dapperlabs.com) | +| **Updated** | 2022-10-24 + +## Abstract + +This FLIP proposes a change to Cadence, Flow Client Libraries and Wallets on Flow to increase the flexibility of how applications and wallets coordinate on determining the assets and accounts used in a transaction. + +## Motivation + +Often, transactions today are written with an understanding of where assets are in the accounts used in the transaction. Transactions are also written with a notion of what accounts a user controls, and how their wallet performs actions with that users assets. + +As Flow progresses to allow assets to be stored wherever the account owner and wallet may choose, and for the wallet to use their preferred account setup to coordinate actions with the users assets, more flexibility in how transaction developers write their transactions is required. + +## Design Proposal + +This FLIP proposes that transactions should have any number of `prepare` statments and any number of `post` statments. Each prepare statment would be responsible for assigning values to variables defined in the transaction annotated with the same `role` as is assigned to the prepare block. + +Each prepare statment would have the ability to initialize transaction varaibles, but not read their values. Each prepare statment can only assign variables annotated with the same role as itself. Since prepare statments can mutate execution state, the order of execution of each prepare statment must match the order they are defined in the cadence transaction. + +A role would be assigned to a variable or prepare phase by an annotation above them (eg: `#role: Example`). Each signer / wallet involved in the transaction is assigned one of the roles defined in the transaction. Each transaction could have any number of roles for it's variables and prepare statements, but each variable and prepare statment can only be assigned to one role. Optionally, each signer / wallet can append a post statment to the transaction with whatever conditions they require. + +Each signer / wallet is responsible for producing the content of their assigned prepare statement using a non-empty set of accounts they control if the prepare statement is not already defined in the transaction. This way, the wallet can choose which accounts they need to use, and how they need to accomplish assigning values for the variables they're responsible for. + +For example, a Cadence transaction might look like: + +```cadence +// NFT Purchase & Transfer +transaction(nftID: UInt64) { + + #role: Seller + let nft: @NonFungibleToken.NFT + + #role: Buyer + let payment: @FlowVault + #role: Buyer + let receiver: Capability<&{NFT.Receiver}> + + #role: Seller + prepare() // <— Seller wallet fills this in and assigns variables marked #Seller + + #role: Buyer + prepare() // <— Buyer wallet fills this in and assigns variables marked #Buyer + + pre { + payment.balance == 20.0 + } + + execute { + ... + } + + post { + ... // <-- Buyer produces a post statment to check that Buyer received the NFT + } + + post { + ... // <-- Seller produces a post statment to check that Seller received 20.0 FLOW + } +} +``` + +Here, the transaction developer is declaring that there are two roles in the transaction, "Buyer" and "Seller". The transaction is effectively saying: + +> "This transaction requires the Buyer to produce a vault of 20.0 FLOW tokens and a reciever capability to recieve the NFT, and the Seller to put up the NFT corresponding to nftID". + +Each wallet / signer for the Buyer and Seller would be responsible for producing the prepare statment assigned to them. This way, the transaction developer is able to declare what happens in the transaction without having to know where in the users accounts their assets are, or which Flow accounts are needed to be used to accomplish the transaction. + +Each wallet / signer involved in the transaction benefits from having their own isolated prepare statement where they can engague with the accounts they control. This way, wallets do not need to concern themselves with the content of other prepare statments in the transaction where other AuthAccounts are made available, as the AuthAccounts the wallet controls are not impacted by them. + +In the case where the wallet produces the content of their assigned prepare statment, they benefit by not needing to be concerned about a malicious prepare statement written by a 3rd party having access to the AuthAccounts they control. + +Flow's Client Libraries would need to be modified to support asking wallets / signers to produce the prepare statments they would like to use for a given transaction. The client libraries would also need to be modified to support declaring the role of each authorizer of the transaction, so the corresponding wallet / signer can understand which role they are acting as. + +The Flow transaction data structure and Access Node API must be updated to coordinate which signature(s) for each authorizer correspond to which AuthAccount in each prepare statment of the transaction. + +Here is an example of how FCL-JS might be used to execute such a transaction: + +```javascript +const txId = await fcl.mutate({ + cadence: `...`, + authorizers: [ + appAuthzWallet({ role: “Seller”}), // Authorizer (wallet) for first prepare block (@Seller) + fcl.currentUser.authz({ role: “Buyer”}) // Authorizer (wallet) for second prepare block (#Buyer) + ] +}) +``` + +## Considerations / Dependencies + +This proposal involves complex changes to multiple areas of Flow, including: +- The Cadence language +- How Flow executes Cadence transactions +- The Flow Transaction data structure +- Flow Access Node API +- Flow Client Library (FCL) & FCL wallet provider spec. +- Flow Wallets + +The benefits of this proposal should be appropriately weighed against the effort required to implement and cordinate this proposals changes among the affected areas and parties. + +For wallets, considerable complexity needs to be addressed in how the wallet can interpret a cadence transaction, and understand how to produce the cadence code required to properly assign the varaiables they are responsible for in their assiend prepare statement. + +## Questions and Discussion Topics + +- Do viable alternatives to this proposal exist that allow greater flexibility in how applications and wallets coordinate on determining the assets and accounts used in a transaction? From b5f5a06dc7896f10e40eb48c6821df2cdeaa59b4 Mon Sep 17 00:00:00 2001 From: Jeffrey Doyle Date: Mon, 24 Oct 2022 17:45:00 -0700 Subject: [PATCH 2/5] FLIP - Extended Transaction Format --- flips/20221024-extended-transaction-format.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flips/20221024-extended-transaction-format.md b/flips/20221024-extended-transaction-format.md index b34631ed..1124b368 100644 --- a/flips/20221024-extended-transaction-format.md +++ b/flips/20221024-extended-transaction-format.md @@ -86,7 +86,7 @@ Here is an example of how FCL-JS might be used to execute such a transaction: const txId = await fcl.mutate({ cadence: `...`, authorizers: [ - appAuthzWallet({ role: “Seller”}), // Authorizer (wallet) for first prepare block (@Seller) + appAuthzWallet({ role: “Seller”}), // Authorizer (wallet) for first prepare block (#Seller) fcl.currentUser.authz({ role: “Buyer”}) // Authorizer (wallet) for second prepare block (#Buyer) ] }) From aa7f6265fda67ed126add6d9538ba9e90d6cc6b8 Mon Sep 17 00:00:00 2001 From: Jeffrey Doyle Date: Mon, 24 Oct 2022 17:51:42 -0700 Subject: [PATCH 3/5] FLIP - Extended Transaction Format --- flips/20221024-extended-transaction-format.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/flips/20221024-extended-transaction-format.md b/flips/20221024-extended-transaction-format.md index 1124b368..51173fe0 100644 --- a/flips/20221024-extended-transaction-format.md +++ b/flips/20221024-extended-transaction-format.md @@ -86,8 +86,8 @@ Here is an example of how FCL-JS might be used to execute such a transaction: const txId = await fcl.mutate({ cadence: `...`, authorizers: [ - appAuthzWallet({ role: “Seller”}), // Authorizer (wallet) for first prepare block (#Seller) - fcl.currentUser.authz({ role: “Buyer”}) // Authorizer (wallet) for second prepare block (#Buyer) + appAuthzWallet({ role: “Seller” }), // Authorizer (wallet) for first prepare block (#Seller) + fcl.currentUser.authz({ role: “Buyer” }) // Authorizer (wallet) for second prepare block (#Buyer) ] }) ``` @@ -104,7 +104,7 @@ This proposal involves complex changes to multiple areas of Flow, including: The benefits of this proposal should be appropriately weighed against the effort required to implement and cordinate this proposals changes among the affected areas and parties. -For wallets, considerable complexity needs to be addressed in how the wallet can interpret a cadence transaction, and understand how to produce the cadence code required to properly assign the varaiables they are responsible for in their assiend prepare statement. +For wallets, considerable complexity needs to be addressed in how the wallet can interpret a cadence transaction, and understand how to produce the cadence code required to properly assign the varaiables they are responsible for in their assigned prepare statement. ## Questions and Discussion Topics From 6a571039f43cb50ae73d8ad7be7cf8f815115d4e Mon Sep 17 00:00:00 2001 From: Jeffrey Doyle Date: Fri, 25 Nov 2022 14:25:49 -0800 Subject: [PATCH 4/5] FLIP - Extended Transaction Format Spelling Fixes --- flips/20221024-extended-transaction-format.md | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/flips/20221024-extended-transaction-format.md b/flips/20221024-extended-transaction-format.md index 51173fe0..a564b6fc 100644 --- a/flips/20221024-extended-transaction-format.md +++ b/flips/20221024-extended-transaction-format.md @@ -20,11 +20,11 @@ As Flow progresses to allow assets to be stored wherever the account owner and w ## Design Proposal -This FLIP proposes that transactions should have any number of `prepare` statments and any number of `post` statments. Each prepare statment would be responsible for assigning values to variables defined in the transaction annotated with the same `role` as is assigned to the prepare block. +This FLIP proposes that transactions should have any number of `prepare` statements and any number of `post` statements. Each prepare statement would be responsible for assigning values to variables defined in the transaction annotated with the same `role` as is assigned to the prepare block. -Each prepare statment would have the ability to initialize transaction varaibles, but not read their values. Each prepare statment can only assign variables annotated with the same role as itself. Since prepare statments can mutate execution state, the order of execution of each prepare statment must match the order they are defined in the cadence transaction. +Each prepare statement would have the ability to initialize transaction variables, but not read their values. Each prepare statement can only assign variables annotated with the same role as itself. Since prepare statements can mutate execution state, the order of execution of each prepare statement must match the order they are defined in the cadence transaction. -A role would be assigned to a variable or prepare phase by an annotation above them (eg: `#role: Example`). Each signer / wallet involved in the transaction is assigned one of the roles defined in the transaction. Each transaction could have any number of roles for it's variables and prepare statements, but each variable and prepare statment can only be assigned to one role. Optionally, each signer / wallet can append a post statment to the transaction with whatever conditions they require. +A role would be assigned to a variable or prepare phase by an annotation above them (eg: `#role: Example`). Each signer / wallet involved in the transaction is assigned one of the roles defined in the transaction. Each transaction could have any number of roles for it's variables and prepare statements, but each variable and prepare statement can only be assigned to one role. Optionally, each signer / wallet can append a post statement to the transaction with whatever conditions they require. Each signer / wallet is responsible for producing the content of their assigned prepare statement using a non-empty set of accounts they control if the prepare statement is not already defined in the transaction. This way, the wallet can choose which accounts they need to use, and how they need to accomplish assigning values for the variables they're responsible for. @@ -68,17 +68,17 @@ transaction(nftID: UInt64) { Here, the transaction developer is declaring that there are two roles in the transaction, "Buyer" and "Seller". The transaction is effectively saying: -> "This transaction requires the Buyer to produce a vault of 20.0 FLOW tokens and a reciever capability to recieve the NFT, and the Seller to put up the NFT corresponding to nftID". +> "This transaction requires the Buyer to produce a vault of 20.0 FLOW tokens and a receiver capability to receive the NFT, and the Seller to put up the NFT corresponding to nftID". -Each wallet / signer for the Buyer and Seller would be responsible for producing the prepare statment assigned to them. This way, the transaction developer is able to declare what happens in the transaction without having to know where in the users accounts their assets are, or which Flow accounts are needed to be used to accomplish the transaction. +Each wallet / signer for the Buyer and Seller would be responsible for producing the prepare statement assigned to them. This way, the transaction developer is able to declare what happens in the transaction without having to know where in the users accounts their assets are, or which Flow accounts are needed to be used to accomplish the transaction. -Each wallet / signer involved in the transaction benefits from having their own isolated prepare statement where they can engague with the accounts they control. This way, wallets do not need to concern themselves with the content of other prepare statments in the transaction where other AuthAccounts are made available, as the AuthAccounts the wallet controls are not impacted by them. +Each wallet / signer involved in the transaction benefits from having their own isolated prepare statement where they can engage with the accounts they control. This way, wallets do not need to concern themselves with the content of other prepare statements in the transaction where other AuthAccounts are made available, as the AuthAccounts the wallet controls are not impacted by them. -In the case where the wallet produces the content of their assigned prepare statment, they benefit by not needing to be concerned about a malicious prepare statement written by a 3rd party having access to the AuthAccounts they control. +In the case where the wallet produces the content of their assigned prepare statement, they benefit by not needing to be concerned about a malicious prepare statement written by a 3rd party having access to the AuthAccounts they control. -Flow's Client Libraries would need to be modified to support asking wallets / signers to produce the prepare statments they would like to use for a given transaction. The client libraries would also need to be modified to support declaring the role of each authorizer of the transaction, so the corresponding wallet / signer can understand which role they are acting as. +Flow's Client Libraries would need to be modified to support asking wallets / signers to produce the prepare statements they would like to use for a given transaction. The client libraries would also need to be modified to support declaring the role of each authorizer of the transaction, so the corresponding wallet / signer can understand which role they are acting as. -The Flow transaction data structure and Access Node API must be updated to coordinate which signature(s) for each authorizer correspond to which AuthAccount in each prepare statment of the transaction. +The Flow transaction data structure and Access Node API must be updated to coordinate which signature(s) for each authorizer correspond to which AuthAccount in each prepare statement of the transaction. Here is an example of how FCL-JS might be used to execute such a transaction: @@ -102,9 +102,9 @@ This proposal involves complex changes to multiple areas of Flow, including: - Flow Client Library (FCL) & FCL wallet provider spec. - Flow Wallets -The benefits of this proposal should be appropriately weighed against the effort required to implement and cordinate this proposals changes among the affected areas and parties. +The benefits of this proposal should be appropriately weighed against the effort required to implement and coordinate this proposals changes among the affected areas and parties. -For wallets, considerable complexity needs to be addressed in how the wallet can interpret a cadence transaction, and understand how to produce the cadence code required to properly assign the varaiables they are responsible for in their assigned prepare statement. +For wallets, considerable complexity needs to be addressed in how the wallet can interpret a cadence transaction, and understand how to produce the cadence code required to properly assign the variables they are responsible for in their assigned prepare statement. ## Questions and Discussion Topics From c2ff945a5628c163592de35c029ed0e67f2300bc Mon Sep 17 00:00:00 2001 From: Jeffrey Doyle Date: Tue, 24 Jan 2023 16:36:02 -0800 Subject: [PATCH 5/5] FLIP - Extended Transaction Format Migrate to Role Block Format --- flips/20221024-extended-transaction-format.md | 37 +++++++++---------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/flips/20221024-extended-transaction-format.md b/flips/20221024-extended-transaction-format.md index a564b6fc..b4934344 100644 --- a/flips/20221024-extended-transaction-format.md +++ b/flips/20221024-extended-transaction-format.md @@ -20,36 +20,35 @@ As Flow progresses to allow assets to be stored wherever the account owner and w ## Design Proposal -This FLIP proposes that transactions should have any number of `prepare` statements and any number of `post` statements. Each prepare statement would be responsible for assigning values to variables defined in the transaction annotated with the same `role` as is assigned to the prepare block. +This FLIP proposes that transactions should have any number of `prepare` statements and any number of `post` statements. Each prepare statement would be responsible for assigning values to variables defined in the transaction within the same `role` block. -Each prepare statement would have the ability to initialize transaction variables, but not read their values. Each prepare statement can only assign variables annotated with the same role as itself. Since prepare statements can mutate execution state, the order of execution of each prepare statement must match the order they are defined in the cadence transaction. +Each prepare statement would have the ability to initialize transaction variables, but not read their values. Each prepare statement can only assign variables annotated within the same role block as itself. Since prepare statements can mutate execution state, the order of execution of each prepare statement must match the order they are defined in the cadence transaction. -A role would be assigned to a variable or prepare phase by an annotation above them (eg: `#role: Example`). Each signer / wallet involved in the transaction is assigned one of the roles defined in the transaction. Each transaction could have any number of roles for it's variables and prepare statements, but each variable and prepare statement can only be assigned to one role. Optionally, each signer / wallet can append a post statement to the transaction with whatever conditions they require. +A role would be assigned to a variable or prepare phase by being included in the same role block. Each signer / wallet involved in the transaction is assigned one of the roles defined in the transaction. Each transaction could have any number of roles for it's variables and prepare statements, but each variable and prepare statement can only be assigned to one role. Optionally, each signer / wallet can append a post statement to the transaction with whatever conditions they require. -Each signer / wallet is responsible for producing the content of their assigned prepare statement using a non-empty set of accounts they control if the prepare statement is not already defined in the transaction. This way, the wallet can choose which accounts they need to use, and how they need to accomplish assigning values for the variables they're responsible for. +Each signer / wallet is responsible for producing a prepare statement for their assigned role block using a non-empty set of accounts they control if the prepare statement is not already defined in the transaction. This way, the wallet can choose which accounts they need to use, and how they need to accomplish assigning values for the variables they're responsible for. For example, a Cadence transaction might look like: ```cadence // NFT Purchase & Transfer -transaction(nftID: UInt64) { +transaction(nftID: UInt64, amount: UFix64) { - #role: Seller - let nft: @NonFungibleToken.NFT + role seller { + let nft: @NonFungibleToken.NFT - #role: Buyer - let payment: @FlowVault - #role: Buyer - let receiver: Capability<&{NFT.Receiver}> + // <-- Seller wallet produces a prepare statement that will be inserted here + } + + role buyer { + let payment: @FlowToken.Vault + let receiver: Capability<&{NFT.Receiver}> - #role: Seller - prepare() // <— Seller wallet fills this in and assigns variables marked #Seller - - #role: Buyer - prepare() // <— Buyer wallet fills this in and assigns variables marked #Buyer + // <-- Buyer wallet produces a prepare statement that will be inserted here + } pre { - payment.balance == 20.0 + payment.balance == amount } execute { @@ -57,11 +56,11 @@ transaction(nftID: UInt64) { } post { - ... // <-- Buyer produces a post statment to check that Buyer received the NFT + ... // <-- Buyer produces a post statement to check that Buyer received the NFT } post { - ... // <-- Seller produces a post statment to check that Seller received 20.0 FLOW + ... // <-- Seller produces a post statement to check that Seller received 20.0 FLOW } } ```