Skip to content
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

Extend isAuthorized to process current transaction in HAS System Contract #792

Open
wants to merge 10 commits into
base: main
Choose a base branch
from

Conversation

bguiz
Copy link
Contributor

@bguiz bguiz commented Aug 22, 2023

Description:

This PR adds a new HIP, which augments HIP-632

WIP:

  • Assigned HIP-number
  • Preamble fields
  • Abstract
  • Motivation
  • Rationale
  • User stories
  • Specification
  • Backwards compatibility
  • Security implications
  • How to teach this
  • Reference implementation
  • Rejected ideas
  • Open issues
  • References
  • Copyright

Related issue(s):

Nil

Notes for reviewer:

Hedera Account Service (HAS) System Contract

Checklist

  • Documented (Code comments, README, etc.)
  • Tested (unit, integration, etc.)

…n in HAS System Contract

Signed-off-by: Brendan Graetz <[email protected]>
@netlify
Copy link

netlify bot commented Aug 22, 2023

Deploy Preview for hedera-hips ready!

Name Link
🔨 Latest commit 8324ccf
🔍 Latest deploy log https://app.netlify.com/sites/hedera-hips/deploys/65c340cc3c3d090008b4e5a7
😎 Deploy Preview https://deploy-preview-792--hedera-hips.netlify.app/hip/hip-792
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify site configuration.

@bguiz bguiz force-pushed the feat/is-authorised-current-tx-in-has-precompile branch 2 times, most recently from 1157da5 to e62adc7 Compare August 22, 2023 08:23
@bguiz bguiz force-pushed the feat/is-authorised-current-tx-in-has-precompile branch from e62adc7 to c26b90c Compare August 22, 2023 08:26
@bguiz bguiz force-pushed the feat/is-authorised-current-tx-in-has-precompile branch from bb29bae to 2fae253 Compare August 23, 2023 08:34
Signed-off-by: Brendan Graetz <[email protected]>
@bguiz bguiz force-pushed the feat/is-authorised-current-tx-in-has-precompile branch from 2fae253 to 652fa26 Compare August 23, 2023 08:36
@bguiz bguiz marked this pull request as ready for review August 23, 2023 08:42
Copy link
Contributor

@Nana-EC Nana-EC left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks fro the proposal.
Left some questions and comments


- When an account's admin key is "complex", and is comprised of multiple EdDSA keys and/or ECDSA keys and/or **smart contract IDs**, combined using one or more `KeyList`s or `ThresholdKey`s.

