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

feat!: NNS-managed RPC providers #252

Merged
merged 115 commits into from
Sep 5, 2024
Merged

feat!: NNS-managed RPC providers #252

merged 115 commits into from
Sep 5, 2024

Conversation

rvanasa
Copy link
Collaborator

@rvanasa rvanasa commented Aug 13, 2024

This PR makes significant changes to the EVM RPC canister to enable using NNS proposals in place of a trusted principal to add, remove, and update RPC providers.

Progress:

  • Refactor Provider struct for immutability
  • Remove per-provider cycles accounting in favor of decentralized ownership
  • Update JSON-RPC API configuration for default RPC providers
  • Change URL and header config to use {API_KEY} placeholder
  • Adjust URL / header validation logic
  • Include more tests for URL / header validation
  • Refactor to use ProviderId type alias
  • Implement post-upgrade actions to add, remove, and update one or more providers
  • Add new API key system
  • Remove stableSize and stableRead canister methods
  • Simplify principal authorization system
  • Remove authorize, deauthorize, and getAuthorized canister methods
  • Convert to using hard-coded RPC providers and service mappings
  • Add sanity checks for hard-coded providers and service-provider mappings
  • Remove FreeRpc auth
  • Remove PriorityRpc auth as well as getOpenRpcAccess and setOpenRpcAccess
  • Use a fixed number of subnet nodes (28) for all EVM RPC deployments
  • Remove PrincipalStorable struct
  • Include changes from dfinity/ic#1027
  • Include changes from dfinity/ic#1113
  • Add optional public_url to each provider
  • Refactor URLs and headers to use enum variants
  • Add demo flag to locally use EVM RPC canister without cycles payments, e.g. through Candid UI
  • Remove header validation in favor of overwriting default Content-Type header
  • Update state machine test runner logic to test new init/upgrade args
  • Update existing tests
  • Add unit tests for URL validation
  • Test API key insertion
  • Test simplified permission system
  • Update Rust and Motoko E2E tests
  • Add public URLs for all RPC providers

@rvanasa rvanasa changed the title feat!: immutable providers via NNS proposals feat!: NNS-managed RPC providers Aug 19, 2024
rvanasa added a commit to dfinity/ic that referenced this pull request Aug 21, 2024
This PR updates the GitHub CI on the `evm-rpc-canister` branch to be
equivalent to the original GitLab configuration.

This is a temporary measure to unblock development, since we intend to
migrate this branch into the [EVM RPC
repository](internet-computer-protocol/evm-rpc-canister#252)
(which contains the vast majority of the testing for the forked
codebase).
@rvanasa rvanasa marked this pull request as ready for review August 29, 2024 23:57
@rvanasa
Copy link
Collaborator Author

rvanasa commented Aug 30, 2024

This PR should be ready to merge!

Copy link
Collaborator

@gregorydemay gregorydemay left a comment

Choose a reason for hiding this comment

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

Thanks a lot @rvanasa for this new version! I reviewed the whole MR in details and it looks very good! I left a few comments but most of them are minor, I don't see any blocker. Feel free to tell me if those comments are better addressed in a follow-up PR (in case you think there are meaningful to implement)

Cargo.toml Outdated Show resolved Hide resolved
src/memory.rs Outdated Show resolved Hide resolved
}

#[test]
fn should_insert_api_keys() {
Copy link
Collaborator

Choose a reason for hiding this comment

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

additional test ideas:

  1. Set the key for a provider that uses Bearer Token.
  2. Updating a previously existing API key of some provider to None, removes it.
  3. Panic if trying to specify an API key for a provider that uses RpcAccess::Unauthenticated
  4. Panic if a provider is not found.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Sounds good. I'll do this in a separate PR so we can get this one merged.

src/memory.rs Outdated
pub static UNSTABLE_METRICS: RefCell<Metrics> = RefCell::new(Metrics::default());
static UNSTABLE_SUBNET_SIZE: RefCell<u32> = RefCell::new(NODES_IN_FIDUCIARY_SUBNET);
static UNSTABLE_DEMO_STATUS: RefCell<bool> = RefCell::new(false);
Copy link
Collaborator

Choose a reason for hiding this comment

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

understanding question: why should this be unstable? For local development purposes, it would be I think a bit weird if one starts in demo mode, one does some local changes and upgrade, and the canister is back to non-demo mode. I would have a priori saved this flag in stable memory. WDYT?

Copy link
Collaborator Author

@rvanasa rvanasa Sep 4, 2024

Choose a reason for hiding this comment

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

I don't have a strong preference either way, but we might as well keep this in stable memory since that seems more intuitive for how people might use the canister in local development.

It also turns out Storable isn't implemented for bool, at least in the version of ic-stable-structures used in the project. 🙃 I added a BoolStorable for now and will include tests in the PR mentioned in this comment.

src/memory.rs Outdated Show resolved Hide resolved
cycles_per_message_byte: 0,
pub const PROVIDERS: &[Provider] = &[
Provider {
provider_id: 0,
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think it would be maybe be a good thing to have a test ensuring that the provider_id are stable (maybe in another PR). WDYT?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

How would you define "stable" in this context? As a starting point, I added a test to enforce the expected provider_id sequence (starting at zero, increasing by one). This covers the cases of removing or skipping a provider, while other changes are unlikely to occur by accident because each ID is hard-coded.

src/providers.rs Outdated Show resolved Hide resolved
.find(|(_, p)| p.primary && p.chain_id == id)
.or_else(|| providers.iter().find(|(_, p)| p.chain_id == id))
RpcService::Chain(id) => ResolvedRpcService::Provider(
find_provider(|p| p.chain_id == id)
Copy link
Collaborator

Choose a reason for hiding this comment

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

understanding question: Before this was returning the first provider for which p.primary && p.chain_id == id, while now this return the first provider for which p.chain_id == id. Is-it clear that those are actually the same providers after this PR (do we need maybe a unit test for this?) or is-it somehow not that relevant here?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

The logic is equivalent because we never ended up changing the primary flag on any provider in the production canister.

We could use this opportunity to remove the RpcService::Chain() variant given the ambiguity. Because this update will make a few other breaking changes, we could include this in the migration steps for canisters to prepare for the new release.

fn from_bytes(bytes: Cow<[u8]>) -> Self {
Decode!(&bytes, Self).unwrap()
Self(String::from_bytes(bytes))
Copy link
Collaborator

Choose a reason for hiding this comment

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

nit: we should call here the try_from from L.225 to ensure that the API key is valid (and panic otherwise).

Copy link
Collaborator Author

@rvanasa rvanasa Sep 4, 2024

Choose a reason for hiding this comment

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

A concern with doing this is that if we change the validation logic to exclude already registered API keys, this logic could potentially break the canister on upgrade and require a fresh reinstall in another NNS proposal. We could probably accept that risk, but it seems like a tradeoff. WDYT?

src/types.rs Show resolved Hide resolved
@rvanasa
Copy link
Collaborator Author

rvanasa commented Sep 5, 2024

Thank you for the detailed review @gregorydemay! I'll merge this and then will make follow-up changes in separate PRs as mentioned.

@rvanasa rvanasa merged commit 1e5ba9a into main Sep 5, 2024
3 checks passed
@rvanasa rvanasa deleted the nns-providers branch September 5, 2024 17:02
rvanasa added a commit that referenced this pull request Sep 12, 2024
Follow-up testing and bugfixes for #252.

This PR...

* Adds state machine tests for the following scenarios:
  * Set the key for a provider that uses `RpcAuth::BearerToken`.
* Update a previously existing API key of some provider to `None`
removes it.
* Panic if trying to specify an API key for a provider that uses
`RpcAccess::Unauthenticated`
  * Panic if a provider is not found.
* Sets up the following canister upgrade tests:
  * Keep previously inserted API keys.
  * Change or keep demo status based on upgrade args.
* Change or keep principals allowed to manage API key providers based on
upgrade args.
* Includes unit tests for `BoolStorable` and `PrincipalStorable`.
* Removes unused `StringStorable`.
* Changes the `requestCost` canister method to return `0` when in demo
mode.
* Fixes the following bugs:
  * `BoolStorable` had an inverted condition in `from_bytes`
* `getProviderCost()` returned `Nat` instead of `Result<Nat, RpcError>`
in the state machine test logic

Note that the canister upgrade tests use the same Wasm before and after
upgrade. In the future (probably out of scope of this PR), we could
extend this to test upgrading from the most recently deployed canister's
Wasm file.
@rvanasa rvanasa mentioned this pull request Sep 19, 2024
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.

2 participants