-
Notifications
You must be signed in to change notification settings - Fork 73
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
Conversation
0e31c86
to
b0e16ec
Compare
pallets/automation-time/src/lib.rs
Outdated
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(), |
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.
is it safe to including the entire error message? will they ever grow big? i think unlikely but maybe we should cap length
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.
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)?
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.
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.
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.
@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>"
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.
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.
- The skip logic needs to be added to the page
- Error code and messages need to be listed on the page.
… include details about both the delegator and collator.
pallets/automation-time/src/lib.rs
Outdated
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 { |
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.
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.
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.
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
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.
After undergoing hex
processing, it will become an account ID string.
For example: 0x3830ef2fdb02d711afe91c830737375e32ac9904886266897f361b5a2ec13a39
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.
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.
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.
Yes, wallet addresses need to be printed out, but not hex or accountId. @imstar15
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.
The macro_export approach looks a little clumsy to me, so we need to find a better solution for Events and Error.
pallets/automation-time/src/lib.rs
Outdated
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 { |
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.
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
@chrisli30 @imstar15 just share my though here. Instead of a String, we can may build the string with Vector? Example:
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 |
@@ -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 { |
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.
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
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.
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
.
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.
ic, that makes sense.
pallets/automation-time/src/lib.rs
Outdated
condition: BTreeMap<String, String>, | ||
encoded_call: Vec<u8>, | ||
/// List of errors causing task abortion. | ||
abort_errors: Vec<String>, |
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.
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 ?
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.
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.
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.
Fixed
pallets/automation-time/src/lib.rs
Outdated
@@ -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), |
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.
is there a reason we need to pass task_id and task separately? inside the task we already had the task_id.
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.
Good call!
Fixed.
pallets/automation-time/src/lib.rs
Outdated
who: AccountOf<T>, | ||
task_id: TaskIdV2, | ||
result: DispatchResult, | ||
}, | ||
/// The call for the DynamicDispatch action can no longer be decoded. |
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.
We don’t need this CallCannotBeDecoded event.
When CallCannotBeDecoded error happens, the below two Events need to capture it
- TaskExecutionFailed
- TaskNotRescheduled.
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.
Fixed
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.
Looks good enough to me.
} | ||
|
||
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()) |
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.
Maybe I’m wrong, but Is this assert_last_event
util function recently added by @v9n? Should we keep or remote the function?
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.
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);
}
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.
Yes, we don't need it now.
If left there, a warning will be generated. Perhaps this warning can be removed through some annotations.
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.
I revert assert_last_event
function and add #[allow(dead_code)]
annotation on it
Fix the issue of auto compound task:
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.
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