Skip to content
Open
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
5db2223
Add `TransactionStorageApi` definition
bkontur Dec 15, 2025
8a53165
Add pub getter `storage_period` to transaction-storage pallet
bkontur Dec 15, 2025
3c1e637
Add TransactionStorageApi to the kithchensink
bkontur Dec 16, 2025
26214fe
Move `DEFAULT_STORAGE_PERIOD`
bkontur Dec 16, 2025
e4bb2e5
Call runtime API to get the storage_period for inherent data provider…
bkontur Dec 16, 2025
432f85b
Licence
bkontur Dec 16, 2025
7c5931c
Update from github-actions[bot] running command 'prdoc --audience run…
github-actions[bot] Dec 16, 2025
5dc9364
Update substrate/frame/transaction-storage/src/lib.rs
bkontur Dec 16, 2025
14cf3f1
Update prdoc/pr_10656.prdoc
bkontur Dec 16, 2025
6f32290
Update from github-actions[bot] running command 'fmt'
github-actions[bot] Dec 16, 2025
1806e30
Update prdoc/pr_10656.prdoc
bkontur Dec 16, 2025
27c42ba
Fix indentation in the prdoc
EgorPopelyaev Dec 16, 2025
58113e4
Fix CI?
bkontur Dec 16, 2025
3e2b9ea
Update prdoc/pr_10656.prdoc
bkontur Dec 16, 2025
041d5e3
Merge branch 'master' into bko-tx-pallet-storage-period
bkontur Dec 16, 2025
d0e55e8
Removed transaction_storage_runtime_api_call_works
bkontur Dec 16, 2025
b725081
Use directly runtime API, no need for "hacky" workaround `client.call…
bkontur Dec 17, 2025
9f77401
Rename `StoragePeriod` to `RetentionPeriod`
bkontur Dec 17, 2025
863492f
Update from github-actions[bot] running command 'fmt'
github-actions[bot] Dec 17, 2025
c92a3fd
Update prdoc/pr_10656.prdoc
bkontur Dec 17, 2025
2773a62
Merge branch 'master' into bko-tx-pallet-storage-period
bkontur Dec 19, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 15 additions & 0 deletions prdoc/pr_10656.prdoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
title: '[pallet-transaction-storage] Configurable StoragePeriod'
doc:
- audience:
- Runtime Dev
- Node Dev
description: |-
This PR refactors `pallet-transaction-storage` and `sp-transaction-storage-proof` (the `new_data_provider` inherent provider), both of which rely on a hard-coded `DEFAULT_STORAGE_PERIOD`. This PR:
- adds a new configurable argument `storage_period` to the `new_data_provider`
- introduces the `TransactionStorageApi::storage_period` runtime API, which the runtime can specify arbitrary
- provides an example of using `new_data_provider`, with the node client calling the runtime API when constructing inherent provider data
crates:
- name: sp-transaction-storage-proof
bump: patch
- name: pallet-transaction-storage
bump: patch
1 change: 1 addition & 0 deletions substrate/bin/node/cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@ wat = { workspace = true }
node-testing = { workspace = true }
sc-service-test = { workspace = true }
substrate-cli-test-utils = { workspace = true }
substrate-rpc-client = { workspace = true }

[build-dependencies]
clap = { optional = true, workspace = true }
Expand Down
10 changes: 5 additions & 5 deletions substrate/bin/node/cli/src/service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -627,11 +627,11 @@ pub fn new_full_base<N: NetworkBackend<Block, <Block as BlockT>::Hash>>(
slot_duration,
);

let storage_proof =
sp_transaction_storage_proof::registration::new_data_provider(
&*client_clone,
&parent,
)?;
let storage_proof = sp_transaction_storage_proof::registration::new_data_provider(
&*client_clone,
&parent,
sp_transaction_storage_proof::runtime_api::client::retrieve_storage_period(&client_clone, parent)?,
)?;

Ok((slot, timestamp, storage_proof))
}
Expand Down
56 changes: 56 additions & 0 deletions substrate/bin/node/cli/tests/runtime_api_works.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// This file is part of Substrate.

// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0

// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.

#![cfg(unix)]
use codec::Decode;
use polkadot_sdk::sp_runtime::traits::NumberFor;
use std::time::Duration;
use substrate_cli_test_utils as common;
use substrate_rpc_client::{ws_client, StateApi};

#[tokio::test]
async fn transaction_storage_runtime_api_call_works() {
common::run_with_timeout(Duration::from_secs(60 * 10), async move {
// Run the node.
let mut node = common::KillChildOnDrop(common::start_node());
let stderr = node.stderr.take().unwrap();
let ws_url = common::extract_info_from_output(stderr).0.ws_url;
common::wait_n_finalized_blocks(1, &ws_url).await;
let block_hash = common::block_hash(1, &ws_url).await.unwrap();
node.assert_still_running();

// Call the runtime API.
let rpc = ws_client(ws_url).await.unwrap();
let result = rpc
.call(
String::from("TransactionStorageApi_storage_period"),
vec![].into(),
Some(block_hash),
)
.await
.unwrap();

// Decode and assert the received value.
let storage_period: NumberFor<kitchensink_runtime::Block> =
Decode::decode(&mut &result.0[..]).unwrap();
assert_eq!(storage_period, 100800);

node.stop();
})
.await;
}
6 changes: 6 additions & 0 deletions substrate/bin/node/runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3776,6 +3776,12 @@ pallet_revive::impl_runtime_apis_plus_revive_traits!(
}
}

impl sp_transaction_storage_proof::runtime_api::TransactionStorageApi<Block> for Runtime {
fn storage_period() -> NumberFor<Block> {
TransactionStorage::storage_period()
}
}

#[cfg(feature = "try-runtime")]
impl frame_try_runtime::TryRuntime<Block> for Runtime {
fn on_runtime_upgrade(checks: frame_try_runtime::UpgradeCheckSelect) -> (Weight, Weight) {
Expand Down
2 changes: 1 addition & 1 deletion substrate/frame/transaction-storage/src/benchmarking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ mod benchmarks {
vec![0u8; T::MaxTransactionSize::get() as usize],
)?;
}
run_to_block::<T>(StoragePeriod::<T>::get() + BlockNumberFor::<T>::one());
run_to_block::<T>(crate::Pallet::<T>::storage_period() + BlockNumberFor::<T>::one());
let encoded_proof = proof();
let proof = TransactionStorageProof::decode(&mut &*encoded_proof).unwrap();

Expand Down
24 changes: 15 additions & 9 deletions substrate/frame/transaction-storage/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,9 @@ pub type CreditOf<T> = Credit<<T as frame_system::Config>::AccountId, <T as Conf
pub use pallet::*;
pub use weights::WeightInfo;

/// Default storage period for data (in blocks).
pub const DEFAULT_STORAGE_PERIOD: u32 = 100800;

// TODO: https://github.com/paritytech/polkadot-bulletin-chain/issues/139 - Clarify purpose of allocator limits and decide whether to remove or use these constants.
/// Maximum bytes that can be stored in one transaction.
// Setting higher limit also requires raising the allocator limit.
Expand Down Expand Up @@ -218,7 +221,7 @@ pub mod pallet {
// Drop obsolete roots. The proof for `obsolete` will be checked later
// in this block, so we drop `obsolete` - 1.
weight.saturating_accrue(db_weight.reads(1));
let period = StoragePeriod::<T>::get();
let period = Self::storage_period();
let obsolete = n.saturating_sub(period.saturating_add(One::one()));
if obsolete > Zero::zero() {
weight.saturating_accrue(db_weight.writes(1));
Expand All @@ -235,7 +238,7 @@ pub mod pallet {
ProofChecked::<T>::take() || {
// Proof is not required for early or empty blocks.
let number = frame_system::Pallet::<T>::block_number();
let period = StoragePeriod::<T>::get();
let period = Self::storage_period();
let target_number = number.saturating_sub(period);

target_number.is_zero() || {
Expand Down Expand Up @@ -264,7 +267,7 @@ pub mod pallet {
!T::MaxTransactionSize::get().is_zero(),
"MaxTransactionSize must be greater than zero"
);
let default_period = sp_transaction_storage_proof::DEFAULT_STORAGE_PERIOD.into();
let default_period = DEFAULT_STORAGE_PERIOD.into();
let storage_period = GenesisConfig::<T>::default().storage_period;
assert_eq!(
storage_period, default_period,
Expand Down Expand Up @@ -394,7 +397,7 @@ pub mod pallet {

// Get the target block metadata.
let number = frame_system::Pallet::<T>::block_number();
let period = StoragePeriod::<T>::get();
let period = Self::storage_period();
let target_number = number.saturating_sub(period);
ensure!(!target_number.is_zero(), Error::<T>::UnexpectedProof);
let transactions =
Expand Down Expand Up @@ -456,8 +459,7 @@ pub mod pallet {
/// Storage fee per transaction.
pub type EntryFee<T: Config> = StorageValue<_, BalanceOf<T>>;

/// Storage period for data in blocks. Should match `sp_storage_proof::DEFAULT_STORAGE_PERIOD`
/// for block authoring.
/// Storage period for data in blocks.
#[pallet::storage]
pub type StoragePeriod<T: Config> = StorageValue<_, BlockNumberFor<T>, ValueQuery>;

Expand All @@ -482,7 +484,7 @@ pub mod pallet {
Self {
byte_fee: 10u32.into(),
entry_fee: 1000u32.into(),
storage_period: sp_transaction_storage_proof::DEFAULT_STORAGE_PERIOD.into(),
storage_period: DEFAULT_STORAGE_PERIOD.into(),
}
}
}
Expand Down Expand Up @@ -525,14 +527,18 @@ pub mod pallet {
) -> Option<BoundedVec<TransactionInfo, T::MaxBlockTransactions>> {
Transactions::<T>::get(block)
}
/// Get ByteFee storage information from outside of this pallet.
/// Get ByteFee storage information from the outside of this pallet.
pub fn byte_fee() -> Option<BalanceOf<T>> {
ByteFee::<T>::get()
}
/// Get EntryFee storage information from outside of this pallet.
/// Get EntryFee storage information from the outside of this pallet.
pub fn entry_fee() -> Option<BalanceOf<T>> {
EntryFee::<T>::get()
}
/// Get StoragePeriod storage information from the outside of this pallet.
pub fn storage_period() -> BlockNumberFor<T> {
StoragePeriod::<T>::get()
}

fn apply_fee(sender: T::AccountId, size: u32) -> DispatchResult {
let byte_fee = ByteFee::<T>::get().ok_or(Error::<T>::NotConfigured)?;
Expand Down
2 changes: 2 additions & 0 deletions substrate/primitives/transaction-storage-proof/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ targets = ["x86_64-unknown-linux-gnu"]
async-trait = { optional = true, workspace = true }
codec = { features = ["derive"], workspace = true }
scale-info = { features = ["derive"], workspace = true }
sp-api = { workspace = true }
sp-core = { optional = true, workspace = true }
sp-inherents = { workspace = true }
sp-runtime = { workspace = true }
Expand All @@ -30,6 +31,7 @@ std = [
"async-trait",
"codec/std",
"scale-info/std",
"sp-api/std",
"sp-core/std",
"sp-inherents/std",
"sp-runtime/std",
Expand Down
9 changes: 4 additions & 5 deletions substrate/primitives/transaction-storage-proof/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@

#![cfg_attr(not(feature = "std"), no_std)]

pub mod runtime_api;

extern crate alloc;

use core::result::Result;
Expand All @@ -33,8 +35,6 @@ pub use sp_inherents::Error;

/// The identifier for the proof inherent.
pub const INHERENT_IDENTIFIER: InherentIdentifier = *b"tx_proof";
/// Storage period for data.
pub const DEFAULT_STORAGE_PERIOD: u32 = 100800;
/// Proof trie value size.
pub const CHUNK_SIZE: usize = 256;

Expand Down Expand Up @@ -168,15 +168,14 @@ pub mod registration {
pub fn new_data_provider<B, C>(
client: &C,
parent: &B::Hash,
storage_period: NumberFor<B>,
) -> Result<InherentDataProvider, Error>
where
B: BlockT,
C: IndexedBody<B>,
{
let parent_number = client.number(*parent)?.unwrap_or(Zero::zero());
let number = parent_number
.saturating_add(One::one())
.saturating_sub(DEFAULT_STORAGE_PERIOD.into());
let number = parent_number.saturating_add(One::one()).saturating_sub(storage_period);
if number.is_zero() {
// Too early to collect proofs.
return Ok(InherentDataProvider::new(None));
Expand Down
76 changes: 76 additions & 0 deletions substrate/primitives/transaction-storage-proof/src/runtime_api.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
// This file is part of Substrate.

// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0

// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! Runtime API definition for the transaction storage proof processing.

use sp_runtime::traits::NumberFor;

sp_api::decl_runtime_apis! {
/// Runtime API trait for transaction storage support.
pub trait TransactionStorageApi {
/// Get the actual value of a storage period in blocks.
fn storage_period() -> NumberFor<Block>;
}
}

#[cfg(feature = "std")]
pub mod client {
use codec::Decode;
use sp_api::{ApiError, CallApiAt, CallApiAtParams};
use sp_core::traits::CallContext;
use sp_runtime::traits::{Block as BlockT, NumberFor};

/// An expected state call key.
pub const TRANSACTION_STORAGE_API_STORAGE_PERIOD: &'static str =
"TransactionStorageApi_storage_period";

/// Fetches the storage period value for a specific block from the runtime API.
///
/// This function interacts with the `TRANSACTION_STORAGE_API_STORAGE_PERIOD` API
/// provided by the runtime.
///
/// # Arguments
/// - `client`: A reference to an object implementing the `CallApiAt` trait, used to interact
/// with the runtime API.
/// - `at_block`: The hash of the specific block for which the storage period is queried.
pub fn retrieve_storage_period<B, C>(
client: &C,
at_block: B::Hash,
) -> Result<NumberFor<B>, ApiError>
where
B: BlockT,
C: CallApiAt<B>,
{
// Call the expected runtime API.
let result = client.call_api_at(CallApiAtParams {
at: at_block,
function: TRANSACTION_STORAGE_API_STORAGE_PERIOD,
arguments: vec![],
overlayed_changes: &Default::default(),
call_context: CallContext::Onchain,
recorder: &None,
extensions: &Default::default(),
})?;

// Decode to `NumberFor<B>`.
Decode::decode(&mut &result[..]).map_err(|e| ApiError::FailedToDecodeReturnValue {
function: TRANSACTION_STORAGE_API_STORAGE_PERIOD,
error: e,
raw: result,
})
}
}
Loading