Skip to content
This repository was archived by the owner on May 22, 2023. It is now read-only.

Commit 1c2eaa3

Browse files
ascjonespepyakin
authored andcommitted
srml-contract: test calls not dispatched if tx fails (paritytech#2917)
* Test for not dispatching calls if top level execution fails * Add comment to test * Only dispatch calls if contract execution succeeded Note that `calls` should be empty in this case, but this makes things clearer * Add comment to test Co-Authored-By: Sergei Pepyakin <s.pepyakin@gmail.com> * Revert: Only dispatch calls if contract execution succeeded
1 parent 7f61107 commit 1c2eaa3

File tree

1 file changed

+105
-1
lines changed

1 file changed

+105
-1
lines changed

srml/contracts/src/tests.rs

Lines changed: 105 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ use runtime_primitives::testing::{Digest, DigestItem, Header, UintAuthorityId, H
3333
use runtime_primitives::traits::{BlakeTwo256, IdentityLookup};
3434
use runtime_primitives::BuildStorage;
3535
use srml_support::{
36-
assert_ok, impl_outer_dispatch, impl_outer_event, impl_outer_origin, storage::child,
36+
assert_ok, assert_err, impl_outer_dispatch, impl_outer_event, impl_outer_origin, storage::child,
3737
traits::Currency, StorageMap, StorageValue
3838
};
3939
use std::sync::atomic::{AtomicUsize, Ordering};
@@ -522,6 +522,110 @@ fn dispatch_call() {
522522
);
523523
}
524524

525+
const CODE_DISPATCH_CALL_THEN_TRAP: &str = r#"
526+
(module
527+
(import "env" "ext_dispatch_call" (func $ext_dispatch_call (param i32 i32)))
528+
(import "env" "memory" (memory 1 1))
529+
530+
(func (export "call")
531+
(call $ext_dispatch_call
532+
(i32.const 8) ;; Pointer to the start of encoded call buffer
533+
(i32.const 11) ;; Length of the buffer
534+
)
535+
(unreachable) ;; trap so that the top level transaction fails
536+
)
537+
(func (export "deploy"))
538+
539+
(data (i32.const 8) "\00\00\03\00\00\00\00\00\00\00\C8")
540+
)
541+
"#;
542+
const HASH_DISPATCH_CALL_THEN_TRAP: [u8; 32] = hex!("55fe5c142dfe2519ca76c7c9b9f05012bd2624b7dcc128d2ce5a7af9d2da1846");
543+
544+
#[test]
545+
fn dispatch_call_not_dispatched_after_top_level_transaction_failure() {
546+
// This test can fail due to the encoding changes. In case it becomes too annoying
547+
// let's rewrite so as we use this module controlled call or we serialize it in runtime.
548+
let encoded = Encode::encode(&Call::Balances(balances::Call::transfer(CHARLIE, 50)));
549+
assert_eq!(&encoded[..], &hex!("00000300000000000000C8")[..]);
550+
551+
let wasm = wabt::wat2wasm(CODE_DISPATCH_CALL_THEN_TRAP).unwrap();
552+
553+
with_externalities(
554+
&mut ExtBuilder::default().existential_deposit(50).build(),
555+
|| {
556+
Balances::deposit_creating(&ALICE, 1_000_000);
557+
558+
assert_ok!(Contract::put_code(Origin::signed(ALICE), 100_000, wasm));
559+
560+
// Let's keep this assert even though it's redundant. If you ever need to update the
561+
// wasm source this test will fail and will show you the actual hash.
562+
assert_eq!(System::events(), vec![
563+
EventRecord {
564+
phase: Phase::ApplyExtrinsic(0),
565+
event: MetaEvent::balances(balances::RawEvent::NewAccount(1, 1_000_000)),
566+
topics: vec![],
567+
},
568+
EventRecord {
569+
phase: Phase::ApplyExtrinsic(0),
570+
event: MetaEvent::contract(RawEvent::CodeStored(HASH_DISPATCH_CALL_THEN_TRAP.into())),
571+
topics: vec![],
572+
},
573+
]);
574+
575+
assert_ok!(Contract::create(
576+
Origin::signed(ALICE),
577+
100,
578+
100_000,
579+
HASH_DISPATCH_CALL_THEN_TRAP.into(),
580+
vec![],
581+
));
582+
583+
// Call the newly created contract. The contract is expected to dispatch a call
584+
// and then trap.
585+
assert_err!(
586+
Contract::call(
587+
Origin::signed(ALICE),
588+
BOB, // newly created account
589+
0,
590+
100_000,
591+
vec![],
592+
),
593+
"during execution"
594+
);
595+
assert_eq!(System::events(), vec![
596+
EventRecord {
597+
phase: Phase::ApplyExtrinsic(0),
598+
event: MetaEvent::balances(balances::RawEvent::NewAccount(1, 1_000_000)),
599+
topics: vec![],
600+
},
601+
EventRecord {
602+
phase: Phase::ApplyExtrinsic(0),
603+
event: MetaEvent::contract(RawEvent::CodeStored(HASH_DISPATCH_CALL_THEN_TRAP.into())),
604+
topics: vec![],
605+
},
606+
EventRecord {
607+
phase: Phase::ApplyExtrinsic(0),
608+
event: MetaEvent::balances(
609+
balances::RawEvent::NewAccount(BOB, 100)
610+
),
611+
topics: vec![],
612+
},
613+
EventRecord {
614+
phase: Phase::ApplyExtrinsic(0),
615+
event: MetaEvent::contract(RawEvent::Transfer(ALICE, BOB, 100)),
616+
topics: vec![],
617+
},
618+
EventRecord {
619+
phase: Phase::ApplyExtrinsic(0),
620+
event: MetaEvent::contract(RawEvent::Instantiated(ALICE, BOB)),
621+
topics: vec![],
622+
},
623+
// ABSENCE of events which would be caused by dispatched Balances::transfer call
624+
]);
625+
},
626+
);
627+
}
628+
525629
const CODE_SET_RENT: &str = r#"
526630
(module
527631
(import "env" "ext_dispatch_call" (func $ext_dispatch_call (param i32 i32)))

0 commit comments

Comments
 (0)