Skip to content
This repository has been archived by the owner on Nov 15, 2023. It is now read-only.

Add extrinsic to improve position in a bag of bags-list #9829

Merged
merged 42 commits into from
Dec 8, 2021

Conversation

emostov
Copy link
Contributor

@emostov emostov commented Sep 21, 2021

This PR adds the extrinsic put_in_front_of which allows the user to specify a lighter and heavier account within the same bag and the extrinsic will move heavier directly in front of lighter. The parameter names refer to the fact that lighter must have a lower VoteWeight then heavier.

In the ideal use case, where a user wants to improve the position of their account within a bag, the user would iterate the bag, starting from the head, and find the first node who's VoteWeight is less than theirs. They would then supply the id of the node as the lighter argument and their own id as the heavier argument.

polkadot companion: paritytech/polkadot#3899


Below is a table with some combinations of edge case and the name of the relevant unit test

heavier head heavier tail heavier non-terminal (in front) heavier non-terminal (behind)
lighter head NA put_in_front_of_heavier_is_tail_lighter_is_head NA put_in_front_of_lighter_is_head_heavier_is_non_terminal
lighter tail put_in_front_of_lighter_is_tail_heavier_is_head NA NA put_in_front_of_lighter_is_tail_heavier_is_not_terminal
lighter non-terminal (in front) NA put_in_front_of_heavier_is_tail_lighter_is_non_terminal NA put_in_front_of_non_terminal_nodes_heavier_behind
lighter non-terminal (behind) put_in_front_of_heavier_is_head_lighter_is_not_terminal NA put_in_front_of_non_terminal_nodes_heavier_in_front NA

TODO

  • if this is permissioned, investigate a reasonable way for allowing the stash to call this - perhaps either via proxy or the controller.

skip check-dependent-cumulus

This PR adds the extrinsic `put_in_front_of` which allows the user to specify a `lighter` and `heavier` account within the same bag. The extrinsic will move `heavier` directly in front of `lighter`.  The parameter names refer to the fact that `lighter` must have a lower `VoteWeight` then `heavier`. 

In the ideal use case, where a user wants to improve the position of their account within a bag, the user would iterate the bag, starting from the head, and find the first node who's `VoteWeight` is less than theirs. They would then supply the `id` of the node as the `lighter` argument and their own `id` as the `heavier` argument.
@emostov emostov added A0-please_review Pull request needs code review. D9-needsaudit 👮 PR contains changes to fund-managing logic that should be properly reviewed and externally audited B7-runtimenoteworthy C1-low PR touches the given topic and has a low impact on builders. labels Sep 21, 2021
@emostov
Copy link
Contributor Author

emostov commented Sep 21, 2021

/benchmark runtime pallet pallet_bags_list

ensure!(weight_of(&heavier_id) > weight_of(&lighter_id), pallet::Error::NotHeavier);

// we need to write the bag to storage if its head and/or tail is updated.
if lighter_node.is_terminal() || heavier_node.is_terminal() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here you are assuming that the lighter node could only ever be a head, and the heavier node could only ever be a tail, while I don't see that being guaranteed. Can you elaborate all the edge cases that you considered, and why you think this is enough?

Copy link
Contributor

@kianenigma kianenigma Sep 22, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

here's an idea to break this down into smaller bite-size pieces:

  1. For removing, we simply use the current fn remove(). After all the checks, we immediately remove heavier. The logic for this function should already update the Bag head/tail if heavier.is_terminal().
  2. For inserting, we write a private helper function as such:
fn insert_at(after: Option<Node>) {
	if let Some(x) = after {
		// new logic, insert a new node on the prev of `after`  
	} else {
		// insert at tail: delegate to whatever we do now
	}
}

This will require the least amount of added logic, which helps with verifying the correctness of the PR (assuming that none of the old code was buggy).


If we keep the current code, here's a mental model to check it

  1. none are terminal
  2. lighter is head, heavier is not terminal (common case)
  3. lighter is tail, heavier is not terminal (mistake case)
  4. heavier is tail, lighter is not terminal (common case)
  5. heavier is head, lighter is not terminal (mistake case)
  6. lighter is head, heavier is tail (common case)
  7. lighter is tail, heavier is head (mistake case)

and for any case that contains a non-terminal, we have to consider if they are adjacent or not (in case 5 it totally makes a different).

I'm going to now start thinking about each case and how it is handled.

Copy link
Contributor

@kianenigma kianenigma Sep 22, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My analysis shows case 5 is not handled correctly, when the non-terminal lighter is not adjacent to the heavier one.

#[test]
fn heavier_is_head_lighter_is_not_terminal() {
	ExtBuilder::default().add_ids(vec![(5, 1000)]).build_and_execute(|| {
		// given
		assert_eq!(List::<Runtime>::get_bags(), vec![(10, vec![1]), (1_000, vec![2, 3, 4, 5])]);
		// when
		BagsList::put_in_front_of(Origin::signed(0), 2, 4).unwrap();
		// then.. :(
		assert_eq!(List::<Runtime>::get_bags(), vec![(10, vec![1]), (1_000, vec![3, 2, 4, 5])]);
	});
}

I'd like to see the fix, but my gut feeling is that rewriting it using smaller functions is easier. This if conditions_in_which_we_might_update_bags {} is a bit hard to verify. If we first remove, then update everything, and then insert, and update everything again, there would probably be a few extra (IMO negligible) lookups to the overlay, but the (non-negligible) db access would be the same.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor

@kianenigma kianenigma left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might have some unconsidered edge cases here. https://github.com/paritytech/substrate/pull/9829/files#r714053661

@emostov
Copy link
Contributor Author

emostov commented Sep 23, 2021

Might have some unconsidered edge cases here. https://github.com/paritytech/substrate/pull/9829/files#r714053661

yup, definitely missed some. To try and be thorough I created this table for edge cases and filled it with the names of relevant unit tests

heavier head heavier tail heavier non-terminal (in front) heavier non-terminal (behind)
lighter head NA put_in_front_of_heavier_is_tail_lighter_is_head NA put_in_front_of_lighter_is_head_heavier_is_non_terminal
lighter tail put_in_front_of_lighter_is_tail_heavier_is_head NA NA put_in_front_of_lighter_is_tail_heavier_is_not_terminal
lighter non-terminal (in front) NA put_in_front_of_heavier_is_tail_lighter_is_non_terminal NA put_in_front_of_non_terminal_nodes_heavier_behind
lighter non-terminal (behind) put_in_front_of_heavier_is_head_lighter_is_not_terminal NA put_in_front_of_non_terminal_nodes_heavier_in_front NA

Copy link
Contributor

@thiolliere thiolliere left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks good to me, minor grumble above, but they should better be addressed IMO.
Also #9829 (comment)

Runtime automation moved this from Please Review to Needs Audit Dec 3, 2021
@kianenigma kianenigma added E6-transactionversion and removed A0-please_review Pull request needs code review. labels Dec 3, 2021
Copy link
Contributor

@thiolliere thiolliere left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

all good to me, we can merge

@emostov
Copy link
Contributor Author

emostov commented Dec 7, 2021

bot merge

@paritytech-processbot
Copy link

Error: It's not possible to meet the minimal approval count of 1 in paritytech/polkadot#3899

@emostov
Copy link
Contributor Author

emostov commented Dec 8, 2021

bot merge

@paritytech-processbot
Copy link

Error: It's not possible to meet the minimal approval count of 1 in paritytech/polkadot#3899

@emostov
Copy link
Contributor Author

emostov commented Dec 8, 2021

Error: It's not possible to meet the minimal approval count of 1 in paritytech/polkadot#3899

Not sure whats up here, @thiolliere already approved the companion

@shawntabrizi
Copy link
Member

bot merge

@paritytech-processbot paritytech-processbot bot merged commit c9cad66 into master Dec 8, 2021
@paritytech-processbot paritytech-processbot bot deleted the zeke-change-bag-position branch December 8, 2021 04:35
Runtime automation moved this from Needs Audit to Done Dec 8, 2021
@shawntabrizi shawntabrizi moved this from Done to Archive in Runtime Dec 9, 2021
seunlanlege pushed a commit to seunlanlege/substrate that referenced this pull request Dec 17, 2021
)

* pallet-bags-list: Add `put_in_front_of` extrinsic

This PR adds the extrinsic `put_in_front_of` which allows the user to specify a `lighter` and `heavier` account within the same bag. The extrinsic will move `heavier` directly in front of `lighter`.  The parameter names refer to the fact that `lighter` must have a lower `VoteWeight` then `heavier`. 

In the ideal use case, where a user wants to improve the position of their account within a bag, the user would iterate the bag, starting from the head, and find the first node who's `VoteWeight` is less than theirs. They would then supply the `id` of the node as the `lighter` argument and their own `id` as the `heavier` argument.

* Test & Benchmarks

* Respect line width

* Remove List::put_in_fron_of tests; Remove AlreadyHigher error

* The dispatch origin for this call must be ...

* Add some periods

* Add back test to list module: put_in_front_of_exits_early_if_bag_not_found

* add test tests::pallet::heavier_is_head_lighter_is_not_terminal

* Cater for edge case of heavier being head

* Add ExtBuilder::add_aux_data; try to make some tests use simpler data

* Update frame/bags-list/src/list/tests.rs

* make insert_at_unchecked infallible

* Make it permissioned - only callable by heavier

* Add test cases for insert_at_unchecked

* Move counter update to insert_at; fix comments

* Address some feedback

* Make voteweight constructed with parameter_types

* Always set vote weight for Ids in build

* Add skip_genesis_ids

* Do not pass weight fn to List put_in_front_of

* Remove remants of CounterForListNodes

* fmt

* cargo run --quiet --release --features=runtime-benchmarks --manifest-path=bin/node/cli/Cargo.toml -- benchmark --chain=dev --steps=50 --repeat=20 --pallet=pallet_bags_list --extrinsic=* --execution=wasm --wasm-execution=compiled --heap-pages=4096 --output=./frame/bags-list/src/weights.rs --template=./.maintain/frame-weight-template.hbs

* Delete messed up weights file

* Some place holder stuff so we can run bench in CI

* Fix

* cargo run --quiet --release --features=runtime-benchmarks --manifest-path=bin/node/cli/Cargo.toml -- benchmark --chain=dev --steps=50 --repeat=20 --pallet=pallet_bags_list --extrinsic=* --execution=wasm --wasm-execution=compiled --heap-pages=4096 --output=./frame/bags-list/src/weights.rs --template=./.maintain/frame-weight-template.hbs

* Update frame/bags-list/src/list/mod.rs

Co-authored-by: Guillaume Thiolliere <gui.thiolliere@gmail.com>

* fmt

* Log + debug assert when refetching an Id

Co-authored-by: Parity Bot <admin@parity.io>
Co-authored-by: Guillaume Thiolliere <gui.thiolliere@gmail.com>
grishasobol pushed a commit to gear-tech/substrate that referenced this pull request Mar 28, 2022
)

* pallet-bags-list: Add `put_in_front_of` extrinsic

This PR adds the extrinsic `put_in_front_of` which allows the user to specify a `lighter` and `heavier` account within the same bag. The extrinsic will move `heavier` directly in front of `lighter`.  The parameter names refer to the fact that `lighter` must have a lower `VoteWeight` then `heavier`. 

In the ideal use case, where a user wants to improve the position of their account within a bag, the user would iterate the bag, starting from the head, and find the first node who's `VoteWeight` is less than theirs. They would then supply the `id` of the node as the `lighter` argument and their own `id` as the `heavier` argument.

* Test & Benchmarks

* Respect line width

* Remove List::put_in_fron_of tests; Remove AlreadyHigher error

* The dispatch origin for this call must be ...

* Add some periods

* Add back test to list module: put_in_front_of_exits_early_if_bag_not_found

* add test tests::pallet::heavier_is_head_lighter_is_not_terminal

* Cater for edge case of heavier being head

* Add ExtBuilder::add_aux_data; try to make some tests use simpler data

* Update frame/bags-list/src/list/tests.rs

* make insert_at_unchecked infallible

* Make it permissioned - only callable by heavier

* Add test cases for insert_at_unchecked

* Move counter update to insert_at; fix comments

* Address some feedback

* Make voteweight constructed with parameter_types

* Always set vote weight for Ids in build

* Add skip_genesis_ids

* Do not pass weight fn to List put_in_front_of

* Remove remants of CounterForListNodes

* fmt

* cargo run --quiet --release --features=runtime-benchmarks --manifest-path=bin/node/cli/Cargo.toml -- benchmark --chain=dev --steps=50 --repeat=20 --pallet=pallet_bags_list --extrinsic=* --execution=wasm --wasm-execution=compiled --heap-pages=4096 --output=./frame/bags-list/src/weights.rs --template=./.maintain/frame-weight-template.hbs

* Delete messed up weights file

* Some place holder stuff so we can run bench in CI

* Fix

* cargo run --quiet --release --features=runtime-benchmarks --manifest-path=bin/node/cli/Cargo.toml -- benchmark --chain=dev --steps=50 --repeat=20 --pallet=pallet_bags_list --extrinsic=* --execution=wasm --wasm-execution=compiled --heap-pages=4096 --output=./frame/bags-list/src/weights.rs --template=./.maintain/frame-weight-template.hbs

* Update frame/bags-list/src/list/mod.rs

Co-authored-by: Guillaume Thiolliere <gui.thiolliere@gmail.com>

* fmt

* Log + debug assert when refetching an Id

Co-authored-by: Parity Bot <admin@parity.io>
Co-authored-by: Guillaume Thiolliere <gui.thiolliere@gmail.com>
ark0f pushed a commit to gear-tech/substrate that referenced this pull request Feb 27, 2023
)

* pallet-bags-list: Add `put_in_front_of` extrinsic

This PR adds the extrinsic `put_in_front_of` which allows the user to specify a `lighter` and `heavier` account within the same bag. The extrinsic will move `heavier` directly in front of `lighter`.  The parameter names refer to the fact that `lighter` must have a lower `VoteWeight` then `heavier`. 

In the ideal use case, where a user wants to improve the position of their account within a bag, the user would iterate the bag, starting from the head, and find the first node who's `VoteWeight` is less than theirs. They would then supply the `id` of the node as the `lighter` argument and their own `id` as the `heavier` argument.

* Test & Benchmarks

* Respect line width

* Remove List::put_in_fron_of tests; Remove AlreadyHigher error

* The dispatch origin for this call must be ...

* Add some periods

* Add back test to list module: put_in_front_of_exits_early_if_bag_not_found

* add test tests::pallet::heavier_is_head_lighter_is_not_terminal

* Cater for edge case of heavier being head

* Add ExtBuilder::add_aux_data; try to make some tests use simpler data

* Update frame/bags-list/src/list/tests.rs

* make insert_at_unchecked infallible

* Make it permissioned - only callable by heavier

* Add test cases for insert_at_unchecked

* Move counter update to insert_at; fix comments

* Address some feedback

* Make voteweight constructed with parameter_types

* Always set vote weight for Ids in build

* Add skip_genesis_ids

* Do not pass weight fn to List put_in_front_of

* Remove remants of CounterForListNodes

* fmt

* cargo run --quiet --release --features=runtime-benchmarks --manifest-path=bin/node/cli/Cargo.toml -- benchmark --chain=dev --steps=50 --repeat=20 --pallet=pallet_bags_list --extrinsic=* --execution=wasm --wasm-execution=compiled --heap-pages=4096 --output=./frame/bags-list/src/weights.rs --template=./.maintain/frame-weight-template.hbs

* Delete messed up weights file

* Some place holder stuff so we can run bench in CI

* Fix

* cargo run --quiet --release --features=runtime-benchmarks --manifest-path=bin/node/cli/Cargo.toml -- benchmark --chain=dev --steps=50 --repeat=20 --pallet=pallet_bags_list --extrinsic=* --execution=wasm --wasm-execution=compiled --heap-pages=4096 --output=./frame/bags-list/src/weights.rs --template=./.maintain/frame-weight-template.hbs

* Update frame/bags-list/src/list/mod.rs

Co-authored-by: Guillaume Thiolliere <gui.thiolliere@gmail.com>

* fmt

* Log + debug assert when refetching an Id

Co-authored-by: Parity Bot <admin@parity.io>
Co-authored-by: Guillaume Thiolliere <gui.thiolliere@gmail.com>
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
C1-low PR touches the given topic and has a low impact on builders. D1-audited 👍 PR contains changes to fund-managing logic that has been properly reviewed and externally audited
Projects
Status: Done
Development

Successfully merging this pull request may close these issues.

None yet

5 participants