-
Notifications
You must be signed in to change notification settings - Fork 14
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
Mark transaction as tainted #5310
base: main
Are you sure you want to change the base?
Conversation
- Extended DepositChannelDetails with owner field - Added extrinsic to mark transaction as tainted - Handling deposit and save details for a refund if tx is tainted
- Added tests to verify that tainted transactions can get detected for all possible swap types - Added tests to check that txs marked by other brokers are getting ignored
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #5310 +/- ##
======================================
- Coverage 71% 71% -0%
======================================
Files 490 491 +1
Lines 85209 85327 +118
Branches 85209 85327 +118
======================================
+ Hits 60659 60682 +23
- Misses 21839 21922 +83
- Partials 2711 2723 +12 ☔ View full report in Codecov by Sentry. |
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.
Some early comments.
<T::TargetChain as Chain>::DepositDetails, | ||
TaintedTransactionDetails<T::AccountId>, |
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.
Consider what happens if same tx is reported twice by different parties.
I think we might need a double-map.
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 point
- Taking refund address if we open a deposit channel as lp - Extended ChannelAction to take an optional refund address - Removed all dependencies regarding the LpRegistration trait (added last commit) - Refactored tests, benchmarks, etc
Still working on the benchmark. It's written, but there is something odd with the way we generate the weights. |
state-chain/traits/src/lib.rs
Outdated
@@ -724,6 +724,7 @@ pub trait DepositApi<C: Chain> { | |||
lp_account: Self::AccountId, | |||
source_asset: C::ChainAsset, | |||
boost_fee: BasisPoints, | |||
refund_address: Option<ForeignChainAddress>, |
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.
This should not be optional.
If the LP hasn't registered a refund address, they can't open a channel, so we should always have this.
let is_lp = | ||
T::AccountRoleRegistry::has_account_role(&account_id, AccountRole::LiquidityProvider); | ||
ensure!(is_broker || is_lp, BadOrigin); |
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.
Actually I'm not sure we want/need LPs to mark transactions as tainted. We need their deposit address in case a tx is marked, but in general they won't be marking their own.
We will probably add another account role or similar for the 'reporter' role, but that can be in a future PR. For now we can remove the LP here.
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'd also suggest unwrapping the origin and doing permission checks in the extrinsic rather than in this inner
method. Might make testing/benchmarking easier.
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.
"Inner" was a more temporary solution since we only wanted to support BTC for now and that would open a whole new number of problems with tests and benchmarks. Since I think it could make sense to support at least ETH as well, we can do this properly and the inner function warp can disappear. In general, I think we should try to benchmark as close as possible to be on the safe side.
broker: account_id.clone(), | ||
refund_address: None, | ||
deposit_witness: None, | ||
expires_at: <frame_system::Pallet<T>>::block_number() | ||
.saturating_add(BlockNumberFor::<T>::from(TAINTED_TX_EXPIRATION_BLOCKS)), | ||
marked_for_refund: false, |
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 it would be clearer/safer/more efficient if we split this into two storage items:
// in a map of AccountId -> TxId -> TaintedTransactionDetails
struct TaintedTransactionDetails {
// I don't think we need this, it's already in the key of the map:
// reporter: AccountId,
expires_at: BlockNumber,
}
// in a value of Vec<RejectionDetails>
struct RejectionDetails {
refund_address: Address,
deposit_witness: DepositWitness,
}
This avoids having to deal with the Option
s and filter against marked_for_refund
etc.
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 DepositWitness tbh. Definitely not for BTC (tx_id is enough here) and for ETH we need other stuff I (refund_address, asset, amount). Because we need to pass this information to the API call, I think we're going to end up to define a new type for that anyway. I would go for now with something static that works for ETH/BTC and extend the type as we go or make generic for the chain.
Also please remember to rebase against main to pick up the new compiler version. |
#[derive(RuntimeDebug, PartialEq, Eq, Encode, Decode, TypeInfo, CloneNoBound)] | ||
#[scale_info(skip_type_params(T, I))] | ||
pub struct TaintedTransactionDetails<T: Config<I>, I: 'static> { | ||
pub refund_address: Option<ForeignChainAddress>, |
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.
Until refund parameters are optional, this has to stay optional. We can change it after this is done.
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.
But we can't refund anything if there is no refund address?
Doesn't it make more sense to make it required here, and handle the case where we don't have it before storing this value?
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 believe it doesn't make any difference other than having to deal with less complex code if we go with the option variant. In the point of the swap flow where we have (or have not) the refund address, it's already too late to do anything else than saving what we have and exit the swap logic. What do you want to save if, instead, if there is just no address? The solution is to make the refund parameters a requirement, so this can not happen, but since this is not done, yet we go with an option here and replace it after @msgmaxim has solved this in his PR.
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.
Some minor nits but I think it's nearly ready.
Don't worry about the version bump CI check - should go away when #5327 is merged.
for (account, tx_id, expire_block) in TaintedTransactions::<T, I>::iter() { | ||
if expire_block <= now { |
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 like having to iterate through all of the TaintedTransactions just to see if any have expired (99% of the time this will return nothing). It would be good to think of a better way to do this.
let channel_owner = deposit_channel_details.owner.clone(); | ||
|
||
if TaintedTransactions::<T, I>::take(channel_owner.clone(), deposit_details.clone()) |
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.
let channel_owner = deposit_channel_details.owner.clone(); | |
if TaintedTransactions::<T, I>::take(channel_owner.clone(), deposit_details.clone()) | |
if TaintedTransactions::<T, I>::take(&channel_owner, &deposit_details) |
#[derive(RuntimeDebug, PartialEq, Eq, Encode, Decode, TypeInfo, CloneNoBound)] | ||
#[scale_info(skip_type_params(T, I))] | ||
pub struct TaintedTransactionDetails<T: Config<I>, I: 'static> { | ||
pub refund_address: Option<ForeignChainAddress>, |
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.
But we can't refund anything if there is no refund address?
Doesn't it make more sense to make it required here, and handle the case where we don't have it before storing this value?
_, | ||
Identity, | ||
T::AccountId, | ||
Twox64Concat, |
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.
Twox64Concat, | |
Blake2_128Concat, |
DepositDetails can be freely set by the caller, so we need to be defensive here.
for (account, channel_details) in old::DepositChannelLookup::<T, I>::drain() { | ||
let dummy_account = T::AccountId::decode(&mut &[0u8; 32][..]).unwrap(); |
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.
Prefer to use translate rather than drain+insert. According to the docs, inserting during iteration can cause unwanted side effects.
Pull Request
Closes: PRO-1664
Checklist
Please conduct a thorough self-review before opening the PR.
Summary
Non-Breaking changes
If this PR includes non-breaking changes, select the
non-breaking
label. On merge, CI will automatically cherry-pick the commit to a PR against the release branch.