-
Notifications
You must be signed in to change notification settings - Fork 446
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
Fail when decoding from storage and not all bytes consumed #1897
Changes from 15 commits
be6fdc6
358c736
d4a49f7
3008310
0766776
cfccd7b
00804c1
363e7cf
3e1612f
f110fe6
e58a560
61bf94a
723a335
cf3aa7c
ece4c65
82fe420
5d2080f
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 |
---|---|---|
|
@@ -47,6 +47,7 @@ pub use self::{ | |
ResolverKey, | ||
}, | ||
storage::{ | ||
decode_all, | ||
AutoStorableHint, | ||
Packed, | ||
Storable, | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -44,6 +44,19 @@ where | |
} | ||
} | ||
|
||
/// Decode and consume all of the given input data. | ||
/// | ||
/// If not all data is consumed, an error is returned. | ||
pub fn decode_all<T: Storable>(input: &mut &[u8]) -> Result<T, scale::Error> { | ||
let res = <T as Storable>::decode(input)?; | ||
|
||
if input.is_empty() { | ||
Ok(res) | ||
} else { | ||
Err("Input buffer has still data left after decoding!".into()) | ||
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. Perhaps would be useful to give the length of the remaining bytes left in a buffer for debugging purposes 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. I'm not sure it is worth bringing in all the string formatting machinery in for that...we need to be mindful of code size. It's possible to debug this without this information by fetching the data at the given key and attempting to decode it into the respective type. |
||
} | ||
} | ||
|
||
pub(crate) mod private { | ||
/// Seals the implementation of `Packed`. | ||
pub trait Sealed {} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
# Ignore build artifacts from the local tests sub-crate. | ||
/target/ | ||
|
||
# Ignore backup files creates by cargo fmt. | ||
**/*.rs.bk | ||
|
||
# Remove Cargo.lock when creating an executable, leave it for libraries | ||
# More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock | ||
Cargo.lock |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
[package] | ||
name = "contract-storage" | ||
version = "4.2.0" | ||
authors = ["Parity Technologies <admin@parity.io>"] | ||
edition = "2021" | ||
publish = false | ||
|
||
[dependencies] | ||
ink = { path = "../../crates/ink", default-features = false } | ||
|
||
[dev-dependencies] | ||
ink_e2e = { path = "../../crates/e2e" } | ||
|
||
[lib] | ||
path = "lib.rs" | ||
|
||
[features] | ||
default = ["std"] | ||
std = [ | ||
"ink/std", | ||
] | ||
ink-as-dependency = [] | ||
e2e-tests = [] |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
use super::contract_storage::*; | ||
use ink_e2e::ContractsBackend; | ||
|
||
type E2EResult<T> = std::result::Result<T, Box<dyn std::error::Error>>; | ||
|
||
#[ink_e2e::test] | ||
async fn get_contract_storage_consumes_entire_buffer<Client: E2EBackend>( | ||
mut client: Client, | ||
) -> E2EResult<()> { | ||
// given | ||
let mut constructor = ContractStorageRef::new(); | ||
let contract = client | ||
.instantiate("contract-storage", &ink_e2e::alice(), &mut constructor) | ||
.submit() | ||
.await | ||
.expect("instantiate failed"); | ||
let call = contract.call::<ContractStorage>(); | ||
|
||
// when | ||
let result = client | ||
.call( | ||
&ink_e2e::alice(), | ||
&call.set_and_get_storage_all_data_consumed(), | ||
) | ||
.submit() | ||
.await | ||
.expect("Calling `insert_balance` failed") | ||
.return_value(); | ||
|
||
assert!(result.is_ok()); | ||
|
||
Ok(()) | ||
} | ||
|
||
#[ink_e2e::test] | ||
async fn get_contract_storage_fails_when_extra_data<Client: E2EBackend>( | ||
mut client: Client, | ||
) -> E2EResult<()> { | ||
// given | ||
let mut constructor = ContractStorageRef::new(); | ||
let contract = client | ||
.instantiate("contract-storage", &ink_e2e::alice(), &mut constructor) | ||
.submit() | ||
.await | ||
.expect("instantiate failed"); | ||
let call = contract.call::<ContractStorage>(); | ||
|
||
// when | ||
let result = client | ||
.call( | ||
&ink_e2e::alice(), | ||
&call.set_and_get_storage_partial_data_consumed(), | ||
) | ||
.submit() | ||
.await; | ||
|
||
assert!( | ||
result.is_err(), | ||
"Expected the contract to revert when only partially consuming the buffer" | ||
); | ||
|
||
Ok(()) | ||
} | ||
|
||
#[ink_e2e::test] | ||
async fn take_contract_storage_consumes_entire_buffer<Client: E2EBackend>( | ||
mut client: Client, | ||
) -> E2EResult<()> { | ||
// given | ||
let mut constructor = ContractStorageRef::new(); | ||
let contract = client | ||
.instantiate("contract-storage", &ink_e2e::alice(), &mut constructor) | ||
.submit() | ||
.await | ||
.expect("instantiate failed"); | ||
let call = contract.call::<ContractStorage>(); | ||
|
||
// when | ||
let result = client | ||
.call( | ||
&ink_e2e::alice(), | ||
&call.set_and_take_storage_all_data_consumed(), | ||
) | ||
.submit() | ||
.await | ||
.expect("Calling `insert_balance` failed") | ||
.return_value(); | ||
|
||
assert!(result.is_ok()); | ||
|
||
Ok(()) | ||
} | ||
|
||
#[ink_e2e::test] | ||
async fn take_contract_storage_fails_when_extra_data<Client: E2EBackend>( | ||
mut client: Client, | ||
) -> E2EResult<()> { | ||
// given | ||
let mut constructor = ContractStorageRef::new(); | ||
let contract = client | ||
.instantiate("contract-storage", &ink_e2e::alice(), &mut constructor) | ||
.submit() | ||
.await | ||
.expect("instantiate failed"); | ||
let call = contract.call::<ContractStorage>(); | ||
|
||
// when | ||
let result = client | ||
.call( | ||
&ink_e2e::alice(), | ||
&call.set_and_take_storage_partial_data_consumed(), | ||
) | ||
.submit() | ||
.await; | ||
|
||
assert!( | ||
result.is_err(), | ||
"Expected the contract to revert when only partially consuming the buffer" | ||
); | ||
|
||
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.
Can't you just import the full path?
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.
There was a type alias here
type Result = core::result::Result<(), Error>;
. However I've just removed that and replaced its usages with the explicit type.