The proposed `hederaAccountService.isAuthorizedCurrentTransaction()` method aims to fulfil the above scenario, and specific use cases pertaining to this scenario will be elaborated upon in the [use cases section](#use-cases) below.
Copy link
Contributor

@Nana-EC Nana-EC Aug 23, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@bguiz is the goal to check if the calling smart contract satisfies the last key slot of the calling EOA's complex key (keyList or threshold)?

That is what is really being asked is "Given a collection of signatures, does making a call from this contract satisfy the the required keys for the transaction origin caller?"
This is most an informative query and doesn't map to a specific transaction but rather the initiating EOA authorization?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not necessarily last key slot. consider these scenario where:

  • the account has a ThresholdKey([edDsaKey, ecDsaKey, scFooId, scBarId], 1)
    • --> transaction is signed using either of the cryptographic keys, does not invoke either of the smart contracts --> ✅
    • --> transaction is signed using either of the cryptographic keys, does not invoke either of the smart contracts --> ✅
    • --> transaction is unsigned using either of the cryptographic keys, but invokes smart contract scFooId --> ✅
    • --> transaction is unsigned using either of the cryptographic keys, but invokes smart contract scBarId --> ✅
  • the account has a ThresholdKey([edDsaKey, ecDsaKey, scFooId, scBarId], 2)
    • --> transaction is unsigned using either of the cryptographic keys, but invokes smart contract scFooId --> ❌
    • --> transaction is unsigned using either of the cryptographic keys, but invokes smart contract scBarId --> ❌
    • --> transaction is signed using either of the cryptographic keys, but invokes smart contract scFooId --> ✅
    • --> transaction is signed using either of the cryptographic keys, but invokes smart contract scBarId --> ✅
  • the account has a ThresholdKey(ThresholdKey([edDsaKey, ecDsaKey], 1), ThresholdKey([scFooId, scBarId], 1), 2)
    • --> transaction is signed using either of the cryptographic keys, and invokes either smart contract --> ✅
    • --> transaction is not signed using either of the cryptographic keys, and invokes either smart contract --> ❌

is the goal to check if the calling smart contract satisfies the last key slot of the calling EOA's complex key (keyList or threshold)?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, the idea is whether the current transaction is authorised according to the Key, KeyList, or ThresholdKey of the account that has submitted a smart contract transaction. (Where the Account is the equivalent of the from field of the EthereumTransaction, or equivalent).

This is most an informative query and doesn't map to a specific transaction but rather the initiating EOA authorization?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think only your last scenario should ever return true. Since SCs don't actually sign and their authorization is based on the matching contract making the call there's no way to authorize if an operation needs more than 1 contractId to match and if all the necessary cryptographic keys haven't signed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, precisely - I was enumerating different examples of when a smart contract ID may not be in the last key slot of an account's complex key, and where there may be more than one contract ID in an account's complex key.

Since SCs don't actually sign and their authorization is based on the matching contract making the call there's no way to authorize if an operation needs more than 1 contractId to match and if all the necessary cryptographic keys haven't signed.


(1) All authorization is performed using cryptographic signatures (EdDSA and/or ECDSA in the case of Hedera).

(2) All authorization is performed "off-chain", that is among clients, and not within (or by) smart contracts.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DO you mean all signing here. The network will still check for authorization

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah exactly. So authorisation checks are performed by the nodes on the network - that of course does not change. But the "thing that generates the authorisation" is what is referred to here. EdDSA keys and ECDSA keys here, and below dDSA key, ECDSA keys, and smart contract functions which invoke the hederaAccountService system contract.

DO you mean all signing here. The network will still check for authorization

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay you should say signing then. Saying authorization is done off-chain might give the wrong impression

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, the reason I use the word authorisation over signing is because the ECDSA and EdDSA keys can sign a transaction, but a smart contract ID does not. So I'm using a term that is inclusive of both "cryptographic signature based authorisation" and "smart contract invocation based authorisation".

Is there perhaps another term (other than "authorisation") which covers both of these?


The authorization methods described in HIP-632 are sufficient to support
the "complex" keys on accounts, as described above, with one exception:
When the "leaf nodes" of a "complex" key include one or more **smart contract IDs**.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you elaborate on where more than 1 contract ID is part of the key and needed for auth.
Indeed the system wouldn't support this at all. The exception might be the direction that HIP 755 is leaning in capturing auth from contracts asynchronously.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In agreement - I don't think that we need to support this scenario.

That being said, there are scenarios in which smart contract IDs could appear in an account's keys more than once which should be supported, see my comment above starting with "not necessarily last key slot. consider these scenario where..."

Can you elaborate on where more than 1 contract ID is part of the key and needed for auth.
Indeed the system wouldn't support this at all.

I say the above without having first read HIP-755, will need to check that out shortly.

The exception might be the direction that HIP 755 is leaning in capturing auth from contracts asynchronously.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay makes sense.
Also when i say last key slot i don't mean in order of the actual key structure, but rather the ContractID has to be the last remaining authorization needed. If not the auth check should always return false.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, gotcha, your previous comment makes more sense now 😅

Also when i say last key slot i don't mean in order of the actual key structure

Yes exactly, this is what I intend to say here too!

but rather the ContractID has to be the last remaining authorization needed.

HIP/hip-792.md Outdated Show resolved Hide resolved
- implement batch processing of multiple transactions

(2)
As a user of Hedera networks,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This user story is already possible no, is there an edge case where this HIP proposes additional capability to create an account with this setup?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently this is only possible - that I've been able to verify at least - with the HTS system contract,
being the only system contract that exposes non-view functions.

Whether or not this is possible depends on the implementation on HIP-632,
which is a pre-requisite for this HIP.

Also, even with HIP-632 implemented, this would still be blocked from happening,
because Solidity/ EVM op codes only expose a subset of transaction information,
which is insufficient to determine whether or not the current transaction is authorised.
This is the critical difference between isAuthorized and isAuthorizedCurrentTransaction.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To clarify i'm saying it's possible for a contract to perform actions on behalf of an account if the contractId satisfies the account key requirement e.g. key is ContractID or 1 of 2 threshold with ContractId as 1 option as you've shown in some examples.
This 2nd User Story makes it seem like it's not possible and this HIP would make it possible.
Might need rewording or removal.


| hash | signature | return | description |
| --- | --- | --- | --- |
| | isAuthorizedCurrentTransaction() | bool | `true` if account is authorized to carry out transaction execution on account. Accepts protobuf key signature blobs. May be used for ECDSA, EdDSA simple key flows, and complex key flows which include any of ECDSA keys, EdDSA keys, and smart contract IDs. |
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should add the selector hash


| hash | signature | return | description |
| --- | --- | --- | --- |
| | isAuthorizedCurrentTransaction() | bool | `true` if account is authorized to carry out transaction execution on account. Accepts protobuf key signature blobs. May be used for ECDSA, EdDSA simple key flows, and complex key flows which include any of ECDSA keys, EdDSA keys, and smart contract IDs. |
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In terms of naming what should CurrentTransaction imply and why does it matter?
There's no reliable way for the system to know what inner transaction is implied without the contract actually intending it.
If my understanding is correct it's not about a specific parent transaction but more so if an EOA's (tx origin) public key authorization requirements will be satisfied by this contract making a call?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes exactly, it is only about whether the authorisation requirements stipulated by the account's key has been satisfied.

If my understanding is correct it's not about a specific parent transaction but more so if an EOA's (tx origin) public key authorization requirements will be satisfied by this contract making a call?

Yes, and this is not needed, only the main transaction is of concern, not that of inner/child transactions.

There's no reliable way for the system to know what inner transaction is implied without the contract actually intending it.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay.
Becoming clearer.
The naming doesn't point to the focus on the EOA (tx origin) signing requirements. It currently suggests there's a transaction or operation to be carried out which you may want to check is possible.
That's how it reads to me.

Without caring about length and describing the intent I'd consider isCallContextTxOriginAuthorized() , now I just have to figure out how to be more concise.
Naming is hard! 🤔

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, open to renaming if it makes it easier to understand/ document!

Without caring about length and describing the intent I'd consider isCallContextTxOriginAuthorized() ,

This function behaves identically to `isAuthorized(address, messageHash, signatureBlob)` as defined in HIP-632, with the following key differences:

- It is called without specifying any parameters
- This function extracts the values that it needs in order to validate if a transaction is authorized from the current transaction
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please be explicit about which values are extracted and from where in the submitted HAPI transaction since you aren't utilizing any function params

- It is called without specifying any parameters
- This function extracts the values that it needs in order to validate if a transaction is authorized from the current transaction
- Therefore it designed to be used exclusively on the current transaction, which is still in-flight
- This is clearly communicated by the `CurrentTransaction` suffix in the function name
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note above that this may not be as clear as intended. The current transaction will be a ContractCall/ContractCreate/EthereumTransaction as that's the submitted HAPI transaction.
How would the system know whether the contract intends to next execute a transfer next.

Actually right now the only thing a contract could do on behalf of an EOA is transfer tokens using a ContractID. Maybe in the future and with an expanded H.A.S system contract there might be more

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes indeed. As noted above, the HTS system contract is currently the only one that exposes a non-view function (that I'm aware of), and hence this is the case.

Actually right now the only thing a contract could do on behalf of an EOA is transfer tokens using a ContractID.

Exactly! (and I'd consider this HIP to the first requested expansion of the HAS system contract)

Maybe in the future and with an expanded H.A.S system contract there might be more

which uses `TxnAwareEvmSigsVerifier` to determine that `tx` is authorised, because:
- `tx` has been signed by `edDsaKey`, which is a member of account `0.0.12345`'s `ThresholdKey`
- `tx` has also been signed by `ecDsaKey`, which is a member of account `0.0.12345`'s `ThresholdKey`
- `tx` invokes `customSc`, which is a member of account `0.0.12345`'s `ThresholdKey`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In a sense it's a readiness check for this contract being the final authorizer correct?.
With the cryptographic key signatures are not provided in the parent transaction and if there are more than 1 contractIDs then this method will always return false.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given this, a contract would well have attempted the transaction and based on a success or error response code it would know.
The difference here would be contract flow and maybe cost in the query cost vs the failed transaction.
Given the current gas strategy the incentive for this seems to be more around cost efficiency

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the cryptographic keys are not provided, and this key requires 3-of-3,
we know (before even getting to the smart contract invocation) that the best case scenario is to get to 1-of-3,
so this would ultimately be destined to unauthorised.
In such a case (and others of a similar vein) the implementation can perform a pre-check, and "fail fast".

With the cryptographic key signatures are not provided in the parent transaction and if there are more than 1 contractIDs then this method will always return false.

I think this would apply for an account with a different key requirement, for example ThresholdKey([edDsaKey, ecDsaKey, scId], 1).
In such a case, if the transaction is not signed by any of the cryptographic keys,
since the requirement is 1-of-3, and it is still possible to get to 1-of-3 by executing the smart contract,
then it would need to proceed with the execution attempt.

Given this, a contract would well have attempted the transaction and based on a success or error response code it would know.

Yes, you're right - I haven't quite considered execution cost (and optimisation) yet.
I think we can use the cost of the verification that the HTS system contract presently has as a starting point.

The difference here would be contract flow and maybe cost in the query cost vs the failed transaction.
Given the current gas strategy the incentive for this seems to be more around cost efficiency

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this would apply for an account with a different key requirement, for example ThresholdKey([edDsaKey, ecDsaKey, scId], 1).
In such a case, if the transaction is not signed by any of the cryptographic keys,
since the requirement is 1-of-3, and it is still possible to get to 1-of-3 by executing the smart contract,
then it would need to proceed with the execution attempt.

Correct what i mean is the check will only even return true if all cryptographic requirements are met whether 0, 1 or n.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Other point is why query when if the only time you're get true is if you were actually authorized.
Only other consideration is price but that might be case by gas based on gasLimit and how expensive the operation is vs the query.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Querying ahead is nice if possible in many scenarios though.

- When an account's admin key is "complex", and is comprised of multiple EdDSA keys and/or ECDSA keys, combined using one or more `KeyList`s or `ThresholdKey`s.

`hederaAccountService.isAuthorized(address, messageHash, signatureBlob)` does **not** work for the following scenarios:

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not exactly clear as to why this scenario would not work with isAuthorized in HIP-632. The signature blob is a protobuf serialized SignatureMap which I believe supports adding contract keys.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The idea here is that the client invoking the smart contract does not need to pass in signatureBlob, and that any values that it needs, including signatureBlob are read from the current transaction.

- implement advanced multisig use cases without the need to split across multiple transactions
- implement atomic multisig use cases
- implement batch processing of multiple transactions

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The batch processing use case is clear to me but the others less so. Are there more concrete examples of why this function is necessary to enable those use cases as opposed to simply failing if the signature is insufficient?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The batch scenario is actually an interesting one where let's say there are 3 accounts,

AccA --> ThresholdKey([ecDsaKeyA, customSc], 1)
AccB --> ThresholdKey([ecDsaKeyB, customSc], 1)
AccC --> ThresholdKey([ecDsaKeyC, customSc], 1)

They could individually sign 3 separate transactions with their cryptographic keys.

Let's say that customSc can perform that same transaction when passed in an array of account addresses.
Since all these accounts require 1-of-2, and customSc's smart contract ID is among their keys, those indiidual transactions can be performed in a single transaction as a batch.

(To be clear, this can also be accomplished with isAuthorized and isAuthorizedRaw from HIP-632 too, with clients taking a different approach)

HIP/hip-792.md Show resolved Hide resolved
Co-authored-by: Nana Essilfie-Conduah <[email protected]>
Signed-off-by: Brendan Graetz <[email protected]>
@david-bakin-sl
Copy link
Member

Just to be clear (to me!): This is not a method that should be exposed via the HAS proxy - where you call an account's address - because you're not trying to see if some arbitrary account authorizes this transaction, but the specific account that sent the transaction?

Or maybe not? Is there a use case for pointing at a specific account and asking: Did this account authorize this transaction taking into account contract id keys? Or is that just isAuthorized where it is suggested, in HIP-632, that the protobuf passed in does include contract id keys that can authorize the message/signature? (Whatever that would mean, actually ...)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants