difftreelog
feat prevent ouroboros creation during nest
in: master
11 files changed
client/rpc/src/lib.rsdiffbeforeafterbothno syntactic changes
pallets/common/src/lib.rsdiffbeforeafterboth918 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;pallets/fungible/src/common.rsdiffbeforeafterboth237 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 {pallets/fungible/src/lib.rsdiffbeforeafterboth226226227 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 }229234295300296 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_budget394 )?,405 )?,395 <CommonError<T>>::ApprovedValueTooLow,406 <CommonError<T>>::ApprovedValueTooLow,pallets/nonfungible/src/common.rsdiffbeforeafterboth251 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 {pallets/nonfungible/src/lib.rsdiffbeforeafterboth298298299 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 }301306381 );386 );382 }387 }383388384 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();389390 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_budget543 )?,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 budget642 )?,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>>::SourceCollectionIsNotAllowedToNest636 );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(())pallets/refungible/src/common.rsdiffbeforeafterboth260 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 {pallets/refungible/src/lib.rsdiffbeforeafterboth354354355 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 }357362455 }460 }456 }461 }457462458 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)?;464470465 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_budget583 )?,590 )?,584 <CommonError<T>>::ApprovedValueTooLow,591 <CommonError<T>>::ApprovedValueTooLow,pallets/structure/src/lib.rsdiffbeforeafterboth71#[derive(PartialEq)]71#[derive(PartialEq)]72pub enum Parent<CrossAccountId> {72pub enum Parent<CrossAccountId> {73 /// Token owned by normal account73 /// Token owned by normal account74 Normal(CrossAccountId),74 User(CrossAccountId),75 /// Passed token not found75 /// Passed token not found76 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)??;142142143 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 }148148149 /// Check if token indirectly owned by specified user149 /// Check if token indirectly owned by specified user150 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 };161162 // Tried to nest token in itself163 if Some((collection, token)) == for_nest {164 return Err(<Error<T>>::OuroborosDetected.into());165 }160166161 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 parents170 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 owned174 v if v == target_parent => return Ok(true),175 Parent::TokenNotFound => return Ok(false),176 _ => {}177 }178 }179180 Err(<Error<T>>::DepthLimit.into())164 }181 }165}182}166183tests/src/eth/util/helpers.tsdiffbeforeafterboth23import 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}838884export function createEthAccount(web3: Web3) {89export function createEthAccount(web3: Web3) {85 const account = web3.eth.accounts.create();90 const account = web3.eth.accounts.create();tests/src/nesting/graphs.test.tsdiffbeforeafterbothno changes