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

Port Polkadot implementation to Rust. #4102

Open
wants to merge 99 commits into
base: master
Choose a base branch
from

Conversation

Neopallium
Copy link
Contributor

@Neopallium Neopallium commented Nov 8, 2024

Description

Continued the C++ -> Rust port of the Polkadot/Substrate blockchain from PR #3857.

Adds Rust crates:

  1. tw_scale - SCALE encoding.
  2. tw_ss58_address - SS58 address.
  3. tw_substrate - Common code for supporting Substrate chains. Substrate chains can implement the SubstrateCoinEntry trait (see the tw_polkadot crate) instead of CoinEntry.
  4. tw_polkadot - Polkadot/Kusama blockchain.

How to test

Types of changes

Checklist

  • Create pull request as draft initially, unless its complete.
  • Add tests to cover changes as needed.
  • Update documentation as needed.
  • If there is a related Issue, mention it in the description.

If you're adding a new blockchain

  • I have read the guidelines for adding a new blockchain.

@Neopallium Neopallium changed the title Polkadot rust impl Port Polkadot implementation to Rust. Nov 8, 2024
@Milerius
Copy link
Collaborator

Great stuff @satoshiotomakan will take a look next week when he is back from PTO

Copy link
Collaborator

@satoshiotomakan satoshiotomakan left a comment

Choose a reason for hiding this comment

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

Thanks for opening the PR! First review iteration

include/TrustWalletCore/TWSS58AddressType.h Outdated Show resolved Hide resolved
rust/Cargo.toml Outdated Show resolved Hide resolved
rust/tw_substrate/src/lib.rs Outdated Show resolved Hide resolved
rust/tw_scale/src/lib.rs Outdated Show resolved Hide resolved
rust/tw_scale/src/lib.rs Outdated Show resolved Hide resolved
rust/tw_substrate/src/extrinsic.rs Outdated Show resolved Hide resolved
rust/tw_substrate/src/extrinsic.rs Outdated Show resolved Hide resolved
rust/chains/tw_polkadot/src/call_encoder/polymesh.rs Outdated Show resolved Hide resolved
rust/chains/tw_polkadot/src/call_encoder/polymesh.rs Outdated Show resolved Hide resolved
Comment on lines 58 to 60
NetworkId::POLKADOT => PolkadotCallEncoder::new(ctx),
NetworkId::KUSAMA => KusamaCallEncoder::new(ctx),
NetworkId::POLYMESH => PolymeshCallEncoder::new(ctx),
Copy link
Collaborator

Choose a reason for hiding this comment

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

While porting chains to Rust, we try to improve overall experience and codebase. Otherwise there is no much need in the migration.
We should handle this on the Entry level. So there should be separate tw_polkadot and tw_kusama crates, where you can, for example, provide a &dyn TWPolkadotCallEncoder object to a signer, or something like that.
Or it will be even better to provide a dynamic object that returns call indices for the asked extrinsic.

Please also note that we started using Polkadot blockchain implementation for all Substrate chains. For example, you can sign a transaction for Polymesh even if it's not in registry.json by using custom CustomCallIndices. But for breaking compatibility, we should use default values if the custom indices are not set for Polkadot or Kusama.

I think there is no need to add tw_polymesh chain, but rather add it to the registry.json file, and we'll use CustomCallIndices for every 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.

I was planning on splitting tw_polkadot into per-chain crates later. For this PR I didn't want to make major structural changes to how the Rust vs C++ Polkadot backend worked.

For a lot of Substrate chains a single generic backend that allows CustomCallIndices would be enough, since they might only need Balance/Staking support. But for chains like Polymesh we have other calls that are needed to make using Trust Wallet work for our users.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Should I split tw_polkadot into 3 different chain impls (polkadot, kusama, polymesh) in this PR? Also we could have one generic Substrate chain crate that requires custom call indices to support chains that just need balance/staking support.

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 have started work on moving Polymesh support into its own coin (tw_polymesh crate). Here is a draft PR for that: PolymeshAssociation#2

Right now it still uses the Polkadot protobufs definition, but we might want to make a new protobuf for Polymesh.

@satoshiotomakan
Copy link
Collaborator

Hi @Neopallium, is the PR ready for the next review iteration?

