difftreelog
test fix for new ouroboros behavior
in: master
4 files changed
crates/struct-versioning/src/lib.rsdiffbeforeafterboth--- a/crates/struct-versioning/src/lib.rs
+++ b/crates/struct-versioning/src/lib.rs
@@ -199,7 +199,7 @@
let mut out = Vec::new();
for version in attr.first_version..=attr.current_version {
let name = if version == attr.current_version {
- input.ident.clone()
+ input.ident.clone()
} else {
format_ident!("{}Version{}", &input.ident, version)
};
tests/src/nesting/unnest.test.tsdiffbeforeafterboth1import {expect} from 'chai';2import {tokenIdToAddress} from '../eth/util/helpers';3import privateKey from '../substrate/privateKey';4import usingApi, {executeTransaction} from '../substrate/substrate-api';5import {6 createCollectionExpectSuccess,7 createItemExpectFailure, 8 createItemExpectSuccess,9 getTokenOwner, 10 getTopmostTokenOwner, 11 normalizeAccountId, 12 setCollectionLimitsExpectSuccess, 13 transferExpectFailure, 14 transferExpectSuccess, 15} from '../util/helpers';16import {IKeyringPair} from '@polkadot/types/types';1718let alice: IKeyringPair;19let bob: IKeyringPair;2021describe('Integration Test: Unnesting', () => {22 before(async () => {23 alice = privateKey('//Alice');24 bob = privateKey('//Bob');25 });2627 it('Allows the owner to successfully unnest a token', async () => {28 await usingApi(async api => {29 const collection = await createCollectionExpectSuccess({mode: {type: 'NFT'}});30 await setCollectionLimitsExpectSuccess(alice, collection, {nestingRule: 'Owner'});31 const targetToken = await createItemExpectSuccess(alice, collection, 'NFT');32 const targetAddress = {Ethereum: tokenIdToAddress(collection, targetToken)};3334 // Create a nested token35 const nestedToken = await createItemExpectSuccess(alice, collection, 'NFT', targetAddress);3637 // Unnest38 await expect(executeTransaction(39 api, 40 alice, 41 api.tx.unique.transferFrom(normalizeAccountId(targetAddress), normalizeAccountId(alice), collection, nestedToken, 1),42 )).to.not.be.rejected;43 expect(await getTokenOwner(api, collection, nestedToken)).to.be.deep.equal({Substrate: alice.address});4445 // Nest and burn46 await transferExpectSuccess(collection, nestedToken, alice, targetAddress);47 await expect(executeTransaction(48 api, 49 alice, 50 api.tx.unique.burnFrom(collection, normalizeAccountId(alice.address), nestedToken, 1),51 )).to.not.be.rejected;52 await expect(getTokenOwner(api, collection, nestedToken)).to.be.rejected; // 'owner == null'53 });54 });5556 // todo refungible-fungible test just in case57});5859describe('Negative Test: Unnesting', () => {60 before(async () => {61 alice = privateKey('//Alice');62 bob = privateKey('//Bob');63 });64 65 it('Disallows a non-owner to unnest/burn a token', async () => {66 await usingApi(async api => {67 const collection = await createCollectionExpectSuccess({mode: {type: 'NFT'}});68 await setCollectionLimitsExpectSuccess(alice, collection, {nestingRule: 'Owner'});69 const targetToken = await createItemExpectSuccess(alice, collection, 'NFT');70 const targetAddress = {Ethereum: tokenIdToAddress(collection, targetToken)};7172 // Create a nested token73 const nestedToken = await createItemExpectSuccess(alice, collection, 'NFT', targetAddress);7475 // Try to unnest76 await expect(executeTransaction(77 api, 78 bob, 79 api.tx.unique.transferFrom(normalizeAccountId(targetAddress), normalizeAccountId(bob), collection, nestedToken, 1),80 )).to.be.rejectedWith(/^common\.ApprovedValueTooLow$/);81 //await transferFromExpectSuccess(collection, nestedToken, bob, targetAddress, {Substrate: bob.address});82 expect(await getTokenOwner(api, collection, nestedToken)).to.be.deep.equal({Ethereum: tokenIdToAddress(collection, targetToken).toLowerCase()});8384 // Try to burn85 await expect(executeTransaction(86 api, 87 bob, 88 api.tx.unique.burnFrom(collection, normalizeAccountId(bob.address), nestedToken, 1),89 )).to.not.be.rejectedWith(/^common\.ApprovedValueTooLow$/);90 expect(await getTokenOwner(api, collection, nestedToken)).to.be.deep.equal({Ethereum: tokenIdToAddress(collection, targetToken).toLowerCase()});91 });92 });93 94 it('Disallows excessive token nesting', async () => {95 await usingApi(async api => {96 const collection = await createCollectionExpectSuccess({mode: {type: 'NFT'}});97 await setCollectionLimitsExpectSuccess(alice, collection, {nestingRule: 'Owner'});98 const targetToken = await createItemExpectSuccess(alice, collection, 'NFT');99100 // Create a nested token matryoshka101 const nestedToken1 = await createItemExpectSuccess(alice, collection, 'NFT', {Ethereum: tokenIdToAddress(collection, targetToken)});102 const nestedToken2 = await createItemExpectSuccess(alice, collection, 'NFT', {Ethereum: tokenIdToAddress(collection, nestedToken1)});103 // The nesting depth is limited by 2104 await createItemExpectFailure(alice, collection, 'NFT', {Ethereum: tokenIdToAddress(collection, nestedToken2)});105106 expect(await getTopmostTokenOwner(api, collection, nestedToken2)).to.be.deep.equal({Substrate: alice.address});107 });108 });109110 // todo another test for creating excessive depth matryoshka with Ethereum, move this one to nest ^111 112 // Recursive nesting113 it('Prevents Ouroboros-nested operations', async () => { 114 await usingApi(async api => {115 const collection = await createCollectionExpectSuccess({mode: {type: 'NFT'}});116 await setCollectionLimitsExpectSuccess(alice, collection, {nestingRule: 'Owner'});117 const targetToken = await createItemExpectSuccess(alice, collection, 'NFT');118119 // Create a nested token ouroboros120 const nestedToken = await createItemExpectSuccess(alice, collection, 'NFT', {Ethereum: tokenIdToAddress(collection, targetToken)});121 await transferExpectSuccess(collection, targetToken, alice, {Ethereum: tokenIdToAddress(collection, nestedToken)});122123 expect(await getTokenOwner(api, collection, nestedToken)).to.be.deep.equal({Ethereum: tokenIdToAddress(collection, targetToken).toLowerCase()});124125 // Make sure the ouroboros is detected126 await expect(getTopmostTokenOwner(api, collection, nestedToken)).to.be.rejected; // With(/^common\.DepthLimit$/);127 // todo transferFrom, must exit with Ouroboros error128 });129 });130});1import {expect} from 'chai';2import {tokenIdToAddress} from '../eth/util/helpers';3import privateKey from '../substrate/privateKey';4import usingApi, {executeTransaction} from '../substrate/substrate-api';5import {6 createCollectionExpectSuccess,7 createItemExpectFailure,8 createItemExpectSuccess,9 getTokenOwner,10 getTopmostTokenOwner,11 normalizeAccountId,12 setCollectionLimitsExpectSuccess,13 transferExpectSuccess,14} from '../util/helpers';15import {IKeyringPair} from '@polkadot/types/types';1617let alice: IKeyringPair;18let bob: IKeyringPair;1920describe('Integration Test: Unnesting', () => {21 before(async () => {22 alice = privateKey('//Alice');23 bob = privateKey('//Bob');24 });2526 it('Allows the owner to successfully unnest a token', async () => {27 await usingApi(async api => {28 const collection = await createCollectionExpectSuccess({mode: {type: 'NFT'}});29 await setCollectionLimitsExpectSuccess(alice, collection, {nestingRule: 'Owner'});30 const targetToken = await createItemExpectSuccess(alice, collection, 'NFT');31 const targetAddress = {Ethereum: tokenIdToAddress(collection, targetToken)};3233 // Create a nested token34 const nestedToken = await createItemExpectSuccess(alice, collection, 'NFT', targetAddress);3536 // Unnest37 await expect(executeTransaction(38 api,39 alice,40 api.tx.unique.transferFrom(normalizeAccountId(targetAddress), normalizeAccountId(alice), collection, nestedToken, 1),41 )).to.not.be.rejected;42 expect(await getTokenOwner(api, collection, nestedToken)).to.be.deep.equal({Substrate: alice.address});4344 // Nest and burn45 await transferExpectSuccess(collection, nestedToken, alice, targetAddress);46 await expect(executeTransaction(47 api,48 alice,49 api.tx.unique.burnFrom(collection, normalizeAccountId(alice.address), nestedToken, 1),50 )).to.not.be.rejected;51 await expect(getTokenOwner(api, collection, nestedToken)).to.be.rejected; // 'owner == null'52 });53 });5455 // todo refungible-fungible test just in case56});5758describe('Negative Test: Unnesting', () => {59 before(async () => {60 alice = privateKey('//Alice');61 bob = privateKey('//Bob');62 });6364 it('Disallows a non-owner to unnest/burn a token', async () => {65 await usingApi(async api => {66 const collection = await createCollectionExpectSuccess({mode: {type: 'NFT'}});67 await setCollectionLimitsExpectSuccess(alice, collection, {nestingRule: 'Owner'});68 const targetToken = await createItemExpectSuccess(alice, collection, 'NFT');69 const targetAddress = {Ethereum: tokenIdToAddress(collection, targetToken)};7071 // Create a nested token72 const nestedToken = await createItemExpectSuccess(alice, collection, 'NFT', targetAddress);7374 // Try to unnest75 await expect(executeTransaction(76 api,77 bob,78 api.tx.unique.transferFrom(normalizeAccountId(targetAddress), normalizeAccountId(bob), collection, nestedToken, 1),79 )).to.be.rejectedWith(/^common\.ApprovedValueTooLow$/);80 //await transferFromExpectSuccess(collection, nestedToken, bob, targetAddress, {Substrate: bob.address});81 expect(await getTokenOwner(api, collection, nestedToken)).to.be.deep.equal({Ethereum: tokenIdToAddress(collection, targetToken).toLowerCase()});8283 // Try to burn84 await expect(executeTransaction(85 api,86 bob,87 api.tx.unique.burnFrom(collection, normalizeAccountId(bob.address), nestedToken, 1),88 )).to.not.be.rejectedWith(/^common\.ApprovedValueTooLow$/);89 expect(await getTokenOwner(api, collection, nestedToken)).to.be.deep.equal({Ethereum: tokenIdToAddress(collection, targetToken).toLowerCase()});90 });91 });9293 it('Disallows excessive token nesting', async () => {94 await usingApi(async api => {95 const collection = await createCollectionExpectSuccess({mode: {type: 'NFT'}});96 await setCollectionLimitsExpectSuccess(alice, collection, {nestingRule: 'Owner'});97 const targetToken = await createItemExpectSuccess(alice, collection, 'NFT');9899 // Create a nested token matryoshka100 const nestedToken1 = await createItemExpectSuccess(alice, collection, 'NFT', {Ethereum: tokenIdToAddress(collection, targetToken)});101 const nestedToken2 = await createItemExpectSuccess(alice, collection, 'NFT', {Ethereum: tokenIdToAddress(collection, nestedToken1)});102 // The nesting depth is limited by 2103 await createItemExpectFailure(alice, collection, 'NFT', {Ethereum: tokenIdToAddress(collection, nestedToken2)});104105 expect(await getTopmostTokenOwner(api, collection, nestedToken2)).to.be.deep.equal({Substrate: alice.address});106 });107 });108109 // todo another test for creating excessive depth matryoshka with Ethereum, move this one to nest ^110111 // Recursive nesting112 it('Prevents ouroboros creation', async () => {113 const collection = await createCollectionExpectSuccess({mode: {type: 'NFT'}});114 await setCollectionLimitsExpectSuccess(alice, collection, {nestingRule: 'Owner'});115 const targetToken = await createItemExpectSuccess(alice, collection, 'NFT');116117 // Create a nested token ouroboros118 const nestedToken = await createItemExpectSuccess(alice, collection, 'NFT', {Ethereum: tokenIdToAddress(collection, targetToken)});119 expect(transferExpectSuccess(collection, targetToken, alice, {Ethereum: tokenIdToAddress(collection, nestedToken)})).to.be.rejectedWith(/^structure\.OuroborosDetected$/);120 });121});tests/src/pallet-presence.test.tsdiffbeforeafterboth--- a/tests/src/pallet-presence.test.ts
+++ b/tests/src/pallet-presence.test.ts
@@ -30,6 +30,7 @@
'timestamp',
'transactionpayment',
'treasury',
+ 'structure',
'system',
'vesting',
'parachainsystem',
tests/src/util/helpers.tsdiffbeforeafterboth--- a/tests/src/util/helpers.ts
+++ b/tests/src/util/helpers.ts
@@ -17,15 +17,15 @@
import '../interfaces/augment-api-rpc';
import '../interfaces/augment-api-query';
import {ApiPromise, Keyring} from '@polkadot/api';
-import type {AccountId, EventRecord} from '@polkadot/types/interfaces';
-import {IKeyringPair} from '@polkadot/types/types';
+import type {AccountId, EventRecord, Event} from '@polkadot/types/interfaces';
+import {AnyTuple, IEvent, IKeyringPair} from '@polkadot/types/types';
import {evmToAddress} from '@polkadot/util-crypto';
import BN from 'bn.js';
import chai from 'chai';
import chaiAsPromised from 'chai-as-promised';
import {alicesPublicKey} from '../accounts';
import privateKey from '../substrate/privateKey';
-import {default as usingApi, submitTransactionAsync, submitTransactionExpectFailAsync} from '../substrate/substrate-api';
+import {default as usingApi, executeTransaction, submitTransactionAsync, submitTransactionExpectFailAsync} from '../substrate/substrate-api';
import {hexToStr, strToUTF16, utf16ToStr} from './util';
import {UpDataStructsRpcCollection} from '@polkadot/types/lookup';
@@ -105,7 +105,6 @@
}
interface TransferResult {
- success: boolean;
collectionId: number;
itemId: number;
sender?: CrossAccountId;
@@ -168,6 +167,12 @@
return result;
}
+export function getEvent<T extends Event>(events: EventRecord[], check: (event: IEvent<AnyTuple>) => event is T): T | undefined {
+ const event = events.find(r => check(r.event));
+ if (!event) return;
+ return event.event as T;
+}
+
export function getGenericResult(events: EventRecord[]): GenericResult {
const result: GenericResult = {
success: false,
@@ -225,27 +230,20 @@
return result;
}
-export function getTransferResult(events: EventRecord[]): TransferResult {
- const result: TransferResult = {
- success: false,
- collectionId: 0,
- itemId: 0,
- value: 0n,
- };
-
- events.forEach(({event: {data, method, section}}) => {
- if (method === 'ExtrinsicSuccess') {
- result.success = true;
- } else if (section === 'common' && method === 'Transfer') {
- result.collectionId = +data[0].toString();
- result.itemId = +data[1].toString();
- result.sender = normalizeAccountId(data[2].toJSON() as any);
- result.recipient = normalizeAccountId(data[3].toJSON() as any);
- result.value = BigInt(data[4].toString());
+export function getTransferResult(api: ApiPromise, events: EventRecord[]): TransferResult {
+ for (const {event} of events) {
+ if (api.events.common.Transfer.is(event)) {
+ const [collection, token, sender, recipient, value] = event.data;
+ return {
+ collectionId: collection.toNumber(),
+ itemId: token.toNumber(),
+ sender: normalizeAccountId(sender.toJSON() as any),
+ recipient: normalizeAccountId(recipient.toJSON() as any),
+ value: value.toBigInt(),
+ };
}
- });
-
- return result;
+ }
+ throw new Error('no transfer event');
}
interface Nft {
@@ -300,9 +298,9 @@
}
const tx = api.tx.unique.createCollectionEx({
- name: strToUTF16(name),
- description: strToUTF16(description),
- tokenPrefix: strToUTF16(tokenPrefix),
+ name: strToUTF16(name),
+ description: strToUTF16(description),
+ tokenPrefix: strToUTF16(tokenPrefix),
mode: modeprm as any,
schemaVersion: schemaVersion,
});
@@ -912,15 +910,15 @@
balanceBefore = await getBalance(api, collectionId, to, tokenId);
}
const transferTx = api.tx.unique.transfer(to, collectionId, tokenId, value);
- const events = await submitTransactionAsync(sender, transferTx);
- const result = getTransferResult(events);
- // tslint:disable-next-line:no-unused-expression
- expect(result.success).to.be.true;
+ const events = await executeTransaction(api, sender, transferTx);
+
+ const result = getTransferResult(api, events);
expect(result.collectionId).to.be.equal(collectionId);
expect(result.itemId).to.be.equal(tokenId);
expect(result.sender).to.be.deep.equal(normalizeAccountId(sender.address));
expect(result.recipient).to.be.deep.equal(to);
expect(result.value).to.be.equal(BigInt(value));
+
if (type === 'NFT') {
expect(await getTokenOwner(api, collectionId, tokenId)).to.be.deep.equal(to);
}