Skip to content
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

Start release-candidate v0.29.0-rc.1 #453

Merged
merged 7 commits into from
Jun 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion SECURITY.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
# Security

Hathor Labs has a bounty program to encourage white hat hackers to collaborate in identifying security breaches and vulnerabilities in Hathor headless wallet. To know more about this, see [https://immunefi.com/bounty/hathornetwork/](https://immunefi.com/bounty/hathornetwork/).
Hathor Labs has a bounty program to encourage white hat hackers to collaborate in identifying security breaches and vulnerabilities in Hathor headless wallet. To know more about this, see [Bug bounty program at Hathor Network](https://hathor.network/bug-bounty/).
188 changes: 162 additions & 26 deletions __tests__/integration/melt-tokens.test.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
import { transactionUtils, constants, network, scriptsUtils } from '@hathor/wallet-lib';
import { transactionUtils, constants, network, scriptsUtils, ScriptData } from '@hathor/wallet-lib';
import { TestUtils } from './utils/test-utils-integration';
import { WALLET_CONSTANTS } from './configuration/test-constants';
import { WalletHelper } from './utils/wallet-helper';

describe('melt tokens', () => {
let wallet1;
const totalMinted = 1000;
const initialHTR = 10;
let meltedAmount = 0;
let htrMelted = 0;

const tokenA = {
name: 'Token A',
symbol: 'TKA',
Expand All @@ -18,19 +23,19 @@ describe('melt tokens', () => {
await WalletHelper.startMultipleWalletsForTest([wallet1]);

// Creating a token for the tests
await wallet1.injectFunds(10, 0);
await wallet1.injectFunds(20, 0);
const tkAtx = await wallet1.createToken({
name: tokenA.name,
symbol: tokenA.symbol,
amount: 800,
amount: 1000,
address: await wallet1.getAddressAt(0),
change_address: await wallet1.getAddressAt(0)
});
tokenA.uid = tkAtx.hash;

/**
* Status:
* wallet1[0]: 2 HTR , 800 TKA
* wallet1[0]: 10 HTR , 1000 TKA
*/
});

Expand Down Expand Up @@ -151,7 +156,7 @@ describe('melt tokens', () => {
.send({
token: tokenA.uid,
address: await wallet1.getAddressAt(1),
amount: 1000
amount: totalMinted + 100,
})
.set({ 'x-wallet-id': wallet1.walletId });

Expand All @@ -173,6 +178,13 @@ describe('melt tokens', () => {
})
.set({ 'x-wallet-id': wallet1.walletId });

meltedAmount += 300;
htrMelted += 3;
/**
* Status:
* wallet1[0]: 13 HTR , 700 TKA
*/

expect(response.body.success).toBe(true);

await TestUtils.waitForTxReceived(wallet1.walletId, response.body.hash);
Expand All @@ -185,12 +197,12 @@ describe('melt tokens', () => {
const addr4htr = await wallet1.getAddressInfo(4);
const addr4tka = await wallet1.getAddressInfo(4, tokenA.uid);
expect(addr4htr.total_amount_available).toBe(0);
expect(addr4tka.total_amount_available).toBe(500);
expect(addr4tka.total_amount_available).toBe(totalMinted - meltedAmount);

const balance1htr = await wallet1.getBalance();
const balance1tka = await wallet1.getBalance(tokenA.uid);
expect(balance1htr.available).toBe(2 + 3);
expect(balance1tka.available).toBe(800 - 300);
expect(balance1htr.available).toBe(initialHTR + htrMelted);
expect(balance1tka.available).toBe(totalMinted - meltedAmount);
});

it('should melt with deposit address only', async () => {
Expand All @@ -207,6 +219,13 @@ describe('melt tokens', () => {
})
.set({ 'x-wallet-id': wallet1.walletId });

meltedAmount += 100;
htrMelted += 1;
/**
* Status:
* wallet1[0]: 14 HTR , 600 TKA
*/

expect(response.body.success).toBe(true);

await TestUtils.waitForTxReceived(wallet1.walletId, response.body.hash);
Expand All @@ -218,8 +237,8 @@ describe('melt tokens', () => {

const balance1htr = await wallet1.getBalance();
const balance1tka = await wallet1.getBalance(tokenA.uid);
expect(balance1htr.available).toBe(2 + 3 + 1);
expect(balance1tka.available).toBe(800 - 300 - 100);
expect(balance1htr.available).toBe(initialHTR + htrMelted);
expect(balance1tka.available).toBe(totalMinted - meltedAmount);
});

it('should melt with change address only', async () => {
Expand All @@ -232,19 +251,26 @@ describe('melt tokens', () => {
})
.set({ 'x-wallet-id': wallet1.walletId });

meltedAmount += 100;
htrMelted += 1;
/**
* Status:
* wallet1[0]: 15 HTR , 500 TKA
*/

expect(response.body.success).toBe(true);

await TestUtils.waitForTxReceived(wallet1.walletId, response.body.hash);

const addr8htr = await wallet1.getAddressInfo(8);
const addr8tka = await wallet1.getAddressInfo(8, tokenA.uid);
expect(addr8htr.total_amount_available).toBe(0);
expect(addr8tka.total_amount_available).toBe(300);
expect(addr8tka.total_amount_available).toBe(totalMinted - meltedAmount);

const balance1htr = await wallet1.getBalance();
const balance1tka = await wallet1.getBalance(tokenA.uid);
expect(balance1htr.available).toBe(2 + 3 + 1 + 1);
expect(balance1tka.available).toBe(800 - 300 - 100 - 100);
expect(balance1htr.available).toBe(initialHTR + htrMelted);
expect(balance1tka.available).toBe(totalMinted - meltedAmount);
});

it('should melt with mandatory parameters', async () => {
Expand All @@ -256,14 +282,21 @@ describe('melt tokens', () => {
})
.set({ 'x-wallet-id': wallet1.walletId });

meltedAmount += 100;
htrMelted += 1;
/**
* Status:
* wallet1[0]: 16 HTR , 400 TKA
*/

expect(response.body.success).toBe(true);

await TestUtils.waitForTxReceived(wallet1.walletId, response.body.hash);

const balance1htr = await wallet1.getBalance();
const balance1tka = await wallet1.getBalance(tokenA.uid);
expect(balance1htr.available).toBe(2 + 3 + 1 + 1 + 1); // 8
expect(balance1tka.available).toBe(800 - 300 - 100 - 100 - 100); // 200
expect(balance1htr.available).toBe(initialHTR + htrMelted); // 16
expect(balance1tka.available).toBe(totalMinted - meltedAmount); // 400
});

it('should not retrieve funds when melting below 100 tokens', async () => {
Expand All @@ -275,14 +308,20 @@ describe('melt tokens', () => {
})
.set({ 'x-wallet-id': wallet1.walletId });

meltedAmount += 50;
/**
* Status:
* wallet1[0]: 16 HTR , 350 TKA
*/

expect(response.body.success).toBe(true);

await TestUtils.waitForTxReceived(wallet1.walletId, response.body.hash);

const balance1htr = await wallet1.getBalance();
const balance1tka = await wallet1.getBalance(tokenA.uid);
expect(balance1htr.available).toBe(8);
expect(balance1tka.available).toBe(150);
expect(balance1htr.available).toBe(initialHTR + htrMelted);
expect(balance1tka.available).toBe(totalMinted - meltedAmount);
});

it('should retrieve funds rounded down when not melting multiples of 100', async () => {
Expand All @@ -291,18 +330,25 @@ describe('melt tokens', () => {
.send({
token: tokenA.uid,
address: await wallet1.getAddressAt(1),
amount: 100
amount: 110
})
.set({ 'x-wallet-id': wallet1.walletId });

meltedAmount += 110;
htrMelted += 1;
/**
* Status:
* wallet1[0]: 17 HTR , 240 TKA
*/

expect(response.body.success).toBe(true);

await TestUtils.waitForTxReceived(wallet1.walletId, response.body.hash);

const balance1htr = await wallet1.getBalance();
const balance1tka = await wallet1.getBalance(tokenA.uid);
expect(balance1htr.available).toBe(9);
expect(balance1tka.available).toBe(50);
expect(balance1htr.available).toBe(initialHTR + htrMelted);
expect(balance1tka.available).toBe(totalMinted - meltedAmount);
});

it('should melt and send melt output to the correct address', async () => {
Expand All @@ -313,18 +359,24 @@ describe('melt tokens', () => {
token: tokenA.uid,
address: await wallet1.getAddressAt(16),
melt_authority_address: address0,
amount: 30
amount: 20
})
.set({ 'x-wallet-id': wallet1.walletId });

meltedAmount += 20;
/**
* Status:
* wallet1[0]: 17 HTR , 220 TKA
*/

const transaction = response.body;
expect(transaction.success).toBe(true);
await TestUtils.waitForTxReceived(wallet1.walletId, response.body.hash);

const balance1htr = await wallet1.getBalance();
const balance1tka = await wallet1.getBalance(tokenA.uid);
expect(balance1htr.available).toBe(9);
expect(balance1tka.available).toBe(20);
expect(balance1htr.available).toBe(initialHTR + htrMelted);
expect(balance1tka.available).toBe(totalMinted - meltedAmount);

// Validating a new melt authority was created by default
const authorityOutputs = transaction.outputs.filter(
Expand All @@ -338,15 +390,97 @@ describe('melt tokens', () => {
expect(p2pkh.address.base58).toEqual(address0);
});

it('should melt tokens and add data outputs to the transaction', async () => {
const response = await TestUtils.request
.post('/wallet/melt-tokens')
.send({
token: tokenA.uid,
amount: 10,
data: ['foobar1', 'foobar2'],
})
.set({ 'x-wallet-id': wallet1.walletId });

meltedAmount += 10;
htrMelted -= 2; // we create 2 data outputs and no melted htr
/**
* Status:
* wallet1[0]: 15 HTR , 210 TKA
*/

expect(response.body.success).toBe(true);

await TestUtils.waitForTxReceived(wallet1.walletId, response.body.hash);

const balance1htr = await wallet1.getBalance();
const balance1tka = await wallet1.getBalance(tokenA.uid);
expect(balance1htr.available).toBe(initialHTR + htrMelted);
expect(balance1tka.available).toBe(totalMinted - meltedAmount);

const transaction = response.body;
const dataOutput1 = transaction.outputs[1];
const dataOutput2 = transaction.outputs[0];
const script1 = Array.from((new ScriptData('foobar1')).createScript());
const script2 = Array.from((new ScriptData('foobar2')).createScript());

expect(dataOutput1.token_data).toBe(0);
expect(dataOutput1.value).toBe(1);
expect(dataOutput1.script.data).toEqual(script1);

expect(dataOutput2.token_data).toBe(0);
expect(dataOutput2.value).toBe(1);
expect(dataOutput2.script.data).toEqual(script2);
});

it('should melt tokens and add data outputs to the transaction at the start of the outputs', async () => {
const response = await TestUtils.request
.post('/wallet/melt-tokens')
.send({
token: tokenA.uid,
amount: 10,
data: ['foobar'],
unshift_data: false,
})
.set({ 'x-wallet-id': wallet1.walletId });

meltedAmount += 10;
htrMelted -= 1; // we create 1 data outputs and no melted htr
/**
* Status:
* wallet1[0]: 14 HTR , 200 TKA
*/

expect(response.body.success).toBe(true);

await TestUtils.waitForTxReceived(wallet1.walletId, response.body.hash);

const balance1htr = await wallet1.getBalance();
const balance1tka = await wallet1.getBalance(tokenA.uid);
expect(balance1htr.available).toBe(initialHTR + htrMelted);
expect(balance1tka.available).toBe(totalMinted - meltedAmount);

const transaction = response.body;
const dataOutput = transaction.outputs[transaction.outputs.length - 1];
const script = Array.from((new ScriptData('foobar')).createScript());

expect(dataOutput.token_data).toBe(0);
expect(dataOutput.value).toBe(1);
expect(dataOutput.script.data).toEqual(script);
});

it('should melt allowing external authority address', async () => {
// XXX: This test should be the last test since it sends the melt authority to the burn address
if (totalMinted - meltedAmount <= 0) {
// when we reach this step the wallet should have at least 1 token to melt and end the tests
throw new Error('No tokens to melt');
}
const externalAddress = TestUtils.getBurnAddress();
const response = await TestUtils.request
.post('/wallet/melt-tokens')
.send({
token: tokenA.uid,
address: await wallet1.getAddressAt(17),
melt_authority_address: externalAddress,
amount: 20
amount: totalMinted - meltedAmount,
})
.set({ 'x-wallet-id': wallet1.walletId });

Expand All @@ -359,17 +493,19 @@ describe('melt tokens', () => {
address: await wallet1.getAddressAt(17),
melt_authority_address: externalAddress,
allow_external_melt_authority_address: true,
amount: 20
amount: totalMinted - meltedAmount,
})
.set({ 'x-wallet-id': wallet1.walletId });

htrMelted += Math.floor((totalMinted - meltedAmount) / 100);

const transaction = response2.body;
expect(transaction.success).toBe(true);
await TestUtils.waitForTxReceived(wallet1.walletId, response2.body.hash);

const balance1htr = await wallet1.getBalance();
const balance1tka = await wallet1.getBalance(tokenA.uid);
expect(balance1htr.available).toBe(9);
expect(balance1htr.available).toBe(initialHTR + htrMelted);
expect(balance1tka.available).toBe(0);

// Validating a new melt authority was created by default
Expand Down
Loading
Loading