git.delta.rocks / unique-network / refs/commits / bebe624eff63

difftreelog

feat prevent ouroboros creation during nest

Yaroslav Bolyukin2022-04-19parent: #07a6969.patch.diff
in: master

11 files changed

modifiedclient/rpc/src/lib.rsdiffbeforeafterboth

no syntactic changes

modifiedpallets/common/src/lib.rsdiffbeforeafterboth
918 fn check_nesting(918 fn check_nesting(
919 &self,919 &self,
920 sender: T::CrossAccountId,920 sender: T::CrossAccountId,
921 from: CollectionId,921 from: (CollectionId, TokenId),
922 under: TokenId,922 under: TokenId,
923 budget: &dyn Budget,923 budget: &dyn Budget,
924 ) -> DispatchResult;924 ) -> DispatchResult;
modifiedpallets/fungible/src/common.rsdiffbeforeafterboth
237 fn check_nesting(237 fn check_nesting(
238 &self,238 &self,
239 _sender: <T>::CrossAccountId,239 _sender: <T>::CrossAccountId,
240 _from: CollectionId,240 _from: (CollectionId, TokenId),
241 _under: TokenId,241 _under: TokenId,
242 _budget: &dyn Budget,242 _budget: &dyn Budget,
243 ) -> sp_runtime::DispatchResult {243 ) -> sp_runtime::DispatchResult {
modifiedpallets/fungible/src/lib.rsdiffbeforeafterboth
226226
227 dispatch.check_nesting(from.clone(), collection.id, target.1, nesting_budget)?;227 dispatch.check_nesting(
228 from.clone(),
229 (collection.id, TokenId::default()),
230 target.1,
231 nesting_budget,
232 )?;
228 }233 }
229234
295300
296 dispatch.check_nesting(sender.clone(), collection.id, target.1, nesting_budget)?;301 dispatch.check_nesting(
302 sender.clone(),
303 (collection.id, TokenId::default()),
304 target.1,
305 nesting_budget,
306 )?;
297 }307 }
298 }308 }
386 if let Some(source) = T::CrossTokenAddressMapping::address_to_token(from) {396 if let Some(source) = T::CrossTokenAddressMapping::address_to_token(from) {
387 // TODO: should collection owner be allowed to perform this transfer?397 // TODO: should collection owner be allowed to perform this transfer?
388 ensure!(398 ensure!(
389 <PalletStructure<T>>::indirectly_owned(399 <PalletStructure<T>>::check_indirectly_owned(
390 spender.clone(),400 spender.clone(),
391 source.0,401 source.0,
392 source.1,402 source.1,
403 None,
393 nesting_budget404 nesting_budget
394 )?,405 )?,
395 <CommonError<T>>::ApprovedValueTooLow,406 <CommonError<T>>::ApprovedValueTooLow,
modifiedpallets/nonfungible/src/common.rsdiffbeforeafterboth
251 fn check_nesting(251 fn check_nesting(
252 &self,252 &self,
253 sender: T::CrossAccountId,253 sender: T::CrossAccountId,
254 from: CollectionId,254 from: (CollectionId, TokenId),
255 under: TokenId,255 under: TokenId,
256 budget: &dyn Budget,256 budget: &dyn Budget,
257 ) -> sp_runtime::DispatchResult {257 ) -> sp_runtime::DispatchResult {
modifiedpallets/nonfungible/src/lib.rsdiffbeforeafterboth
298298
299 dispatch.check_nesting(from.clone(), collection.id, target.1, nesting_budget)?;299 dispatch.check_nesting(
300 from.clone(),
301 (collection.id, token),
302 target.1,
303 nesting_budget,
304 )?;
300 }305 }
301306
381 );386 );
382 }387 }
383388
384 for (to, _) in balances.iter() {389 for (i, data) in data.iter().enumerate() {
390 let token = TokenId(first_token + i as u32 + 1);
385 if let Some(target) = T::CrossTokenAddressMapping::address_to_token(to) {391 if let Some(target) = T::CrossTokenAddressMapping::address_to_token(&data.owner) {
386 let handle = <CollectionHandle<T>>::try_get(target.0)?;392 let handle = <CollectionHandle<T>>::try_get(target.0)?;
387 let dispatch = T::CollectionDispatch::dispatch(handle);393 let dispatch = T::CollectionDispatch::dispatch(handle);
388 let dispatch = dispatch.as_dyn();394 let dispatch = dispatch.as_dyn();
389
390 dispatch.check_nesting(sender.clone(), collection.id, target.1, nesting_budget)?;395 dispatch.check_nesting(
396 sender.clone(),
397 (collection.id, token),
398 target.1,
399 nesting_budget,
400 )?;
391 }401 }
392 }402 }
535 if let Some(source) = T::CrossTokenAddressMapping::address_to_token(from) {545 if let Some(source) = T::CrossTokenAddressMapping::address_to_token(from) {
536 // TODO: should collection owner be allowed to perform this transfer?546 // TODO: should collection owner be allowed to perform this transfer?
537 ensure!(547 ensure!(
538 <PalletStructure<T>>::indirectly_owned(548 <PalletStructure<T>>::check_indirectly_owned(
539 spender.clone(),549 spender.clone(),
540 source.0,550 source.0,
541 source.1,551 source.1,
552 None,
542 nesting_budget553 nesting_budget
543 )?,554 )?,
544 <CommonError<T>>::ApprovedValueTooLow,555 <CommonError<T>>::ApprovedValueTooLow,
610 pub fn check_nesting(621 pub fn check_nesting(
611 handle: &NonfungibleHandle<T>,622 handle: &NonfungibleHandle<T>,
612 sender: T::CrossAccountId,623 sender: T::CrossAccountId,
613 from: CollectionId,624 from: (CollectionId, TokenId),
614 under: TokenId,625 under: TokenId,
615 nesting_budget: &dyn Budget,626 nesting_budget: &dyn Budget,
616 ) -> DispatchResult {627 ) -> DispatchResult {
617 fn ensure_sender_allowed<T: Config>(628 fn ensure_sender_allowed<T: Config>(
618 collection: CollectionId,629 collection: CollectionId,
619 token: TokenId,630 token: TokenId,
631 for_nest: (CollectionId, TokenId),
620 sender: T::CrossAccountId,632 sender: T::CrossAccountId,
621 budget: &dyn Budget,633 budget: &dyn Budget,
622 ) -> DispatchResult {634 ) -> DispatchResult {
623 ensure!(635 ensure!(
624 <PalletStructure<T>>::indirectly_owned(sender, collection, token, budget)?,636 <PalletStructure<T>>::check_indirectly_owned(
637 sender,
638 collection,
639 token,
640 Some(for_nest),
641 budget
642 )?,
625 <CommonError<T>>::OnlyOwnerAllowedToNest,643 <CommonError<T>>::OnlyOwnerAllowedToNest,
628 }646 }
629 match handle.limits.nesting_rule() {647 match handle.limits.nesting_rule() {
630 NestingRule::Disabled => fail!(<CommonError<T>>::NestingIsDisabled),648 NestingRule::Disabled => fail!(<CommonError<T>>::NestingIsDisabled),
631 NestingRule::Owner => ensure_sender_allowed::<T>(handle.id, under, sender, nesting_budget)?,649 NestingRule::Owner => {
650 ensure_sender_allowed::<T>(handle.id, under, from, sender, nesting_budget)?
651 }
632 NestingRule::OwnerRestricted(whitelist) => {652 NestingRule::OwnerRestricted(whitelist) => {
633 ensure!(653 ensure!(
634 whitelist.contains(&from),654 whitelist.contains(&from.0),
635 <CommonError<T>>::SourceCollectionIsNotAllowedToNest655 <CommonError<T>>::SourceCollectionIsNotAllowedToNest
636 );656 );
637 ensure_sender_allowed::<T>(handle.id, under, sender, nesting_budget)?657 ensure_sender_allowed::<T>(handle.id, under, from, sender, nesting_budget)?
638 }658 }
639 }659 }
640 Ok(())660 Ok(())
modifiedpallets/refungible/src/common.rsdiffbeforeafterboth
260 fn check_nesting(260 fn check_nesting(
261 &self,261 &self,
262 _sender: <T>::CrossAccountId,262 _sender: <T>::CrossAccountId,
263 _from: CollectionId,263 _from: (CollectionId, TokenId),
264 _under: TokenId,264 _under: TokenId,
265 _budget: &dyn Budget,265 _budget: &dyn Budget,
266 ) -> sp_runtime::DispatchResult {266 ) -> sp_runtime::DispatchResult {
modifiedpallets/refungible/src/lib.rsdiffbeforeafterboth
354354
355 dispatch.check_nesting(from.clone(), collection.id, target.1, nesting_budget)?;355 dispatch.check_nesting(
356 from.clone(),
357 (collection.id, token),
358 target.1,
359 nesting_budget,
360 )?;
356 }361 }
357362
455 }460 }
456 }461 }
457462
458 for token in data.iter() {463 for (i, token) in data.iter().enumerate() {
464 let token_id = TokenId(first_token_id + i as u32 + 1);
459 for (to, _) in token.users.iter() {465 for (to, _) in token.users.iter() {
460 if let Some(target) = T::CrossTokenAddressMapping::address_to_token(to) {466 if let Some(target) = T::CrossTokenAddressMapping::address_to_token(to) {
461 let handle = <CollectionHandle<T>>::try_get(target.0)?;467 let handle = <CollectionHandle<T>>::try_get(target.0)?;
464470
465 dispatch.check_nesting(471 dispatch.check_nesting(
466 sender.clone(),472 sender.clone(),
467 collection.id,473 (collection.id, token_id),
468 target.1,474 target.1,
469 nesting_budget,475 nesting_budget,
470 )?;476 )?;
575 if let Some(source) = T::CrossTokenAddressMapping::address_to_token(from) {581 if let Some(source) = T::CrossTokenAddressMapping::address_to_token(from) {
576 // TODO: should collection owner be allowed to perform this transfer?582 // TODO: should collection owner be allowed to perform this transfer?
577 ensure!(583 ensure!(
578 <PalletStructure<T>>::indirectly_owned(584 <PalletStructure<T>>::check_indirectly_owned(
579 spender.clone(),585 spender.clone(),
580 source.0,586 source.0,
581 source.1,587 source.1,
588 None,
582 nesting_budget589 nesting_budget
583 )?,590 )?,
584 <CommonError<T>>::ApprovedValueTooLow,591 <CommonError<T>>::ApprovedValueTooLow,
modifiedpallets/structure/src/lib.rsdiffbeforeafterboth
71#[derive(PartialEq)]71#[derive(PartialEq)]
72pub enum Parent<CrossAccountId> {72pub enum Parent<CrossAccountId> {
73 /// Token owned by normal account73 /// Token owned by normal account
74 Normal(CrossAccountId),74 User(CrossAccountId),
75 /// Passed token not found75 /// Passed token not found
76 TokenNotFound,76 TokenNotFound,
77 /// Token owner is another token (target token still may not exist)77 /// Token owner is another token (target token still may not exist)
94 Ok(match handle.token_owner(token) {94 Ok(match handle.token_owner(token) {
95 Some(owner) => match T::CrossTokenAddressMapping::address_to_token(&owner) {95 Some(owner) => match T::CrossTokenAddressMapping::address_to_token(&owner) {
96 Some((collection, token)) => Parent::Token(collection, token),96 Some((collection, token)) => Parent::Token(collection, token),
97 None => Parent::Normal(owner),97 None => Parent::User(owner),
98 },98 },
99 None => Parent::TokenNotFound,99 None => Parent::TokenNotFound,
100 })100 })
137 ) -> Result<T::CrossAccountId, DispatchError> {137 ) -> Result<T::CrossAccountId, DispatchError> {
138 let owner = Self::parent_chain(collection, token)138 let owner = Self::parent_chain(collection, token)
139 .take_while(|_| budget.consume())139 .take_while(|_| budget.consume())
140 .find(|p| matches!(p, Ok(Parent::Normal(_) | Parent::TokenNotFound)))140 .find(|p| matches!(p, Ok(Parent::User(_) | Parent::TokenNotFound)))
141 .ok_or(<Error<T>>::DepthLimit)??;141 .ok_or(<Error<T>>::DepthLimit)??;
142142
143 Ok(match owner {143 Ok(match owner {
144 Parent::Normal(v) => v,144 Parent::User(v) => v,
145 _ => fail!(<Error<T>>::TokenNotFound),145 _ => fail!(<Error<T>>::TokenNotFound),
146 })146 })
147 }147 }
148148
149 /// Check if token indirectly owned by specified user149 /// Check if token indirectly owned by specified user
150 pub fn indirectly_owned(150 pub fn check_indirectly_owned(
151 user: T::CrossAccountId,151 user: T::CrossAccountId,
152 collection: CollectionId,152 collection: CollectionId,
153 token: TokenId,153 token: TokenId,
154 for_nest: Option<(CollectionId, TokenId)>,
154 budget: &dyn Budget,155 budget: &dyn Budget,
155 ) -> Result<bool, DispatchError> {156 ) -> Result<bool, DispatchError> {
156 let target_parent = match T::CrossTokenAddressMapping::address_to_token(&user) {157 let target_parent = match T::CrossTokenAddressMapping::address_to_token(&user) {
157 Some((collection, token)) => Parent::Token(collection, token),158 Some((collection, token)) => Parent::Token(collection, token),
158 None => Parent::Normal(user),159 None => Parent::User(user),
159 };160 };
161
162 // Tried to nest token in itself
163 if Some((collection, token)) == for_nest {
164 return Err(<Error<T>>::OuroborosDetected.into());
165 }
160166
161 Ok(Self::parent_chain(collection, token)167 for parent in Self::parent_chain(collection, token).take_while(|_| budget.consume()) {
162 .take_while(|_| budget.consume())
163 .any(|parent| Ok(&target_parent) == parent.as_ref()))168 match parent? {
169 // Tried to nest token in chain, which has this token as one of parents
170 Parent::Token(collection, token) if Some((collection, token)) == for_nest => {
171 return Err(<Error<T>>::OuroborosDetected.into())
172 }
173 // Found needed parent, token is indirecty owned
174 v if v == target_parent => return Ok(true),
175 Parent::TokenNotFound => return Ok(false),
176 _ => {}
177 }
178 }
179
180 Err(<Error<T>>::DepthLimit.into())
164 }181 }
165}182}
166183
modifiedtests/src/eth/util/helpers.tsdiffbeforeafterboth
23import usingApi, {submitTransactionAsync} from '../../substrate/substrate-api';23import usingApi, {submitTransactionAsync} from '../../substrate/substrate-api';
24import {IKeyringPair} from '@polkadot/types/types';24import {IKeyringPair} from '@polkadot/types/types';
25import {expect} from 'chai';25import {expect} from 'chai';
26import {getGenericResult, UNIQUE} from '../../util/helpers';26import {CrossAccountId, getGenericResult, UNIQUE} from '../../util/helpers';
27import * as solc from 'solc';27import * as solc from 'solc';
28import config from '../../config';28import config from '../../config';
29import privateKey from '../../substrate/privateKey';29import privateKey from '../../substrate/privateKey';
80 ]);80 ]);
81 return Web3.utils.toChecksumAddress('0x' + buf.toString('hex'));81 return Web3.utils.toChecksumAddress('0x' + buf.toString('hex'));
82}82}
83export function tokenIdToCross(collection: number, token: number): CrossAccountId {
84 return {
85 Ethereum: tokenIdToAddress(collection, token),
86 };
87}
8388
84export function createEthAccount(web3: Web3) {89export function createEthAccount(web3: Web3) {
85 const account = web3.eth.accounts.create();90 const account = web3.eth.accounts.create();
addedtests/src/nesting/graphs.test.tsdiffbeforeafterboth

no changes