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

Add proper test Custom values #1147

Merged
merged 16 commits into from Sep 12, 2023

Conversation

tadeohepperle
Copy link
Contributor

fixes #1134.

I added a test that uses some generated metadata to get custom values via an OfflineClient:

// static query:
let foo_address = node::custom().foo();
let foo = api.custom_values().at(&foo_address)?;
assert_eq!(foo, expected_foo);

// dynamic query:
let foo_value = api.custom_values().at("Foo")?;
let foo: Foo = foo_value.as_type()?;
assert_eq!(foo, expected_foo);

@tadeohepperle tadeohepperle requested a review from a team as a code owner August 29, 2023 13:56
@@ -1,15 +1,14 @@
// Copyright 2019-2023 Parity Technologies (UK) Ltd.
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: I think we could keep the copyright here (everywhere else)

fn main() {
let metadata_prefixed = generate_custom_metadata::metadata_custom_values_foo();
let bytes = metadata_prefixed.encode();
std::fs::write("./artifacts/metadata_with_custom_values.scale", bytes)
Copy link
Collaborator

@jsdw jsdw Aug 30, 2023

Choose a reason for hiding this comment

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

Can we add a call to this to /scripts/artifacts.sh (ie cargo run --bin generate-custom-metadata > artifacts/metadata_with_custom_values.scale), so that running that script updates all of our artifacts :)

(also, let's either allow the caller to decide where to write the file to, or just write the file to stdout, so that the file location can be specified in artifacts.sh so it's clear from that script what is being generated)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oh yes, that is a great idea!

@@ -48,7 +54,7 @@ pub fn metadata_custom_values_foo() -> RuntimeMetadataPrefixed {
let unit_ty = registry.register_type(&meta_type::<()>());

// Metadata needs to contain this DispatchError, since codegen looks for it.
registry.register_type(&meta_type::<crate::utils::dispatch_error::ArrayDispatchError>());
registry.register_type(&meta_type::<dispatch_error::ArrayDispatchError>());

let metadata = RuntimeMetadataV15 {
Copy link
Collaborator

@jsdw jsdw Aug 30, 2023

Choose a reason for hiding this comment

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

Below, we add some CustomMetadata with a valid Foo value inside it (sorry; I can't comment on the exact line).

Could we also add a custom value with a random type ID that isn't in use (eg u32::MAX); people might opt to do this too and we want to be sure that we can still access the bytes and that nothing will panic (just Err) if we try decoding them.

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 that case we just test if the bytes are returned from the dynamic interface right? E.g. we do not generate any static custom value address. Or should we generate a static address that has a return type of Vec<u8>?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Since we've decided that you can't call .at() on a value with an invalid type ID, perhaps we can test that such an attempt fails with a compile error (via trybuild), but that .at_bytes() works, sicne that's what we've decided to allow.

@jsdw
Copy link
Collaborator

jsdw commented Aug 30, 2023

Looks good; just a couple of small comments! I'm not super keen on the dispatch error types being exported from the new generate-custom-values crate (it's a bit of a random place for them), but I'm not too bothered for now (I think ultimately a test-utils crate to dump various helper code like this is probably the way to go ultimately, so feel free to do that at some point if you run out of things :))

Comment on lines 121 to 123
pub fn types(&self) -> &PortableRegistry {
self.type_registry
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

Nit: Instead of exposing this in this TypeGenerator, let's just pass the PortableRegistry into the function instead (it's accessible as something like self.metadata.types() in the parent function). Not a big deal though!

&hash(custom_value.bytes()),
)
let name_hash = hash(custom_value.name.as_bytes());
let bytes_hash =hash(custom_value.bytes());
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
let bytes_hash =hash(custom_value.bytes());
let bytes_hash = hash(custom_value.bytes());

&hash(custom_value.bytes()),
)
let name_hash = hash(custom_value.name.as_bytes());
let bytes_hash =hash(custom_value.bytes());
Copy link
Collaborator

Choose a reason for hiding this comment

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

The goal of this validation stuff is to make sure that the code generated from some metadata is still compatible with some node (and the node metadata) so that it can still be used without issue.

With that in mind, we don't actually need to check that custom_value.bytes() is identical; it doesn't matter if the bytes are different so long as they still decode OK into the generated type that the codegen is expecting. So let's remove the hashing of bytes. Otherwise looks good offhand (though this stuff always needs some thought and I obv missed this last time :))

@@ -10,6 +10,9 @@ use crate::metadata::DecodeWithMetadata;
pub trait CustomValueAddress {
/// The type of the custom value.
type Target: DecodeWithMetadata;
/// Should be set to `Yes` for Dynamic values and static values that have a valid type.
/// Should be `()` for custom values, that have an invalid type id.
type Decodable;
Copy link
Collaborator

@jsdw jsdw Sep 11, 2023

Choose a reason for hiding this comment

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

Nice! For consistency with storage, how about IsDecodable?

@@ -26,7 +26,7 @@ impl<T, Client> CustomValuesClient<T, Client> {
impl<T: Config, Client: OfflineClientT<T>> CustomValuesClient<T, Client> {
/// Access a custom value by the address it is registered under. This can be just a [str] to get back a dynamic value,
/// or a static address from the generated static interface to get a value of a static type returned.
pub fn at<Address: CustomValueAddress + ?Sized>(
pub fn at<Address: CustomValueAddress<Decodable =Yes> + ?Sized>(
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
pub fn at<Address: CustomValueAddress<Decodable =Yes> + ?Sized>(
pub fn at<Address: CustomValueAddress<Decodable = Yes> + ?Sized>(

.ok_or_else(|| MetadataError::CustomValueNameNotFound(address.name().to_string()))?;

Ok(custom_value.bytes().to_vec())
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

Looks good! Shame about needing to to_vec(), but I can see it's because the metadata is cloned and not referenced; maybe something we can address elsewhere at some point :)

scale-info = { workspace = true, features = ["bit-vec"] }
frame-metadata ={ workspace = true }
codec = { package = "parity-scale-codec", workspace = true, features = ["derive", "bit-vec"] }
subxt = { workspace = true, features = ["native", "jsonrpsee"] }
subxt = { workspace = true, features = ["native", "jsonrpsee", "substrate-compat"] }
Copy link
Collaborator

Choose a reason for hiding this comment

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

Where is substrate-compat needed? Offhand I can't see any use for it but I'm likely to be missing something :)

@jsdw
Copy link
Collaborator

jsdw commented Sep 11, 2023

Looking good, and I like the new bytes_at() approach for valiues without valid type IDs!

I left a few comments which are mostly just little things, but with these small bits looked at I'd be happy to see this merge:

  • Decodable -> IsDecodable
  • Don't hash the bytes in the validation check (name and type should suffice)
  • Confirm whether substrate-compat feature is needed in the subxt dev-dep
  • Check on the test failure; I'm not quite sure what that's about so would be good to understand it :)

@tadeohepperle
Copy link
Contributor Author

Confirm whether substrate-compat feature is needed in the subxt dev-dep

You are right, it is not necessary, I changed an import to config::substrate::H256 and now it works without it. I found the cause of the test failure (older custom value metadata) and now it should be okay.

Co-authored-by: James Wilson <james@jsdw.me>
Copy link
Collaborator

@jsdw jsdw left a comment

Choose a reason for hiding this comment

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

The changes look great to me; nice one!

cargo run --bin subxt metadata --file artifacts/polkadot_metadata_full.scale --pallets "" > artifacts/polkadot_metadata_tiny.scale
# generate a metadata file that only contains some custom metadata
cargo run --bin generate-custom-metadata > artifacts/metadata_with_custom_values.scale
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: Could use an extra line at the end of the script

scale-info = { workspace = true, features = ["bit-vec"] }
frame-metadata ={ workspace = true }
codec = { package = "parity-scale-codec", workspace = true, features = ["derive", "bit-vec"] }
subxt = { workspace = true, features = ["native", "jsonrpsee"] }
subxt-metadata = { workspace = true }
generate-custom-metadata = { path = "../generate-custom-metadata" }
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: extra line

Copy link
Contributor

@lexnv lexnv left a comment

Choose a reason for hiding this comment

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

LGTM! Nice one!

cargo run --bin subxt metadata --file artifacts/polkadot_metadata_full.scale --pallets "" > artifacts/polkadot_metadata_tiny.scale
# generate a metadata file that only contains some custom metadata
cargo run --bin generate-custom-metadata > artifacts/metadata_with_custom_values.scale
Copy link
Contributor

Choose a reason for hiding this comment

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

Not related to this PR, just to not forget about it, would be a good practice to exit on failures:
set -e such we don't move forward if some prior step fails

@tadeohepperle tadeohepperle merged commit c8462de into master Sep 12, 2023
9 checks passed
@tadeohepperle tadeohepperle deleted the tadeo-hepperle-custom-values-add-proper-test branch September 12, 2023 13:46
@jsdw jsdw mentioned this pull request Sep 27, 2023
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.

Custom Values: add proper tests of the interface
3 participants