-
Notifications
You must be signed in to change notification settings - Fork 262
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 example using legacy rpc #1259
Changes from all commits
cfb2191
e655bd1
22f563d
aa1f1ed
6a7b074
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
use subxt::backend::legacy::LegacyRpcMethods; | ||
use subxt::backend::rpc::RpcClient; | ||
use subxt::config::DefaultExtrinsicParamsBuilder; | ||
use subxt::{OnlineClient, PolkadotConfig}; | ||
use subxt_signer::sr25519::dev; | ||
use thiserror::Error; | ||
|
||
#[derive(Debug, Error)] | ||
pub enum Error { | ||
#[error("Unexpected None")] | ||
UnexpectedNone, | ||
} | ||
|
||
// Generate an interface that we can use from the node's metadata. | ||
#[subxt::subxt(runtime_metadata_path = "../artifacts/polkadot_metadata_small.scale")] | ||
pub mod polkadot {} | ||
|
||
#[tokio::main] | ||
async fn main() -> Result<(), Box<dyn std::error::Error>> { | ||
// Create both an OnlineClient and a LegacyRpcClient, configured to talk to Polkadot | ||
// nodes with the same RpcClient using the default URL | ||
let rpc_client = RpcClient::from_url("ws://127.0.0.1:9944").await?; | ||
let api = OnlineClient::<PolkadotConfig>::from_rpc_client(rpc_client.clone()).await?; | ||
let rpc = LegacyRpcMethods::<PolkadotConfig>::new(rpc_client); | ||
|
||
let alice = dev::alice().public_key().to_account_id(); | ||
|
||
// Some example destinations accounts and amounts | ||
let transactions = vec![ | ||
(dev::bob().public_key(), 10000000), | ||
(dev::charlie().public_key(), 12000000), | ||
(dev::ferdie().public_key(), 30000000), | ||
(dev::eve().public_key(), 40000000), | ||
(dev::dave().public_key(), 50000000), | ||
(dev::bob().public_key(), 1000000), | ||
(dev::charlie().public_key(), 1200000), | ||
(dev::ferdie().public_key(), 3000000), | ||
(dev::eve().public_key(), 4000000), | ||
(dev::dave().public_key(), 50000000), | ||
(dev::bob().public_key(), 50000000), | ||
(dev::charlie().public_key(), 22000000), | ||
(dev::ferdie().public_key(), 130000000), | ||
(dev::eve().public_key(), 40003000), | ||
(dev::dave().public_key(), 150000000), | ||
]; | ||
|
||
println!( | ||
"📛 System Name: {:?}\n🩺 Health: {:?}\n🖫 Properties: {:?}\n🔗 Chain: {:?}\n", | ||
rpc.system_name().await?, | ||
rpc.system_health().await?, | ||
rpc.system_properties().await?, | ||
rpc.system_chain().await? | ||
); | ||
|
||
for (dest, amount) in transactions { | ||
let nonce = rpc.system_account_next_index(&alice).await?; | ||
|
||
// Extrinsics are mortal, only valid for a limited number of blocks | ||
// from the current best | ||
let extrinsic_parameters = rpc | ||
.chain_get_header(None) | ||
.await? | ||
.map(|header| { | ||
DefaultExtrinsicParamsBuilder::default() | ||
.mortal(&header, 8) | ||
.build() | ||
}) | ||
.ok_or(Error::UnexpectedNone)?; | ||
let balance_transfer = polkadot::tx() | ||
.balances() | ||
.transfer_allow_death(dest.into(), amount); | ||
let extrinsic = api.tx().create_signed_with_nonce( | ||
&balance_transfer, | ||
&dev::alice(), | ||
nonce, | ||
extrinsic_parameters, | ||
)?; | ||
println!("Submitting {:?} with Nonce:{nonce}", extrinsic.hash()); | ||
|
||
// Submitting without tracking the progress of the extrinsics | ||
let _ = extrinsic.submit().await?; | ||
|
||
// Account nonce will return the next nonce based on finalized extrinsics but | ||
// RPC call System.account_next_index will adjust for transactions already in | ||
// the pool | ||
println!( | ||
"AccountNonce: {:?} System.account_next_index: {:?}", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. IIUC, without the 4 seconds sleep we'd eventually after running a few times have different values those 2 methods? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @lexnv even wihtout the delay |
||
api.tx().account_nonce(&alice).await?, | ||
rpc.system_account_next_index(&alice).await? | ||
); | ||
// Sleep less than block time, but long enough to ensure | ||
// not all transactions end up in the same block. | ||
tokio::time::sleep(tokio::time::Duration::from_secs(1)).await; | ||
} | ||
|
||
for _ in 0..20 { | ||
tokio::time::sleep(tokio::time::Duration::from_secs(3)).await; | ||
let account_nonce = api.tx().account_nonce(&alice).await?; | ||
let system_next = rpc.system_account_next_index(&alice).await?; | ||
|
||
println!( | ||
"AccountNonce: {account_nonce:?} System.account_next_index: {system_next:?} Finalized: {:?}", | ||
rpc.chain_get_finalized_head().await? | ||
); | ||
if account_nonce == system_next { | ||
return Ok(()); | ||
} | ||
} | ||
Ok(()) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Given that
dev::bob()
makes multiple transactions here, did you encounter an edge case with the default API that motives the use ofsystem_accountNextIndex
?Something of the following lines:
dev::bob()
tx during the same finalized blockThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes that is how this started in my primary project a few months back, when the client was disconnected, suddenly nonce could come out of sync. First I solved it with dead counting, which was better but still had issues when disconnected and extrinsics still pending in the transaction pool. I was about to start implementation of code compensating for pending transactions when I was made aware of the
system_accountNextIndex
rpc callhttps://github.com/paritytech/polkadot-sdk/blob/8d2637905ba920dd1f0e8f1212ec98e45420f514/substrate/utils/frame/rpc/system/src/lib.rs#L44-L48
vs
https://github.com/paritytech/polkadot-sdk/blob/8d2637905ba920dd1f0e8f1212ec98e45420f514/substrate/frame/system/rpc/runtime-api/src/lib.rs#L28-L35
Even without the wait the two methods will show different values but as in this example below
AccountNonce: 173
never advances beyond 173, as all transactions are submitted during the same block.But with the wait, it runs slow enough for blocks to be finalized:
When all transactions are completed and finalized, the both methods will again show the same value. Should I add a few lines illustrating this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Feel free to make any changes to this as you like to make it fit in to this project better. I just wanted to give back some of the lessons I learnt while using subxt in my main project to the community and writing the example code I could not find when I needed it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
22f563d could be rolled back if it does not fit.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@lexnv did my explanation to you question make sense or should I try to be more clear what I'm trying to do?
What do we need to change to be able to conclude and merge an rpc-legacy example?