-
Notifications
You must be signed in to change notification settings - Fork 146
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
created atomic transactions chain hip draft #551
created atomic transactions chain hip draft #551
Conversation
Signed-off-by: Piotr Swierzy <[email protected]>
✅ Deploy Preview for hedera-hips ready!
To edit notification comments on pull requests, go to your Netlify site configuration. |
Signed-off-by: Piotr Swierzy <[email protected]>
changed name to batch transactions Signed-off-by: Piotr Swierzy <[email protected]>
Signed-off-by: Michael Garber <[email protected]>
Signed-off-by: Michael Garber <[email protected]>
Signed-off-by: Michael Garber <[email protected]>
Signed-off-by: Piotr Swierzy <[email protected]>
…in' into hip-0000-atomic-transactions-chain
Hello everyone. In order to add 2 more batch scenarios, that can be very helpfull to some use cases. Scenario 1: Mint -> Transfer Me as the owner of the admin of a token, want to gift Alice some of my tokens. So I want in a Scenario 2: Grant KYC -> Wipe tokens from Alice account -> Revoke KYC Me as the owner of the token regreted my decision to give tokens to Alice and Revoked KYC from Alice account to my token. Further in order to not have tokens flying away, I want to remove tokens from Alice account where doesn't KYC'ed anymore. Since it is not possible to wipe an non-KYC'ed account tried this before and transaction shows an error. Tks @mgarbs for the talk we had on discord channel |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Interesting idea.
Left some comments in the form of questions
HIP/hip-551.md
Outdated
|
||
## User stories | ||
|
||
As a Hedera token service user, I want to be able to unfreeze an account, send an NFT, and freeze it again in one ACID transaction, that way I can achieve an account-bound NFT(nontransferable NFT) collection, without using the hedera smart contract service. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: should number the list
As a Hedera token service user, I want to be able to unfreeze an account, send an NFT, and freeze it again in one ACID transaction, that way I can achieve an account-bound NFT(nontransferable NFT) collection, without using the hedera smart contract service. | |
1. As a Hedera token service user, I want to be able to unfreeze an account, send an NFT, and freeze it again in one ACID transaction, that way I can achieve an account-bound NFT(nontransferable NFT) collection, without using the hedera smart contract service. | |
## Specification | ||
We could create a new protobuf message based on `SignedTransaction` message: | ||
``` | ||
message BatchTransaction { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Q: Is it fair to say the assumption or requirement is that the submitter is authorized to carry out all the batch transactions?
For example to carry out some of the example one be both the treasurer, in another case the freezeKey owner.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The submitter does not need to be authorized to carry out all the batch transaction, every transaction in the batch needs to be signed by all parties involved in the individual transaction.
It would work like every transaction in Hedera, where the submitter can be any account as long as the transaction is properly signed
/** | ||
* TransactionBodies serialized into bytes, which must be signed | ||
*/ | ||
repeated bytes bodyBytes = 1; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Q: do each of these transactions result in externalized individual transaction records in the record stream?
If individual records are exposed is there a concept of parent and child transactions? If so you should supply an example of this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't understand you question completely, but in my opinion, the batch transaction consists of individual transactions, but because there are batched, everyone needs to be successful, or they are reverted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This won't really work with the current HAPI protobuf model. bodyBytes
includes a transaction ID. You need to sign a single logical "parent" transaction ID in this case. The list of "transactions" should not have individual transaction ids associated with them because they should be assigned by the ledger upon execution (with appropriate nonces attached to them).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead, it might be possible to modify the TransactionBody
protobuf message with a new field inside the data
field that would be a repeated field of TransactionBodyXXXX
(now this is where it gets murky, should it be a repeated field of encoded transaction body XXX, or should it be an explicitly defined new message that is a union of all the possible TransactionBodyXXXX types) Either way, with an ordered list of TransactionBodyXXXX you can achieve the desired outcome and only have to generate the enclosing Transaction ID for the outer TransactionBody envelope.
``` | ||
## Backwards Compatibility | ||
|
||
## Security Implications |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Q: Are there cases where multiple operators are involved or will the batch transactions always see a single entity carrying out some steps?
If there's a case where the batch contains entity A doing one thing and entity B doing something else there would be potential security considerations to address
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, the transaction can involve multiple parties, but every individual transaction in the batch needs to fulfill the standard transaction signing requirements, so I don't see any added security implications.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Nana-EC are you referring to the client operator here?
## Reference Implementation | ||
|
||
## Rejected Ideas | ||
We rejected the idea to add the support of conditional branching to the batch transactions, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you expand on this. Did you mean you rejected the case where essentially some transactions are carried out but not all?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, basically we won't support conditional branching, understood as if transaction B is not successful then execute transaction C.
HIP/hip-551.md
Outdated
if the community asks for it.<br> | ||
Initial set: | ||
``` | ||
Unfreeze -> Transfer -> Freeze |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are there any non HTS cases ? Is this for HTS only or all HAPI transactions?
If there's a set on non applicable transactions those should be called out
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be more practical if these were for all HAPI transactions. Any application that needs a hapi transaction to succeed in order to do something else would use this. Gated topics or consensus messages have huge potential for content creation use cases. Here are a couple of scenarios:
Transfer -> Topic Create -> Submit Topic Message
Transfer -> Submit Topic Message
Submit Topic Message -> Transfer
You wouldn't want these transactions to get incredibly long. Just a few transactions that, with the signatures included is less than the 6kb limit
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The ability to combine HTS with HCS would be super powerful if they are batched. So, would also agree for all HAPI transactions to reach the maximum value out of this HIP proposal.
/** | ||
* TransactionBodies serialized into bytes, which must be signed | ||
*/ | ||
repeated bytes bodyBytes = 1; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Q: Also how should the network manage when one of these transaction has a child transaction?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the parent transaction is in the batch and the batch isn't successful, then the child transaction should also be reverted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
that's how i see it. All or nothing.
/** | ||
* TransactionBodies serialized into bytes, which must be signed | ||
*/ | ||
repeated bytes bodyBytes = 1; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Q: Are there any applicable byte size limits for consideration by the SDK and the network itself?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think, the standard transaction size limit should be respected
HIP/hip-551.md
Outdated
|
||
The existing implementation of transactions in the Hedera network does not allow multiple different HAPI transactions to be called in one single network transaction that would have all the ACID properties. | ||
This makes it impossible to create more complicated flows without using smart contracts (which do not support all the HAPI transactions at this point) and listening to the mirror node to check the status of the previous transaction. | ||
This way we can also achieve an abstraction away from smart contracts |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are a few more very powerful transaction chains that I would love to see incorporated into the motivation and user stories for this HIP.
This proposal enhances contract-less development on Hedera as well as smart contract orchestration and security on Hedera.
All of the below transaction chains will improve user experience, account security, contract security, contract compatibility, and contract transparency.
- Associate -> Contract Call
- The contract call is going to send a token to the user. The user must associate the token first, or the contract will revert.
- Allowance Approval -> Contract Call
- The contract call is going to take tokens from a user. The user must grant a token allowance first, or the contract will revert.
- Allowance Approval -> Allowance Approval -> Associate -> Contract Call
- The contract call is going to take 2 different tokens from a user and send the user an unassociated token that has already been created on the network. The user must grant token allowances first and associate the receivable token, or the contract will revert.
- Allowance Approval -> Allowance Approval -> Account Update -> Contract Call
- The contract call is going to take 2 different tokens from a user and send the user an unassociated token that will be created in the contract call. The user must grant token allowances first and have an available auto association slot, or the contract will revert.
- Contract Call -> Contract Call
- First contract call is an external contract. Second contract call is a contract validating the results of the first contract. The second contract will revert if the first contract does not produce the intended results. This could be a security enhancement and improve contract call extensibility.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This also means that if any of the contract calls at the end are reverted, then the approvals, associates, and account updates would be undone. This would improve the security of the network by reducing dangling allowances and auto association slots.
…ns to the initial set, and added a security implication. Signed-off-by: Piotr Swierzy <[email protected]>
Looking to finalize this HIPI have a few questions as I think through the implementation. As I see it, there are two implementation paths that diverge as it relates to signing transactions. @se7enarianelabs @NilsonBertola @bugbytesinc @Ashe-Oro @Nana-EC @pwoosauce Looking for your feedback here on the implementation paths described below so that we may finalize this HIP. Implementation Path 1: We create a new Protobuf message based on
|
Path 1 creates a Replay Attack vulnerability. If one TX fails, an actor could crack open the batched TX and replay individual signed transactions without the consent of the original signer. In short, you can't sign individual transactions within a batch for safety reasons. |
What will be the security posture regarding the movement of tokens? Will it be only the Payer of the wrapped set of |
Great point @bugbytesinc implementation path 1 would likely create a replay attack, but it does offer superior composability of transactions. Because the And also, if the bad actor is the a consensus node receiving the What I like about implementation path 1 is that it enables composability of transactions. Not every transaction involved in the Requiring all parties to sign a I would like to see implementation path 1, but the replay attack is concerning. To prevent this replay attack, each child transaction's signature must be different from an equivalent individual transaction not in a BatchSignedTransaction. Perhaps each child transaction's signature should be signed again by the BatchSignedTransaction's TransactionId's account and the doubly-signed signature should replace the original transaction's signature. Basically, the if a transaction is a child transaction of a BatchSignedTransaction, then the original child transaction's signature must not be stored on the network. The signature must be modified to include the BatchSignedTransaction's TransactionId in some way. |
A couple of issues, first one that comes to mind is that is starting to add to many separate signatures to the payload, it will become impractical to add more than a hand full of transactions due to message size limitations. Also, signing then bundling multiple transactions is difficult to express in an SDK or more importantly with a wallet. Why 3 signatures when one would suffice? Additionally, you can't safely mitigate the replay attack of path 1. For example, pre-check failures do not result in the propagation of the transaction to the network. A precheck errored batched TX could then be broken apart and re-packaged - same problem as before. |
If that is the case, what we have today works. It is extremely important to be made aware of this condition for the types of constructs this is trying to support. I only am willing to let A happen if I know that B happened than C happens, otherwise I'm not signing this. |
For example, I'm not going to give an "allowance" to saucer swap unless I know that immediately afterwards that liquidity is sent to the contract. If that fails, I don't want the allowance to ever have happened, and I don't want that authorization to be replayable if the contract fails. |
Sorry, I meant not every signer needs to know that the transaction they signed is in a |
I agree, the replay must be prevented. I also do not want to give an allowance unless I know the allowance will be exercised or reverted in the same transaction. |
A batched transaction's child transaction signatures must be invalid for individual transactions. This would prevent a replay attack. The child transaction signatures should only be valid when they are children of the parent |
@bugbytesinc I believe we would add all the transactions inside the batch to the "duplicate list" whether they were successfully processed or not. That way, any transactions that come in while those would still be valid (max is probably around 3 minutes) would be considered duplicates and would not be processed. |
@nickpoorman it is my understanding that adding all transactions in the batch to the "duplicate list" will not be sufficient to prevent the replay attack. If the bad actor is a consensus node (community node), then the child transactions may be extracted and executed without the other transactions in the batch. I believe child transactions must have their signatures modified to only be valid as child transactions under the specific batch. This way the child transaction cannot be extracted after being submitted to a consensus node. |
But that's not how it works for prechecks, if they fail the TX "never happened" it will never reach this list to be considered a de-dup. Failed precheck TXs are not gossiped. and then, if you made an exception for this use case, you open yourself up to a very cheap way to DOS the network. |
@rbair23 thank you for the well-thought response
I agree, it must not be possible to execute an inner transaction outside the context of the batch in which it was submitted to a consensus node. I believe there is some nuance here. You should have the ability build a transaction separately from the batch transaction, then put it in a batch transaction before submitting it to a consensus node.
I agree that the inner transactions of a batch must not be able to be rearranged. But I believe they should be able to be rearranged up until the point that the batch transaction is frozen (or signed)
I agree with this statement 100% 100% we need to ensure the immutable integrity of batch transactions before they are sent to a consensus nodes. But just before submitting the batch transaction to a consensus node, it is ideal to have the flexibility to compose batch transactions from different sources. For example:
Please let me know if you have any question or concerns with what I said above. 😊 |
What if Alice is a malicious user that wants to kill the marketplace? In this case, Alice could take a If the issue was only with fees, I suppose, if you wanted to include a transaction in a batch that was not aware it was in the batch, then you could, as the one organizing the batch, designate yourself as the payer for all the fees of the transactions in the batch. However, transactions in a batch need to know they are in a batch for other reasons. As @rbair23 pointed out:
|
@nickpoorman the It is bad practice to allow a user to execute transactions that the dapp pays for. For the other two concerns you mentioned, I proposed a potential solution earlier that satisfies those concerns:
|
I don't understand this. Can you explain what you mean? Do you mean "sign the signature" or "sign the transaction"? I tried to work through this yesterday and I believe this would leave you wide open to attack. But maybe I am not understanding the idea right. |
If I have transaction T1, signed by user U1, and batch signed by user U2, then T1 must have a reference to the batch, or it is possible for an intermediary to disassemble and reassemble a batch.
I agree, once the inner transaction has been signed, its position within the batch is fixed and the batch it is part of is fixed.
In these cases, as long as the user signing these transactions signs them in such a way that they fix them within the batch in their order, then I think we're good. If the expectation is that somebody else beside the user is signing it, then the user cannot subsequently add it to a batch. Otherwise, I think we're opening ourselves up to some attacks. Thanks! |
@rbair23 Yes, I will break this down a bit. But I'll make a change because I see a flaw with my previous method. Let's define some common language:
Before creating a batch transaction, a new public/private keypair should be created. When creating a BatchTransaction, the batch transaction should be initialized with a public key. The batch transaction should be populated with valid, signed inner transactions. Before submitting the batch transaction to the network, the batch transaction should be finalized with the private key we created earlier. The This would look something like: const client = Client.forPreviewNet();
client.setOperator('', '');
const tx1 = await getArbitrarySignedTransaction1();
const tx2 = await getArbitrarySignedTransaction2();
const privateKey = PrivateKey.generateED25519();
const batchTransaction = await new BatchTransaction()
.setPublicKey(privateKey.publicKey)
.addTransaction(tx1)
.addTransaction(tx2)
.finalizeBatch(privateKey) // this will replace all inner transaction's `TransactionSignatures` with `BatchedTransactionSignatures`;
const txResponse = await batchTransaction.execute(client); |
Goodness, this is getting complicated, I fear the level of effort required by wallet makers to successfully sort this all out. If you have something that "does not need to know its in a batch" - then it should just be submitted to the network on its own without being put inside a batch. If you have a series of all-or-nothing commands, then, it points to the easiest and most attainable option for wallet makers and system designers to require everyone to sign the outer envelope of all of the batched unsigned commands (XXXBodyBytes). If that solves 80% of the problem, and is decent forward progress on the matter, the other use cases can be sorted out after we get traction on some sort of atomic batching (particularly now with the allowance TX requirement for contracts) ... and if a wallet can't parse this new new Batched Transaction BodyBytes protobuf format, it has no business signing such a TX, because doing so could harm the key owner. |
@bugbytesinc - I believe we must have the inner transactions signed individually. A signature at the top level should not be an indication that that key authorizes everything within the batch, this could open us up to attacks. Generally, we want the finest-grained transaction signed to provide the most security and flexibility. |
I was talking with some others offline about how to do this. @netopyr convinced me some of my concerns were not valid. Let me see if I can explain. Let's say:
In other words, inner transactions are normal transactions. The batch is signed by all participants. I had claimed:
The first two requirements were meant to prohibit a man-in-the-middle, or a dishonest node, from being able to break open and rearrange the batch, or break up a batch and create a new one, with a mind to preventing front-running. But @netopyr made a strong case that this is not something we should solve for, because a man-in-the-middle or a dishonest node can already front run you with normal transactions just by inspecting the transaction and holding it up and submitting their own transaction first. If you want to prevent front-running, you should use TLS to connect to the node and you should choose a node you trust. That last requirement is really important. Signatures are used to determine authorization, so we want to use the signatures per-inner-transaction for authorization for that transaction. Now, by requiring the signers of each inner transaction to also sign the batch, we make it so that the person who assembles the batch cannot fool the signers of the inner transactions. For example, as a batch creator I may say that you send me 100 HBAR and I will send you this fancy NFT worth $1M. You sign it. Then I (the batch creator), remove the second transaction and just take your 100 HBAR. It feels very "scammy". Instead, if the user has to sign the batch as well, then we are locking in place the batch and the agreement by all participants that the batch they're going to execute cannot be modified after they sign. What do you think? |
@rbair23 Requiring signers of each inner transaction to also sign the batch implies that the context of the transaction is crucial for execution, rather than the signature itself. When someone signs a transaction, they individually agree to the outcome. Batch transactions are not designed for retail users but rather for developers, dApps, etc. |
Co-authored-by: Nick Poorman <[email protected]> Signed-off-by: Michael Garber <[email protected]>
Co-authored-by: Nick Poorman <[email protected]> Signed-off-by: Michael Garber <[email protected]>
Co-authored-by: Nick Poorman <[email protected]> Signed-off-by: Michael Garber <[email protected]>
Co-authored-by: Nick Poorman <[email protected]> Signed-off-by: Michael Garber <[email protected]>
Co-authored-by: Nick Poorman <[email protected]> Signed-off-by: Michael Garber <[email protected]>
Co-authored-by: Nick Poorman <[email protected]> Signed-off-by: Michael Garber <[email protected]>
Co-authored-by: Nick Poorman <[email protected]> Signed-off-by: Michael Garber <[email protected]>
Co-authored-by: Nick Poorman <[email protected]> Signed-off-by: Michael Garber <[email protected]>
Co-authored-by: Nick Poorman <[email protected]> Signed-off-by: Michael Garber <[email protected]>
Co-authored-by: Nick Poorman <[email protected]> Signed-off-by: Michael Garber <[email protected]>
Co-authored-by: Nick Poorman <[email protected]> Signed-off-by: Michael Garber <[email protected]>
Co-authored-by: Nick Poorman <[email protected]> Signed-off-by: Michael Garber <[email protected]>
Signed-off-by: Michael Garber <[email protected]> Signed-off-by: Piotr Swierzy <[email protected]> Co-authored-by: Michael Garber <[email protected]> Co-authored-by: Nick Poorman <[email protected]> Signed-off-by: Kim Rader <[email protected]>
Signed-off-by: Piotr Swierzy [email protected]
Description: