Skip to content
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

Fix #387 - Generalize the Event and Error of tasks by separating execution from triggering and rescheduling #383

Merged
merged 42 commits into from
Aug 3, 2023

Conversation

imstar15
Copy link
Member

@imstar15 imstar15 commented Jul 13, 2023

Fix the issue of auto compound task:

  1. When there’s not enough fee to pay for this execution, fail it with InsufficientFee error but do not stop the recurring tasks. At the next execution, we check it again to see if there’s enough fee for execution.

  2. The task will stop when there’s no staking delegation within the user’s wallet. That means the user has unstaked all from collators.

Changes in pallet-parachain-staking:
https://github.com/OAK-Foundation/moonbeam/pulls

@imstar15 imstar15 changed the title [WIP]Fix auto compound task Fix auto compound task Jul 14, 2023
@imstar15 imstar15 requested review from chrisli30 and v9n July 14, 2023 15:44
@imstar15 imstar15 marked this pull request as ready for review July 14, 2023 15:44
pallets/automation-time/src/tests.rs Outdated Show resolved Hide resolved
pallets/automation-time/src/tests.rs Outdated Show resolved Hide resolved
pallets/automation-time/src/tests.rs Show resolved Hide resolved
let e = sp_runtime::DispatchErrorWithPostInfo::from(Error::<T>::DelegationNonFound);
Self::deposit_event(Event::AutoCompoundDelegatorStakeFailed {
task_id,
error_message: Into::<&str>::into(e).as_bytes().to_vec(),
Copy link
Member

Choose a reason for hiding this comment

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

is it safe to including the entire error message? will they ever grow big? i think unlikely but maybe we should cap length

Copy link
Member

Choose a reason for hiding this comment

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

Discussed with Charlse. In this line of code, error_message doesn’t contain more info than the error so we need to pass in variables to make it more meaningful. Let’s not bother the length for now. We can prune them later.

@v9n, there are two ways to go with this error_message,
First, we can add full information here, e.g. "delegator:<delegator_wallet_address>. collator: <collator_wallet_address>", however, the two wallet information can be inferred and don’t really needed in this case, because delegator is the wallet who created this task and collator is specified during task creation.

Adding the full error_message will help live debugging, but as you said will increase output and maybe blockchain data size. What do you recommend, should we log the full message or leave it blank? Could leaving it blank really save block space (for example, maybe the size of the event is decided by its largest parameter)?

Copy link
Member

Choose a reason for hiding this comment

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

yes, the log message absolutely userful when debugging.

I don't think log/event is saving into block space(at least in EVM they are stored separaly because log doesn't require consensus).

Let emit the full message IMHO. I was just want to make sure we don't accidently store MB of data due to generated string.

Copy link
Member

Choose a reason for hiding this comment

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

@imstar15, as Vinh said, let’s add the variables to error_message in the below format, and update the tests accordingly.
"delegator:<delegator_wallet_address>, collator: <collator_wallet_address>"

Copy link
Member

@chrisli30 chrisli30 left a comment

Choose a reason for hiding this comment

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

Comments for the new test cases are clear, 👍 however, there are a few more comments and questions.

Besides, we need to update the Documentation regarding this API on Docs website, https://docs.oak.tech/docs/yield-boost-auto-restaking/. @imstar15 please work on the documentation after this PR is approved and merged.

  1. The skip logic needs to be added to the page
  2. Error code and messages need to be listed on the page.

pallets/automation-time/src/lib.rs Outdated Show resolved Hide resolved
pallets/automation-time/src/tests.rs Show resolved Hide resolved
pallets/automation-time/src/tests.rs Show resolved Hide resolved
@imstar15 imstar15 requested review from v9n and chrisli30 July 18, 2023 12:07
… include details about both the delegator and collator.
ArithmeticError, DispatchError, Perbill,
};
use sp_std::{boxed::Box, vec, vec::Vec};
pub use weights::WeightInfo;
use xcm::{latest::prelude::*, VersionedMultiLocation};

#[macro_export]
macro_rules! ERROR_MESSAGE_DELEGATION_FORMAT {
Copy link
Member Author

Choose a reason for hiding this comment

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

In Rust, the format! macro requires the formatting string to be a string literal. We cannot use constants or static variables directly as the formatting string. To address this, we can use a macro to extract the formatting string.

Copy link
Member

Choose a reason for hiding this comment

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

I don’t think we need an extra hex library to encode the wallets. Can you write down the actual message of the error_message here? Is the wallet address still human readable after hex encoding?

On second thought, if we were to add more details, we should always print out delegator and collator for all AutoCompoundDelegatorStake events in a way that is easier to parse.

For example,
AutoCompoundDelegatorStakeSucceeded

  • taskId
  • amount
  • collator

AutoCompoundDelegatorStakeSkipped

  • taskId
  • amount
  • collator
  • message: Unable to pay fees for the next execution
  • value:

The balance reason needs an explanation, as in what was the balance of the wallet, so we add that information by adding an extra field to this event. The challenge is to find a common method to add extra information to a message, or an error. Let me ask round to find out the best practice to extend the Error definition. @v9n any recommendations? We need to standardize the format of Success and Error events.

AutoCompoundDelegatorStakeFailed

  • taskId
  • amount
  • collator
  • error: DelegationNotFound

Since there’s already a collator information included, we don’t need any extra fields for this AutoCompoundDelegatorStakeFailed error

Copy link
Member Author

@imstar15 imstar15 Jul 20, 2023

Choose a reason for hiding this comment

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

After undergoing hex processing, it will become an account ID string.
For example: 0x3830ef2fdb02d711afe91c830737375e32ac9904886266897f361b5a2ec13a39

Copy link
Member

Choose a reason for hiding this comment

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

is there a reason we cannot use the wallet address of delegator or collator but have to hexing them? because hexing them make our original purpose of easy debugging become harder. hexing is one way so cannot easy reverse without a utility convert function somewhere.

also, If it's hard/inconvenience to format into the string maybe we can just return a Vec such as

deposit_event(FailedEvent {
  delegator: Vec<u8> address,
  collator: ...,
  error_mesage: ...
  error: ...
}

This make it easiy to parse on the client when indexing event, but make it hard to add more arbitrary data because we need to change the event shape. with the error_mesage as string/Vec it was easy to put add/any info down the life.

Copy link
Member

Choose a reason for hiding this comment

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

Yes, wallet addresses need to be printed out, but not hex or accountId. @imstar15

Copy link
Member

@chrisli30 chrisli30 left a comment

Choose a reason for hiding this comment

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

The macro_export approach looks a little clumsy to me, so we need to find a better solution for Events and Error.

ArithmeticError, DispatchError, Perbill,
};
use sp_std::{boxed::Box, vec, vec::Vec};
pub use weights::WeightInfo;
use xcm::{latest::prelude::*, VersionedMultiLocation};

#[macro_export]
macro_rules! ERROR_MESSAGE_DELEGATION_FORMAT {
Copy link
Member

Choose a reason for hiding this comment

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

I don’t think we need an extra hex library to encode the wallets. Can you write down the actual message of the error_message here? Is the wallet address still human readable after hex encoding?

On second thought, if we were to add more details, we should always print out delegator and collator for all AutoCompoundDelegatorStake events in a way that is easier to parse.

For example,
AutoCompoundDelegatorStakeSucceeded

  • taskId
  • amount
  • collator

AutoCompoundDelegatorStakeSkipped

  • taskId
  • amount
  • collator
  • message: Unable to pay fees for the next execution
  • value:

The balance reason needs an explanation, as in what was the balance of the wallet, so we add that information by adding an extra field to this event. The challenge is to find a common method to add extra information to a message, or an error. Let me ask round to find out the best practice to extend the Error definition. @v9n any recommendations? We need to standardize the format of Success and Error events.

AutoCompoundDelegatorStakeFailed

  • taskId
  • amount
  • collator
  • error: DelegationNotFound

Since there’s already a collator information included, we don’t need any extra fields for this AutoCompoundDelegatorStakeFailed error

@v9n
Copy link
Member

v9n commented Jul 20, 2023

The macro_export approach looks a little clumsy to me, so we need to find a better solution for Events and Error.

@chrisli30 @imstar15 just share my though here. Instead of a String, we can may build the string with Vector?

Example:

 //  'delegator'
const DELEGATOR_MSG = [100, 101, 108, 101, 103, 97, 116, 111, 114];;
 // collator
const COLLATOR_MSG = [32, 99, 111, 108, 108, 97, 116, 111, 114];

fn build_error_message(collator: vec, delagator vec) -> Vec<u8> {
  let s = DELEGATOR_MSG.to_vec(),
  s.extend(delegator);
  s.extend(COLLATOR_MSG.to_vec()); 
  s.extend(collator);
  s
}

I'm not sure if it's more clean but at least we don't use macro, we can also add any thing there and don't need to rely on format! std. We can also limit the length here for build_error_message as well etc.

@imstar15 imstar15 requested a review from chrisli30 July 31, 2023 15:13
pallets/automation-time/src/tests.rs Show resolved Hide resolved
pallets/automation-time/src/tests.rs Outdated Show resolved Hide resolved
pallets/automation-time/src/tests.rs Show resolved Hide resolved
pallets/automation-time/src/tests.rs Show resolved Hide resolved
pallets/automation-time/src/tests.rs Show resolved Hide resolved
pallets/automation-time/src/tests.rs Show resolved Hide resolved
pallets/automation-time/src/tests.rs Show resolved Hide resolved
pallets/automation-time/src/tests.rs Outdated Show resolved Hide resolved
pallets/automation-time/src/tests.rs Outdated Show resolved Hide resolved
pallets/automation-time/src/tests.rs Show resolved Hide resolved
@imstar15 imstar15 requested a review from chrisli30 August 1, 2023 16:51
@@ -324,9 +327,6 @@ benchmarks! {
let call: <T as Config>::Call = frame_system::Call::remark { remark: vec![] }.into();
let encoded_call = call.encode();
}: { AutomationTime::<T>::run_dynamic_dispatch_action(caller.clone(), encoded_call, task_id.clone()) }
verify {
Copy link
Member

Choose a reason for hiding this comment

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

I think we should still keep this verify block. i had a similar code in here we can use to get the right task id

https://github.com/OAK-Foundation/OAK-blockchain/blob/917cb56330ece0ac6fc8c5b79a804ac04ec39e8a/pallets/automation-time/src/benchmarking.rs#L221-L227

Copy link
Member Author

Choose a reason for hiding this comment

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

Because run_dynamic_dispatch_action does not throw events, it only returns error. Therefore, I checked the error it returns. Since error variable cannot be used in the verify block, I did not include these checks in the verify block

Refers to:
https://github.com/paritytech/substrate/blob/fbddfbd76c60c6fda0024e8a44e82ad776033e4b/frame/state-trie-migration/src/lib.rs#L964

.

Copy link
Member

Choose a reason for hiding this comment

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

ic, that makes sense.

condition: BTreeMap<String, String>,
encoded_call: Vec<u8>,
/// List of errors causing task abortion.
abort_errors: Vec<String>,
Copy link
Member

Choose a reason for hiding this comment

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

Per OIP #387 I don't think we intent to include abort_errors on TaskTriggered @chrisli30 ? It is redundant because it is the same array we clone from task.abort_errors.

My understanding is when a task execution failed, we compared the error it throw with a pre-defined list(or maybe user input in the future) in abort_errors, if matched, we stop re-schedule for the next run. so abort_error should be a single error of the particular error that match one of error in the task.abort_errors, and will be include in TaskNotSchedule/TaskFailedSchedule ?

Copy link
Member

Choose a reason for hiding this comment

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

Good call,

TaskTriggered doesn’t need this array cuz it’s too noisy.

Neither does TaskNotRescheduled need it, because it already contains the actual error that caused the NotRescheduled event. We can always look up the task creation event to verify that the causing error is in the abort_errors array.

TaskRescheduleFailed doesn’t need this because that error is most likely to be caused by block full and not relate to abort_errors.

Copy link
Member Author

Choose a reason for hiding this comment

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

Fixed

@@ -1483,7 +1533,7 @@ pub mod pallet {
match task.schedule {
Schedule::Fixed { .. } =>
Self::decrement_task_and_remove_if_complete(task_id, task),
Schedule::Recurring { .. } => Self::reschedule_or_remove_task(task, error),
Schedule::Recurring { .. } => Self::reschedule_or_remove_task(task_id, task, error),
Copy link
Member

Choose a reason for hiding this comment

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

is there a reason we need to pass task_id and task separately? inside the task we already had the task_id.

Copy link
Member Author

Choose a reason for hiding this comment

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

Good call!
Fixed.

who: AccountOf<T>,
task_id: TaskIdV2,
result: DispatchResult,
},
/// The call for the DynamicDispatch action can no longer be decoded.
Copy link
Member

Choose a reason for hiding this comment

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

We don’t need this CallCannotBeDecoded event.

When CallCannotBeDecoded error happens, the below two Events need to capture it

  1. TaskExecutionFailed
  2. TaskNotRescheduled.

Copy link
Member Author

Choose a reason for hiding this comment

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

Fixed

Copy link
Member

@chrisli30 chrisli30 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 enough to me.

pallets/automation-time/src/mock.rs Outdated Show resolved Hide resolved
}

run_dynamic_dispatch_action_fail_decode {
let caller: T::AccountId = account("caller", 0, SEED);
let task_id = vec![49, 45, 48, 45, 52];

let bad_encoded_call: Vec<u8> = vec![1];
}: { AutomationTime::<T>::run_dynamic_dispatch_action(caller.clone(), bad_encoded_call, task_id.clone()) }
verify {
assert_last_event::<T>(Event::CallCannotBeDecoded{ who: caller, task_id: task_id.clone() }.into())
Copy link
Member

Choose a reason for hiding this comment

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

Maybe I’m wrong, but Is this assert_last_event util function recently added by @v9n? Should we keep or remote the function?

Copy link
Member

Choose a reason for hiding this comment

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

the util function already removed in this PR. It was being used only in benchmark file. but with the change in this PR, we move away from events -> return dispatch error so now we don't need to assert the event anymore.

I still think it's useful to keep the function around but at the same time it's one liner and currently we don't use it at al anywhere so I think ok to remove for now and in the future if we ever has that need we can just whipe up it quickly.

#[cfg(any(feature = "std", feature = "runtime-benchmarks", test))]
pub fn assert_last_event(event: RuntimeEvent) {
	assert_eq!(events().last().expect("events expected"), &event);
}

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, we don't need it now.

If left there, a warning will be generated. Perhaps this warning can be removed through some annotations.

Copy link
Member Author

@imstar15 imstar15 Aug 3, 2023

Choose a reason for hiding this comment

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

I revert assert_last_event function and add #[allow(dead_code)] annotation on it

@imstar15 imstar15 merged commit 1f62244 into master Aug 3, 2023
3 checks passed
@imstar15 imstar15 deleted the fix-auto-compound-task branch August 3, 2023 09:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants