4

What is idiomatic way to submit signed extrinsic in outer node?

Is it using BasicPool and submit_local? Would Rust example at the bottom be be good approach? If it is how to:

  • properly choose period and current args for Era::mortal?
  • obtain nonce for frame_system::CheckNonce?

Also it seems using subxt requires way less boilerplate than example below.

Is there better interface, with less boilerplate than in example below, I could use when submitting signed extrinsics in outer node?

There's always possibility of using subxt but that doesn't seem right since I've got access to runtime, transaction pool and needed types within outer node.

fn submit_signed_extrinsic_example(
    transaction_pool: &dyn sc_transaction_pool_api::LocalTransactionPool<
        Block = Block,
        Hash = Hash,
        Error = sc_transaction_pool::error::Error,
    >,
    keystore: &sc_keystore::LocalKeystore,
    genesis_hash: sp_core::H256,
    block_hash: sp_core::H256,
) -> Result<()> {
    let call = my_pallet::Call::my_call {};

    let keys = keystore.sr25519_public_keys(key_types::ACCOUNT);

    let public = keys.first().ok_or(Error::NoNodeAccountKey)?.clone();

    let extra: runtime::SignedExtra = (
        frame_system::CheckNonZeroSender::<runtime::Runtime>::new(),
        frame_system::CheckSpecVersion::<runtime::Runtime>::new(),
        frame_system::CheckTxVersion::<runtime::Runtime>::new(),
        frame_system::CheckGenesis::<runtime::Runtime>::new(),
        frame_system::CheckMortality::<runtime::Runtime>::from(sp_runtime::generic::Era::mortal(
            0, 0,
        )),
        frame_system::CheckNonce::<runtime::Runtime>::from(0),
        frame_system::CheckWeight::<runtime::Runtime>::new(),
        pallet_transaction_payment::ChargeTransactionPayment::<runtime::Runtime>::from(0),
    );

    let payload = runtime::SignedPayload::from_raw(
        call.clone().into(),
        extra.clone(),
        (
            (),
            runtime::VERSION.spec_version,
            runtime::VERSION.transaction_version,
            genesis_hash,
            genesis_hash,
            (),
            (),
            (),
        ),
    );

    let signature = sp_runtime::MultiSignature::Sr25519(payload.using_encoded(|payload| {
        keystore
            .sr25519_sign(key_types::ACCOUNT, &public, payload)?
            .ok_or(Error::NoNodeAccountKey)
    })?);

    let extrinsic = runtime::UncheckedExtrinsic::new_signed(
        call.clone().into(),
        sp_runtime::AccountId32::from(public).into(),
        signature,
        extra,
    );
    let block_id = sp_runtime::generic::BlockId::Hash(block_hash);

    transaction_pool.submit_local(&block_id, extrinsic.into())?;

    Ok(())
}

1
  • Mmm, I think in this case Subxt isn't super relevant (it's more about interacting with a node via the RPC APIs), though in theory you could build the signed extrinsic bytes with it if having those helped. Probably not worth all of the added dependencies just for that though, even if it was useful!
    – jsdw
    Aug 29, 2023 at 13:56

1 Answer 1

2

Answering my own question:

If you look at substrate RPC interface, calls author_submitExtrinsic and author_submitAndWatchExtrinsic correspond to submit_one and submit_and_watch of TransactionPool trait.

So easiest way to submit extrinsics in outer node is to grab TransactionPool and utilize it for extrinsic submission.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.