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

Create no_std example for extrinsic creation #556

Merged
merged 22 commits into from
Dec 5, 2023
Merged

Conversation

Niederb
Copy link
Contributor

@Niederb Niederb commented Apr 28, 2023

Adjusted copy of compose_extrinsic_offline.
Closing #543

@Niederb
Copy link
Contributor Author

Niederb commented Apr 28, 2023

@haerdib Not finished yet but can you have a look if this is the functionality you imagined?

  • Next step would be to extract the extrinsic creation into a separate function (see issue Provide an example for async requests #543 )
    • Will be a bit involved because lots of types will have to be made explicit
    • It would be nice to actually compile this part in no_std mode but I don't see an easy way to do it.
  • If possible merging the code into compose_extrinsic_offline to avoid redundancy
    • Check how easy it is once the function is ready

@Niederb Niederb requested a review from haerdib April 28, 2023 07:34
Copy link
Contributor

@haerdib haerdib left a comment

Choose a reason for hiding this comment

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

This definitely goes into the right direction.

Regarding the comment:

Next step would be to extract the extrinsic creation into a separate function

While I wrote that in the issue, that's just a thought on how to make it clear what's std and not. If you have a better idea or if it's too complicated, then feel free to adapt.

It would be nice to actually compile this part in no_std mode but I don't see an easy way to do it.

Neither do I... move it to another crate and everything is an overkill I believe. So let's do it with best effort. That should suffice.


// Construct extrinsic without using Api (no_std).
let additional_extrinsic_params: GenericAdditionalParams<
AssetTip<AssetBalanceFor<Runtime>>,
Copy link
Contributor

Choose a reason for hiding this comment

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

This won't work, as importing the Runtime into no_std is tricky. So I'd use hardcoded Balance type here.

/// Get the balance type from your node runtime and adapt it if necessary.
type Balance = u128;
/// We need AssetTip here, because the kitchensink runtime uses the asset pallet. Change to PlainTip if your node uses the balance pallet only.
type AdditionalParams = GenericAdditionalParams<AssetTip<Balance>, Hash>

additional_extrinsic_params,
);
let call =
RuntimeCall::Balances(BalancesCall::transfer_allow_death { dest: recipient, value: 42 });
Copy link
Contributor

Choose a reason for hiding this comment

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

Same here for the RuntimeCall: In no_std the runtime compiles to wasm, so tricky to import in no_std environments. Better to go without it. That's the fun part now: To get the Call you need the metadata.

Check out how it's done in the worker:
metadata call: https://github.com/integritee-network/worker/blob/master/core-primitives/node-api/metadata/src/lib.rs#L72-L88
Retrieve indexes for a specific pallet: https://github.com/integritee-network/worker/blob/master/core-primitives/node-api/metadata/src/pallet_teeracle.rs

@Niederb Niederb force-pushed the tn/no-std-example branch 2 times, most recently from 82c7825 to b4e4657 Compare June 28, 2023 11:00
@Niederb Niederb marked this pull request as ready for review June 29, 2023 11:52
@Niederb Niederb added the Z3-example Add or fix an example label Jun 29, 2023
@Niederb Niederb requested a review from haerdib June 29, 2023 11:52
Copy link
Contributor

@haerdib haerdib left a comment

Choose a reason for hiding this comment

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

Cool, thanks a lot for the effort!

Two more things:

  1. Can you integrate this example into CI?
  2. Any chance of merging the code into compose_extrinsic_offline to avoid redundancy, as you've mentioned?

type KitchensinkExtrinsicSigner = ExtrinsicSigner<SubstrateKitchensinkConfig>;
type ExtrinsicAddressOf<Signer> = <Signer as SignExtrinsic<AccountId32>>::ExtrinsicAddress;

type Hash = H256; //<Runtime as FrameSystemConfig>::Hash;
Copy link
Contributor

Choose a reason for hiding this comment

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

The situation has changed a little since my last comment - sorry about that. But now you could take the type directly from the Config:


(same for Balance).

// If this is not acceptable, use the Api::new_offline(..) function instead. There are no examples for this,
// because of the constantly changing substrate node. But check out our unit tests - there are Apis created with `new_offline`.
//
// ! Careful: AssetTipExtrinsicParams is used here, because the substrate kitchensink runtime uses assets as tips. But for most
Copy link
Contributor

Choose a reason for hiding this comment

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

Outdated comment

// runtimes, the PlainTipExtrinsicParams needs to be used.
let mut api = Api::<SubstrateKitchensinkConfig, _>::new(client).unwrap();
let extrinsic_signer = ExtrinsicSigner::<_>::new(signer);
// Signer is needed to get the nonce
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
// Signer is needed to get the nonce
// Signer is needed to set the nonce and sign the extrinsic.

MultiAddress::Id(AccountKeyring::Bob.to_account_id());

