sp-api: Support nested transactions (#14447)

* sp-api: Support nested transactions

Adds support for nested transactions in `sp-api` by using `execute_in_transaction`. This was working
until a recent refactor, but this was actually not intended. However, supporting nested transactions
is a worthwhile feature to have. So, this pr "brings it back" and adds a test to ensure it will not break.

* Make clippy happy

* Assert that the runtime api type is not unwind safe

* Count number of transactions
This commit is contained in:
Bastian Köcher
2023-06-29 18:01:45 +02:00
committed by GitHub
parent 83caca85b6
commit 448989f5ec
5 changed files with 111 additions and 44 deletions
@@ -15,15 +15,20 @@
// See the License for the specific language governing permissions and
// limitations under the License.
use sp_api::{Core, ProvideRuntimeApi};
use sp_runtime::traits::{HashFor, Header as HeaderT};
use std::panic::UnwindSafe;
use sp_api::{ApiExt, Core, ProvideRuntimeApi};
use sp_runtime::{
traits::{HashFor, Header as HeaderT},
TransactionOutcome,
};
use sp_state_machine::{
create_proof_check_backend, execution_proof_check_on_trie_backend, ExecutionStrategy,
};
use substrate_test_runtime_client::{
prelude::*,
runtime::{Block, Header, TestAPI, Transfer},
DefaultTestClientBuilderExt, TestClientBuilder,
DefaultTestClientBuilderExt, TestClient, TestClientBuilder,
};
use codec::Encode;
@@ -187,3 +192,46 @@ fn disable_logging_works() {
assert!(output.contains("Logging from native works"));
}
}
// Certain logic like the transaction handling is not unwind safe.
//
// Ensure that the type is not unwind safe!
static_assertions::assert_not_impl_any!(<TestClient as ProvideRuntimeApi<_>>::Api: UnwindSafe);
#[test]
fn ensure_transactional_works() {
const KEY: &[u8] = b"test";
let client = TestClientBuilder::new().build();
let best_hash = client.chain_info().best_hash;
let runtime_api = client.runtime_api();
runtime_api.execute_in_transaction(|api| {
api.write_key_value(best_hash, KEY.to_vec(), vec![1, 2, 3], false).unwrap();
api.execute_in_transaction(|api| {
api.write_key_value(best_hash, KEY.to_vec(), vec![1, 2, 3, 4], false).unwrap();
TransactionOutcome::Commit(())
});
TransactionOutcome::Commit(())
});
let changes = runtime_api
.into_storage_changes(&client.state_at(best_hash).unwrap(), best_hash)
.unwrap();
assert_eq!(changes.main_storage_changes[0].1, Some(vec![1, 2, 3, 4]));
let runtime_api = client.runtime_api();
runtime_api.execute_in_transaction(|api| {
assert!(api.write_key_value(best_hash, KEY.to_vec(), vec![1, 2, 3], true).is_err());
TransactionOutcome::Commit(())
});
let changes = runtime_api
.into_storage_changes(&client.state_at(best_hash).unwrap(), best_hash)
.unwrap();
assert_eq!(changes.main_storage_changes[0].1, Some(vec![1, 2, 3]));
}