@Neopallium
Copy link
Contributor Author

Hi @Neopallium, is the PR ready for the next review iteration?

I have some more changes (use TWError to improve errors) that I am in the middle of. Also a few more changes that are needed (from the unresolved comments). I will ping here when ready for the next review.

@Neopallium
Copy link
Contributor Author

@satoshiotomakan Ready for the next review.

@satoshiotomakan
Copy link
Collaborator

Hi @Neopallium, thank you for letting me know! I'll review it next Monday, sorry for the delay

Copy link
Collaborator

@satoshiotomakan satoshiotomakan left a comment

Choose a reason for hiding this comment

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

Hi @Neopallium, thanks for the previous changes, appreciate the effort!
Last review iteration

rust/Cargo.toml Outdated Show resolved Hide resolved
rust/chains/tw_polkadot/Cargo.toml Outdated Show resolved Hide resolved
rust/chains/tw_polkadot/src/call_encoder/generic.rs Outdated Show resolved Hide resolved
Comment on lines +179 to +186
// Special case for batches.
match msg {
SigningVariant::balance_call(b) => {
if let Some(batch) = self.encode_balance_batch_call(b)? {
return Ok(batch);
}
},
SigningVariant::staking_call(s) => {
if let Some(batch) = self.encode_staking_batch_call(s)? {
return Ok(batch);
}
},
_ => (),
}
// non-batch calls.
self.encoder.encode_call(msg)
Copy link
Collaborator

Choose a reason for hiding this comment

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

I find these batch and non-batch procession suboptimal and difficult to understand.
The problem is that some calls are handled in CallEncoder (eg BondAndNominate), and later converted into separated Bond and Nominate, and then forwarded to a Substrate specific encoder.

However, developers need to keep in mind that in PolkadotCallEncoder you should return an error if BondAndNominate is passed. It's not obvious. Also encode_staking_batch_call confuses by returning optional value.

Could you please refactor it by providing a single structure responsible for Proto -> RawOwned conversion?
You can make it the way so tw_polymesh and tw_kusama can just reuse some functions of the function. Please take a look at Greenfield chain as an example:
https://github.com/trustwallet/wallet-core/blob/master/rust/chains/tw_greenfield/src/modules/tx_builder.rs

Copy link
Collaborator

Choose a reason for hiding this comment

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

Please note that GenericBalances and GenericStaking look good to me

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 find these batch and non-batch procession suboptimal and difficult to understand.
That is because of how the Polkadot.proto has special cases for batching. Batching should not be handled inside each pallet. On Substrate networks batching of calls is handled by the Utility pallet and each network can have some differences in that pallet.

I have tried multiple times to handle those special case batching inside the pallet (Balance, Staking), but it make all other simple call cases more complex.

In the next PR for Polymesh I have use a more generic batching method that simplifies the call encoding a lot:
Call encoder:
https://github.com/PolymeshAssociation/wallet-core/blob/polymesh_rust_impl/rust/chains/tw_polymesh/src/call_encoder.rs
Polymesh.proto:
https://github.com/PolymeshAssociation/wallet-core/blob/polymesh_rust_impl/src/proto/Polymesh.proto

The same designed could be used for Polkadot, but changing the proto file would break frontend clients.

rust/tw_scale/src/macros.rs Show resolved Hide resolved
rust/tw_tests/tests/chains/polkadot/polkadot_address.rs Outdated Show resolved Hide resolved
tests/chains/Polkadot/TWAnySignerTests.cpp Outdated Show resolved Hide resolved
tests/chains/Polkadot/TransactionCompilerTests.cpp Outdated Show resolved Hide resolved
@satoshiotomakan
Copy link
Collaborator

Hi @Neopallium, please let me know once the PR is ready for the final review

@Neopallium
Copy link
Contributor Author

@satoshiotomakan The only items remaining is:

  1. Porting the C++ signing tests over to Rust. I still plan on doing this (keeping the same expected outputs), most likely next week.
  2. The batching support. I still think we should be doing batching at the top-level. It would allow batching any set of calls, not just a limited few. Also there are other extrinsic calls (Multi-sig, committee proposals) that need to wrap another call. So it is better to handle that in a generic way.

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.

5 participants