// Construct extrinsic without using Api (no_std).
let additional_extrinsic_params: AdditionalParams = GenericAdditionalParams::new()
Copy link
Contributor

Choose a reason for hiding this comment

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

Extracting the no_std part in a separate function is not possible? I think that would be rather important, because in actual use cases (just like in Integritee) the std and no_std part will be separate.

.era(Era::mortal(period, header.number.into()), last_finalized_header_hash)
.tip(0);
let extrinsic_params =
GenericExtrinsicParams::<SubstrateKitchensinkConfig, AssetTip<u128>>::new(
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 type could be taken directly from the SubstrateKitchensinkConfig as well:

type ExtrinsicParams = AssetTipExtrinsicParams<Self>;

let header = api.get_header(Some(last_finalized_header_hash)).unwrap().unwrap();
let period = 5;

// Get information out of Api (online).
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
// Get information out of Api (online).
// Get information out of Api (online). This information could also be set offline in the `no_std`, but that would need to be static and adapted whenever the node changes. You can get the information directly from the node runtime file or the api of https://polkadot.js.org.

// Signer is needed to get the nonce
api.set_signer(extrinsic_signer.clone());

// Information for Era for mortal transactions (online).
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
// Information for Era for mortal transactions (online).
// Get the last finalized header to retrieve information for Era for mortal transactions (online).

@Niederb Niederb linked an issue Aug 4, 2023 that may be closed by this pull request
@haerdib
Copy link
Contributor

haerdib commented Sep 14, 2023

Is this PR open for Review btw?

@Niederb
Copy link
Contributor Author

Niederb commented Sep 21, 2023

Is this PR open for Review btw?

@haerdib Unfortunately still not quite... I merged the examples but I'm still not quite happy how the code looks. Also the CI integration is missing.

@Niederb
Copy link
Contributor Author

Niederb commented Nov 28, 2023

The two examples are now merged into a single one. Both approaches are in the main(), extracting separate functions would need more type annotations. There is also still some code duplication which is due to use of different generic types, but overall I think the code is relative straightforward.

@Niederb Niederb requested a review from haerdib November 28, 2023 12:51
println!("Compose extrinsic in no_std environment (No Api instance)");
{
let signer_nonce = api.get_nonce().unwrap();
println!("[+] Alice's Account Nonce is {}", signer_nonce);
Copy link
Contributor

Choose a reason for hiding this comment

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

Would it be possible to move the api part outside of this environment? That would make it clearer, that really no api client is needed (given the information is availbale).
E.g.

let spec_version = api.runtime_version().spec_version;
let transaction_version = api.runtime_version().transaction_version;
let genesis_hash = api.genesis_hash
..
let mut xt = ...;
{
        let extrinsic_params = <AssetRuntimeConfig as Config>::ExtrinsicParams::new(
			spec_version,
			transaction_version,
			signer_nonce,
			genesis_hash,
			additional_extrinsic_params,
		);

		let recipients_extrinsic_address: ExtrinsicAddressOf<AssetExtrinsicSigner> =
			recipient.clone().into();

		let call = compose_call!(
			metadata,
			"Balances",
			"transfer_allow_death",
			recipients_extrinsic_address,
			Compact(4u32)
		);
		xt = compose_extrinsic_offline!(extrinsic_signer, call, extrinsic_params);
}
// Test created extrinsic via api call to the node (online)
let hash = api
			.submit_and_watch_extrinsic_until(xt, XtStatus::InBlock)
			.unwrap()
			.block_hash
			.unwrap();

Copy link
Contributor

Choose a reason for hiding this comment

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

Same for the api (offline) part - move the online part outside?

Does that make sense?

@Niederb Niederb requested a review from haerdib November 30, 2023 07:34
Copy link
Contributor

@haerdib haerdib left a comment

Choose a reason for hiding this comment

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

Cool, I think it makes more sense now. Thanks for the adaptions! Few last nitpicks, other than that it's great :)

examples/examples/compose_extrinsic.rs Outdated Show resolved Hide resolved
.tip(0);

println!("Compose extrinsic in no_std environment (No Api instance)");
let signer_nonce = api.get_nonce().unwrap();
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 move this below the descriptive comment below? I think this belongs to the same category.

examples/examples/compose_extrinsic.rs Show resolved Hide resolved
examples/examples/compose_extrinsic.rs Outdated Show resolved Hide resolved
@Niederb Niederb requested a review from haerdib December 4, 2023 14:50
Copy link
Contributor

@haerdib haerdib left a comment

Choose a reason for hiding this comment

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

Cool, thanks a lot!

@Niederb Niederb merged commit e83aee9 into master Dec 5, 2023
51 checks passed
@haerdib haerdib deleted the tn/no-std-example branch February 5, 2024 15:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Introduce no-std example for extrinsic creation
2 participants