diff --git a/packages/e2e/src/__snapshots__/resume.test.ts.snap b/packages/e2e/src/__snapshots__/resume.test.ts.snap new file mode 100644 index 00000000..41908eaf --- /dev/null +++ b/packages/e2e/src/__snapshots__/resume.test.ts.snap @@ -0,0 +1,321 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`resume > resume with multi network > resume and xcm works > system events 1`] = ` +[ + { + "data": { + "amount": "(rounded 45000000000)", + "currencyId": { + "Token": "AUSD", + }, + "who": "23M5ttkmR6KcnvsNJdmYTpLo9xfc54g8uCk55buDfiJPon69", + }, + "method": "Deposited", + "section": "tokens", + }, + { + "data": { + "amount": "(rounded 2300000000)", + "currencyId": { + "Token": "AUSD", + }, + "who": "23M5ttkmR6KcnvsNJdmYTpLo9xfc54g8uCk55buDfiJPon69", + }, + "method": "Deposited", + "section": "tokens", + }, + { + "data": { + "amount": "(rounded 1200000000)", + "currencyId": { + "Token": "AUSD", + }, + "who": "23M5ttkmR6KcnvsNJdmYTpLo9xfc54g8uCk55buDfiJPon69", + }, + "method": "Deposited", + "section": "tokens", + }, + { + "data": { + "amount": "(rounded 6800000000)", + "currencyId": { + "Token": "AUSD", + }, + "who": "23M5ttkmR6KcnvsNJdmYTpLo9xfc54g8uCk55buDfiJPon69", + }, + "method": "Deposited", + "section": "tokens", + }, + { + "data": { + "dispatchInfo": { + "class": "Mandatory", + "paysFee": "Yes", + "weight": { + "proofSize": 0, + "refTime": "(rounded 260000000)", + }, + }, + }, + "method": "ExtrinsicSuccess", + "section": "system", + }, + { + "data": { + "dispatchInfo": { + "class": "Mandatory", + "paysFee": "No", + "weight": { + "proofSize": 0, + "refTime": "(rounded 99000000)", + }, + }, + }, + "method": "ExtrinsicSuccess", + "section": "system", + }, + { + "data": { + "amount": "(rounded 2600000000)", + "who": "23y3WetbNi6rDMgHmyRDjgpb7PnhgPotuPPawxruTMLYTLzG", + }, + "method": "Withdraw", + "section": "balances", + }, + { + "data": { + "amount": 100000000000, + "currencyId": { + "Token": "DOT", + }, + "who": "23y3WetbNi6rDMgHmyRDjgpb7PnhgPotuPPawxruTMLYTLzG", + }, + "method": "Withdrawn", + "section": "tokens", + }, + { + "data": { + "assets": [ + { + "fun": { + "Fungible": 100000000000, + }, + "id": { + "Concrete": { + "interior": "Here", + "parents": 1, + }, + }, + }, + ], + "dest": { + "interior": { + "X1": { + "AccountId32": { + "id": "(hash)", + "network": "Any", + }, + }, + }, + "parents": 1, + }, + "fee": { + "fun": { + "Fungible": 100000000000, + }, + "id": { + "Concrete": { + "interior": "Here", + "parents": 1, + }, + }, + }, + "sender": "23y3WetbNi6rDMgHmyRDjgpb7PnhgPotuPPawxruTMLYTLzG", + }, + "method": "TransferredMultiAssets", + "section": "xTokens", + }, + { + "data": { + "amount": "(rounded 2600000000)", + "who": "23M5ttkmR6KcoTAAE6gcmibnKFtVaTP5yxnY8HF1BmrJ2A1i", + }, + "method": "Deposit", + "section": "balances", + }, + { + "data": { + "value": "(rounded 2600000000)", + }, + "method": "Deposit", + "section": "treasury", + }, + { + "data": { + "actualFee": "(rounded 2600000000)", + "actualSurplus": 0, + "actualTip": 0, + "who": "23y3WetbNi6rDMgHmyRDjgpb7PnhgPotuPPawxruTMLYTLzG", + }, + "method": "TransactionFeePaid", + "section": "transactionPayment", + }, + { + "data": { + "dispatchInfo": { + "class": "Normal", + "paysFee": "Yes", + "weight": { + "proofSize": 0, + "refTime": "(rounded 600000000)", + }, + }, + }, + "method": "ExtrinsicSuccess", + "section": "system", + }, +] +`; + +exports[`resume > resume with multi network > resume and xcm works > system events 2`] = ` +[ + { + "data": { + "dispatchInfo": { + "class": "Mandatory", + "paysFee": "Yes", + "weight": { + "proofSize": 0, + "refTime": "(rounded 230000000)", + }, + }, + }, + "method": "ExtrinsicSuccess", + "section": "system", + }, + { + "data": { + "amount": 100000000000, + "who": "13YMK2eYoAvStnzReuxBjMrAvPXmmdsURwZvc62PrdXimbNy", + }, + "method": "Withdraw", + "section": "balances", + }, + { + "data": { + "amount": "(rounded 100000000000)", + "who": "146SvjUZXoMaemdeiecyxgALeYMm8ZWh1yrGo8RtpoPfe7WL", + }, + "method": "Deposit", + "section": "balances", + }, + { + "data": { + "account": "146SvjUZXoMaemdeiecyxgALeYMm8ZWh1yrGo8RtpoPfe7WL", + }, + "method": "NewAccount", + "section": "system", + }, + { + "data": { + "account": "146SvjUZXoMaemdeiecyxgALeYMm8ZWh1yrGo8RtpoPfe7WL", + "freeBalance": "(rounded 100000000000)", + }, + "method": "Endowed", + "section": "balances", + }, + { + "data": { + "amount": "(rounded 410000000)", + "who": "12ZNJzjPBZUh8VV5cuJFkbbwMttFNkH39EhoeYcgGHsJd4MG", + }, + "method": "Deposit", + "section": "balances", + }, + { + "data": [ + "(hash)", + { + "Complete": 4000000000, + }, + ], + "method": "ExecutedUpward", + "section": "ump", + }, + { + "data": { + "dispatchInfo": { + "class": "Mandatory", + "paysFee": "Yes", + "weight": { + "proofSize": 0, + "refTime": "(rounded 98000000)", + }, + }, + }, + "method": "ExtrinsicSuccess", + "section": "system", + }, +] +`; + +exports[`resume > resume with multi network > resume and xcm works 1`] = ` +{ + "consumers": "0", + "data": { + "feeFrozen": "0", + "free": "0", + "miscFrozen": "0", + "reserved": "0", + }, + "nonce": "0", + "providers": "0", + "sufficients": "0", +} +`; + +exports[`resume > resume with multi network > resume and xcm works 2`] = ` +{ + "consumers": "0", + "data": { + "feeFrozen": "0", + "free": "10,000,000,000,000", + "miscFrozen": "0", + "reserved": "0", + }, + "nonce": "0", + "providers": "0", + "sufficients": "0", +} +`; + +exports[`resume > resume with multi network > resume and xcm works 3`] = ` +{ + "free": "10,000,000,000,000", + "frozen": "0", + "reserved": "0", +} +`; + +exports[`resume > resume with multi network > resume and xcm works 4`] = ` +{ + "free": "9,900,000,000,000", + "frozen": "0", + "reserved": "0", +} +`; + +exports[`resume > resume with multi network > resume and xcm works 5`] = ` +{ + "consumers": "0", + "data": { + "feeFrozen": "0", + "free": "99,591,353,032", + "miscFrozen": "0", + "reserved": "0", + }, + "nonce": "0", + "providers": "1", + "sufficients": "0", +} +`; diff --git a/packages/e2e/src/blocks-save.test.ts b/packages/e2e/src/blocks-save.test.ts deleted file mode 100644 index c19085e0..00000000 --- a/packages/e2e/src/blocks-save.test.ts +++ /dev/null @@ -1,74 +0,0 @@ -import { assert, describe, expect, it } from 'vitest' -import { resolve } from 'node:path' -import { tmpdir } from 'node:os' -import networks from './networks' - -describe('block-save', async () => { - const buildBlocks = async () => { - // save blocks - const acala = await networks.acala({ db: resolve(tmpdir(), 'db.sqlite') }) - const { chain, dev } = acala - await dev.newBlock({ count: 2 }) - const head = await chain.getBlockAt(chain.head.number) - const savedHeadHash = head?.hash - await acala.teardown() - - return savedHeadHash - } - - it('saved blocks data', async () => { - const acala = await networks.acala({ db: resolve(tmpdir(), 'db.sqlite') }) - const { chain, dev } = acala - await dev.newBlock({ count: 2 }) - - const numberOfBlocks = await chain.db!.getRepository('Block').count() - expect(numberOfBlocks).toEqual(2) - - const block = await chain.getBlockAt(chain.head.number) - const blockData = await chain.db!.getRepository('Block').findOne({ where: { number: chain.head.number } }) - - assert(block && blockData, 'block and blockData should be defined') - expect(blockData.hash).toEqual(block.hash) - expect(JSON.stringify(blockData.header)).toEqual(JSON.stringify(block.header)) - expect(blockData.parentHash).toEqual((await block.parentBlock)!.hash) - expect(JSON.stringify(blockData.extrinsics)).toEqual(JSON.stringify(await block.extrinsics)) - expect(JSON.stringify(blockData.storageDiff)).toEqual(JSON.stringify(await block.storageDiff())) - - await acala.teardown() - }) - - it('load chain using the latest saved block', async () => { - const savedHeadHash = await buildBlocks() - - // load block - const newAcala = await networks.acala({ db: resolve(tmpdir(), 'db.sqlite'), resume: true }) - const newHeadNumber = newAcala.chain.head.number - const loadedHead = await newAcala.chain.getBlockAt(newHeadNumber) - - expect(loadedHead?.hash).toEqual(savedHeadHash) - await newAcala.teardown() - }) - - it('load chain using a block number', async () => { - await buildBlocks() - - // load blocks - const newAcala = await networks.acala({ db: resolve(tmpdir(), 'db.sqlite'), resume: 3000001 }) - const newHeadNumber = newAcala.chain.head.number - - expect(newHeadNumber).toEqual(3000001) - await newAcala.teardown() - }) - - it('load chain using a block hash', async () => { - const savedHeadHash = await buildBlocks() - - // load blocks - const newAcala = await networks.acala({ db: resolve(tmpdir(), 'db.sqlite'), resume: savedHeadHash }) - const newHeadNumber = newAcala.chain.head.number - const loadedHead = await newAcala.chain.getBlockAt(newHeadNumber) - - expect(loadedHead?.hash).toEqual(savedHeadHash) - await newAcala.teardown() - }) -}) diff --git a/packages/e2e/src/resume.test.ts b/packages/e2e/src/resume.test.ts new file mode 100644 index 00000000..56395229 --- /dev/null +++ b/packages/e2e/src/resume.test.ts @@ -0,0 +1,162 @@ +import { assert, describe, expect, it } from 'vitest' +import { connectUpward } from '@acala-network/chopsticks-core/xcm/upward' +import { matchSnapshot } from './helper' +import { matchSystemEvents, testingPairs } from '@acala-network/chopsticks-testing' +import { resolve } from 'node:path' +import { setStorage } from '@acala-network/chopsticks' +import { tmpdir } from 'node:os' +import networks from './networks' + +describe('resume', async () => { + const buildBlocksAndTeardown = async (network = 'acala', dbName = 'db.sqlite') => { + const blockchain = await networks[network]({ db: resolve(tmpdir(), dbName) }) + const { chain, dev } = blockchain + await dev.newBlock({ count: 2 }) + const head = await chain.getBlockAt(chain.head.number) + const savedHeadHash = head?.hash + await blockchain.teardown() + + return savedHeadHash + } + + it('save blocks data', async () => { + const acala = await networks.acala({ db: resolve(tmpdir(), 'db.sqlite') }) + const { chain, dev } = acala + await dev.newBlock({ count: 2 }) + + const numberOfBlocks = await chain.db!.getRepository('Block').count() + expect(numberOfBlocks).toEqual(2) + + const block = await chain.getBlockAt(chain.head.number) + const blockData = await chain.db!.getRepository('Block').findOne({ where: { number: chain.head.number } }) + + assert(block && blockData, 'block and blockData should be defined') + expect(blockData.hash).toEqual(block.hash) + expect(JSON.stringify(blockData.header)).toEqual(JSON.stringify(block.header)) + expect(blockData.parentHash).toEqual((await block.parentBlock)!.hash) + expect(JSON.stringify(blockData.extrinsics)).toEqual(JSON.stringify(await block.extrinsics)) + expect(JSON.stringify(blockData.storageDiff)).toEqual(JSON.stringify(await block.storageDiff())) + + await acala.teardown() + }) + + it('resume with the latest saved block', async () => { + const savedHeadHash = await buildBlocksAndTeardown() + + // load block + const newAcala = await networks.acala({ db: resolve(tmpdir(), 'db.sqlite'), resume: true }) + const newHeadNumber = newAcala.chain.head.number + const loadedHead = await newAcala.chain.getBlockAt(newHeadNumber) + + expect(loadedHead?.hash).toEqual(savedHeadHash) + await newAcala.teardown() + }) + + it('resume with a block number', async () => { + await buildBlocksAndTeardown() + + // load blocks + const newAcala = await networks.acala({ db: resolve(tmpdir(), 'db.sqlite'), resume: 3000001 }) + const newHeadNumber = newAcala.chain.head.number + + expect(newHeadNumber).toEqual(3000001) + await newAcala.teardown() + }) + + it('resume with a block hash', async () => { + const savedHeadHash = await buildBlocksAndTeardown() + + // load blocks + const newAcala = await networks.acala({ db: resolve(tmpdir(), 'db.sqlite'), resume: savedHeadHash }) + const newHeadNumber = newAcala.chain.head.number + const loadedHead = await newAcala.chain.getBlockAt(newHeadNumber) + + expect(loadedHead?.hash).toEqual(savedHeadHash) + await newAcala.teardown() + }) + + describe('resume with multi network', async () => { + it('resume with Acala and Polkadot works', async () => { + const savedAcalaHash = await buildBlocksAndTeardown('acala', 'db.acala.sqlite') + const savedPolkadotHash = await buildBlocksAndTeardown('polkadot', 'db.polkadot.sqlite') + + // resume + const acala = await networks.acala({ db: resolve(tmpdir(), 'db.acala.sqlite'), resume: savedAcalaHash }) + const polkadot = await networks.polkadot({ + db: resolve(tmpdir(), 'db.polkadot.sqlite'), + resume: savedPolkadotHash, + }) + + const loadedAcalaHead = acala.chain.head + const loadedPolkadotHead = polkadot.chain.head + + expect(loadedAcalaHead.hash).toEqual(savedAcalaHash) + expect(loadedPolkadotHead.hash).toEqual(savedPolkadotHash) + + await acala.teardown() + await polkadot.teardown() + }) + + it('resume and xcm works', async () => { + const savedAcalaHash = await buildBlocksAndTeardown('acala', 'db.acala.sqlite') + const savedPolkadotHash = await buildBlocksAndTeardown('polkadot', 'db.polkadot.sqlite') + + // resume + const acala = await networks.acala({ db: resolve(tmpdir(), 'db.acala.sqlite'), resume: savedAcalaHash }) + const polkadot = await networks.polkadot({ + db: resolve(tmpdir(), 'db.polkadot.sqlite'), + resume: savedPolkadotHash, + }) + + // test ump + await connectUpward(acala.chain, polkadot.chain) + const { alice } = testingPairs() + await setStorage(acala.chain, { + System: { + Account: [[[alice.address], { data: { free: 1000 * 1e10 } }]], + }, + Tokens: { + Accounts: [[[alice.address, { token: 'DOT' }], { free: 1000e10 }]], + }, + }) + + await matchSnapshot(polkadot.api.query.system.account(alice.address)) + await matchSnapshot(acala.api.query.system.account(alice.address)) + await matchSnapshot(acala.api.query.tokens.accounts(alice.address, { token: 'DOT' })) + + await acala.api.tx.xTokens + .transfer( + { + Token: 'DOT', + }, + 10e10, + { + V1: { + parents: 1, + interior: { + X1: { + AccountId32: { + network: 'Any', + id: alice.addressRaw, + }, + }, + }, + }, + }, + { + Unlimited: null, + }, + ) + .signAndSend(alice) + + await acala.chain.newBlock() + await matchSystemEvents(acala) + await matchSnapshot(acala.api.query.tokens.accounts(alice.address, { token: 'DOT' })) + + await polkadot.chain.newBlock() + + await matchSnapshot(polkadot.api.query.system.account(alice.address)) + await matchSystemEvents(polkadot) + }) + }) +})