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

Seperate signer from provider in user-facing API #270

Merged
merged 3 commits into from
Sep 4, 2023

Conversation

piotr-roslaniec
Copy link
Contributor

@piotr-roslaniec piotr-roslaniec commented Aug 23, 2023

Type of PR:

  • Feature

Required reviews:

  • 2

What this does:

  • Separates signer and provider abstractions in user-facing APIs

Issues fixed/closed:

Why it's needed:

Explain how this PR fits in the greater context of the NuCypher Network.
E.g., if this PR addresses a nucypher/productdev issue, let reviewers know!

Notes for reviewers:

  • Please see tests for the relevant changes in the user-facing API
  • Now, a lot of methods will require two parameters instead of one (signer, provider). This is a mild inconvenience, and I could argue that we could simply take one of these instead and then check for the connection with the other. But in that case, we would have to throw an error when the user mishandles the API. We could also introduce our own type, SignerWithProvider, and use it where appropriate. IMHO these considerations are best left for taco API, and not for this PR in particular.
  • This change will be superseded by changes in Migrate from ethers-v5 to viem #271 in the future
  • Rebase once this PR is merged: Apply PR suggestions for #273 #286

@github-actions
Copy link

Bundled size for the package is listed below:

build/main/src/characters: 74.22 KB
build/main/src/kits: 19.53 KB
build/main/src/conditions/context: 42.97 KB
build/main/src/conditions/predefined: 19.53 KB
build/main/src/conditions/base: 54.69 KB
build/main/src/conditions: 156.25 KB
build/main/src/agents: 39.06 KB
build/main/src/sdk/strategy: 35.16 KB
build/main/src/sdk: 46.88 KB
build/main/src/policies: 19.53 KB
build/main/src: 433.59 KB
build/main/types/ethers-contracts/factories: 82.03 KB
build/main/types/ethers-contracts: 152.34 KB
build/main/types: 156.25 KB
build/main: 644.53 KB
build/module/src/characters: 74.22 KB
build/module/src/kits: 19.53 KB
build/module/src/conditions/context: 42.97 KB
build/module/src/conditions/predefined: 19.53 KB
build/module/src/conditions/base: 54.69 KB
build/module/src/conditions: 156.25 KB
build/module/src/agents: 39.06 KB
build/module/src/sdk/strategy: 31.25 KB
build/module/src/sdk: 42.97 KB
build/module/src/policies: 19.53 KB
build/module/src: 425.78 KB
build/module/types/ethers-contracts/factories: 82.03 KB
build/module/types/ethers-contracts: 152.34 KB
build/module/types: 156.25 KB
build/module: 636.72 KB
build: 1.26 MB

@codecov-commenter
Copy link

codecov-commenter commented Aug 25, 2023

Codecov Report

Merging #270 (0c28fa5) into alpha (b7161fd) will increase coverage by 0.04%.
The diff coverage is 76.19%.

@@            Coverage Diff             @@
##            alpha     #270      +/-   ##
==========================================
+ Coverage   80.58%   80.63%   +0.04%     
==========================================
  Files          36       36              
  Lines         994     1012      +18     
  Branches      108      112       +4     
==========================================
+ Hits          801      816      +15     
- Misses        185      188       +3     
  Partials        8        8              
Files Changed Coverage Δ
src/agents/coordinator.ts 27.27% <0.00%> (ø)
src/agents/subscription-manager.ts 17.39% <0.00%> (ø)
src/dkg.ts 28.20% <0.00%> (ø)
src/sdk/strategy/pre-strategy.ts 98.55% <ø> (ø)
src/policies/policy.ts 84.31% <60.00%> (ø)
src/conditions/condition-expr.ts 97.87% <83.33%> (-2.13%) ⬇️
src/conditions/context/context.ts 94.73% <87.50%> (-3.09%) ⬇️
src/characters/alice.ts 91.11% <100.00%> (ø)
src/characters/cbd-recipient.ts 93.65% <100.00%> (ø)
src/characters/pre-recipient.ts 82.08% <100.00%> (ø)
... and 3 more

Copy link
Member

@manumonti manumonti left a comment

Choose a reason for hiding this comment

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

Code looks good to me. 👍

About the convenience of separating Web3Provider into Provider and Signer, I would agree. For instance, recent versions of ethers have introduced this strong division (see "Coming from Web3.js" notice in https://docs.ethers.org/v5/api/providers/provider/#Provider).

IMHO this establishes a clear line between methods that require a provider with signer (like MetaMask does) and others that can use just a provider (like just using Infura or Alchemy endpoint). So, the developers have a cleaner and less confusing vision about what's going on under TACo/CBD.

I think that with some good examples in documentation, we can easily make clear for adopters how to get a signer from a provider (as Piotr mentioned).

Although maybe this is not the best place to discuss it, I would prefer not to choose the option of creating a SignerWithProvider object, since IMHO this add some complexity to the code, which can result in confusion. The terms Provider and Signer are pretty common in web3 development and I think most of the developers are familiar with them.

@jdbertron
Copy link

Thanks for the explanation.

src/agents/subscription-manager.ts Show resolved Hide resolved
@@ -85,9 +85,10 @@ export class ConditionExpression {
}

public buildContext(
provider: ethers.providers.Web3Provider
provider: ethers.providers.Provider,
signer: ethers.Signer
Copy link
Member

@derekpierre derekpierre Aug 29, 2023

Choose a reason for hiding this comment

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

Does buildContext get called if there are no context variables in the condition, eg. blocktime condition?

What if there are only user-defined context variables since they don't need anything signed?

Basically, I'm wondering if we properly handle signer being undefined if no signing is needed for the context...either because the condition doesn't need it (in which case buildContext may not even be called), or if there are only user-defined custom context variables in which case there is nothing to be signed.

Copy link
Contributor Author

@piotr-roslaniec piotr-roslaniec Aug 30, 2023

Choose a reason for hiding this comment

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

We could check ConditionExpression for the presence of context parameters and avoid passing signer in if it doesn't contain one. But we still need to make signer available in the user-facing API in case we need it (https://github.com/nucypher/nucypher-ts/blob/sep-signer-provider/src/characters/cbd-recipient.ts#L70)

I was considering this and other cases where the signer is optional. Which one of these approaches do we prefer?

  1. Make the signer optional and throw an error if it turns out we actually need it
  2. Split API into two user flows and make it required in one of them and remove it from the other.

I'm leaning towards the first option. I'm assuming that API users can detect/handle these cases correctly.
If we agree on 1), I will simply add optionality and relevant errors in this PR.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I tentatively implemented these changes.

Copy link
Member

Choose a reason for hiding this comment

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

I think 1) is reasonable.

I'm assuming that API users can detect/handle these cases correctly.

Is there a public API/utility function that can detect whether a signer is needed based on the condition?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think it's the ThresholdDecrypter.retrieveAndDecrypt, which will be exposed by taco.decrypt

Copy link
Member

@derekpierre derekpierre Aug 30, 2023

Choose a reason for hiding this comment

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

I guess I'm wondering, if I'm an API user,

  • if a signer is needed and one is not provided I get prompted somehow
    OR
  • do I need to inspect the condition myself to determine if :userAddress is in it and therefore I know I need to provide a signer
    OR
  • is there a function on a condition object, or the decrypter, or some other utility that provides me with a bool result as to whether I need the signer or not based on the condition
    OR
    ...

TL;DR 😅 If signer is optional, how does the API user determine whether to pass it or not?

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 a signer is needed and one is not provided I get prompted somehow

There are only two ways that the user can learn that:

  • From a function signature (it's "marked" as required, throws an error if missing during TS compilation etc.)
  • From run-time errors - The parameter is marked as optional, but there is some dependency on it being present under certain conditions (context parameter is present or not). And because of this hidden dependency, we have to throw an exception in runtime instead

do I need to inspect the condition myself to determine if :userAddress is in it and therefore I know I need to provide a signer

If you want to avoid having errors thrown in runtime, then yes.

is there a function on a condition object, or the decrypter, or some other utility that provides me with a bool result as to whether I need the signer or not based on the condition

No, but we can add it.

Copy link
Member

Choose a reason for hiding this comment

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

If signer is optional then I think providing such a facility would be good. cc @KPrasch , @cygnusv in case they have a different view.

Copy link
Contributor Author

@piotr-roslaniec piotr-roslaniec Aug 30, 2023

Choose a reason for hiding this comment

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

I've added a ConditionExpression.contextRequiresSigner helper function and related utilities. This is something end-users may use to check if they need to use a signer for their conditions (to build the context). We should document it an advanced usage guide.

@github-actions
Copy link

github-actions bot commented Sep 1, 2023

Bundled size for the package is listed below:

build/main/src/characters: 74.22 KB
build/main/src/kits: 19.53 KB
build/main/src/conditions/context: 42.97 KB
build/main/src/conditions/predefined: 19.53 KB
build/main/src/conditions/base: 54.69 KB
build/main/src/conditions: 156.25 KB
build/main/src/agents: 39.06 KB
build/main/src/sdk/strategy: 35.16 KB
build/main/src/sdk: 46.88 KB
build/main/src/policies: 19.53 KB
build/main/src: 433.59 KB
build/main/types/ethers-contracts/factories: 82.03 KB
build/main/types/ethers-contracts: 152.34 KB
build/main/types: 156.25 KB
build/main: 644.53 KB
build/module/src/characters: 78.13 KB
build/module/src/kits: 19.53 KB
build/module/src/conditions/context: 42.97 KB
build/module/src/conditions/predefined: 19.53 KB
build/module/src/conditions/base: 54.69 KB
build/module/src/conditions: 156.25 KB
build/module/src/agents: 39.06 KB
build/module/src/sdk/strategy: 31.25 KB
build/module/src/sdk: 42.97 KB
build/module/src/policies: 19.53 KB
build/module/src: 429.69 KB
build/module/types/ethers-contracts/factories: 82.03 KB
build/module/types/ethers-contracts: 152.34 KB
build/module/types: 156.25 KB
build/module: 640.63 KB
build: 1.26 MB

@piotr-roslaniec piotr-roslaniec merged commit 281e19e into alpha Sep 4, 2023
12 checks passed
@piotr-roslaniec piotr-roslaniec deleted the sep-signer-provider branch September 4, 2023 08:12
Comment on lines +103 to +104
expect(conditionExpr.contextRequiresSigner()).toBe(false);
expect(conditionContext.requiresSigner()).toBe(false);
Copy link
Member

Choose a reason for hiding this comment

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

How come we need the check on both the ConditionExpr and the Context object? I would have assumed it's only needed in ConditionExpr.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We don't necessarily need to check it - it's just when a wrapper for exposing this functionality is higher up in the API. I think it makes sense to add this check to the tests to document this API.

Copy link
Member

Choose a reason for hiding this comment

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

I guess I really meant "expose" and not "check"

What's the case where the call requiresSigner() is performed on the Context instead of the Condition? Having it in the Context doesn't mesh with my understanding - the Context is just the dictionary of values, but it is the condition that determines whether a signer is needed or not.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think I had some use case in mind but on second thought it's already covered by ContextCondition.validate. I think we can make ContextCondition.requiresSigner private then.

Copy link
Member

Choose a reason for hiding this comment

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

If still needed internally for validation - then yes, private would be better 👍 .

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

Successfully merging this pull request may close these issues.

6 participants