Skip to content

Commit b1dacb3

Browse files
Merge pull request #788 from HathorNetwork/master
v1.14.0
2 parents 41efdd2 + f99f5f3 commit b1dacb3

File tree

11 files changed

+600
-249
lines changed

11 files changed

+600
-249
lines changed

.github/workflows/lint.yml

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
name: linter
2+
on:
3+
push:
4+
branches:
5+
- master
6+
- release
7+
- release-candidate
8+
tags:
9+
- v*
10+
pull_request:
11+
branches:
12+
- release
13+
- release-candidate
14+
- master
15+
jobs:
16+
linter:
17+
runs-on: 'ubuntu-latest'
18+
timeout-minutes: 40 # default is 360
19+
strategy:
20+
matrix:
21+
node-version: [20.x]
22+
steps:
23+
- name: Checkout
24+
# https://github.com/actions/checkout/releases/tag/v4.1.7
25+
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332
26+
27+
- name: Use Node.js ${{ matrix.node-version }}
28+
# https://github.com/actions/setup-node/releases/tag/v4.0.2
29+
uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8
30+
with:
31+
node-version: ${{ matrix.node-version }}
32+
33+
- name: Install dependencies
34+
run: npm install
35+
36+
- name: Build
37+
run: npm run build
38+
39+
- name: Lint and format
40+
run: npm run format:check && npm run lint

.github/workflows/main.yml renamed to .github/workflows/unit-test.yml

-3
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,6 @@ jobs:
3636
- name: Build
3737
run: npm run build
3838

39-
- name: Lint and format
40-
run: npm run format:check && npm run lint
41-
4239
- name: Test
4340
run: npm run test
4441

__tests__/integration/configuration/docker-compose.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ services:
66

77
fullnode:
88
image:
9-
${HATHOR_LIB_INTEGRATION_TESTS_FULLNODE_IMAGE:-hathornetwork/hathor-core:experimental-nano-sdk-new-types}
9+
${HATHOR_LIB_INTEGRATION_TESTS_FULLNODE_IMAGE:-hathornetwork/hathor-core:experimental-nano-sdk-v1.5-rc4-mempool-hotfix}
1010
command: [
1111
"run_node",
1212
"--listen", "tcp:40404",

__tests__/integration/hathorwallet_facade.test.ts

+55
Original file line numberDiff line numberDiff line change
@@ -1736,6 +1736,61 @@ describe('sendManyOutputsTransaction', () => {
17361736
});
17371737
});
17381738

1739+
describe('authority utxo selection', () => {
1740+
afterEach(async () => {
1741+
await stopAllWallets();
1742+
await GenesisWalletHelper.clearListeners();
1743+
});
1744+
1745+
it('getMintAuthority', async () => {
1746+
// Setting up the custom token
1747+
const hWallet = await generateWalletHelper();
1748+
await GenesisWalletHelper.injectFunds(hWallet, await hWallet.getAddressAtIndex(0), 1);
1749+
const { hash: tokenUid } = await createTokenHelper(hWallet, 'Token to test', 'ATST', 100);
1750+
1751+
// Mark mint authority as selected_as_input
1752+
const [mintInput] = await hWallet.getMintAuthority(tokenUid, { many: false });
1753+
await hWallet.markUtxoSelected(mintInput.txId, mintInput.index, true);
1754+
1755+
// getMintAuthority should return even if the utxo is already selected_as_input
1756+
await expect(hWallet.getMintAuthority(tokenUid, { many: false })).resolves.toStrictEqual([
1757+
mintInput,
1758+
]);
1759+
await expect(
1760+
hWallet.getMintAuthority(tokenUid, { many: false, only_available_utxos: false })
1761+
).resolves.toStrictEqual([mintInput]);
1762+
1763+
// getMintAuthority should not return selected_as_input utxos if only_available_utxos is true
1764+
await expect(
1765+
hWallet.getMintAuthority(tokenUid, { many: false, only_available_utxos: true })
1766+
).resolves.toStrictEqual([]);
1767+
});
1768+
1769+
it('getMeltAuthority', async () => {
1770+
// Setting up the custom token
1771+
const hWallet = await generateWalletHelper();
1772+
await GenesisWalletHelper.injectFunds(hWallet, await hWallet.getAddressAtIndex(0), 1);
1773+
const { hash: tokenUid } = await createTokenHelper(hWallet, 'Token to test', 'ATST', 100);
1774+
1775+
// Mark melt authority as selected_as_input
1776+
const [meltInput] = await hWallet.getMeltAuthority(tokenUid, { many: false });
1777+
await hWallet.markUtxoSelected(meltInput.txId, meltInput.index, true);
1778+
1779+
// getMeltAuthority should return even if the utxo is already selected_as_input
1780+
await expect(hWallet.getMeltAuthority(tokenUid, { many: false })).resolves.toStrictEqual([
1781+
meltInput,
1782+
]);
1783+
await expect(
1784+
hWallet.getMeltAuthority(tokenUid, { many: false, only_available_utxos: false })
1785+
).resolves.toStrictEqual([meltInput]);
1786+
1787+
// getMeltAuthority should not return selected_as_input utxos if only_available_utxos is true
1788+
await expect(
1789+
hWallet.getMeltAuthority(tokenUid, { many: false, only_available_utxos: true })
1790+
).resolves.toStrictEqual([]);
1791+
});
1792+
});
1793+
17391794
describe('createNewToken', () => {
17401795
afterEach(async () => {
17411796
await stopAllWallets();

__tests__/integration/helpers/wallet.helper.ts

+4-2
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
* LICENSE file in the root directory of this source tree.
66
*/
77

8-
import { get } from 'lodash';
8+
import { get, includes } from 'lodash';
99
import Connection from '../../../src/new/connection';
1010
import {
1111
DEBUG_LOGGING,
@@ -459,7 +459,9 @@ export async function waitTxConfirmed(
459459

460460
try {
461461
// Only return the positive response after the tx has a first block
462-
while ((await getTxFirstBlock(hWallet, txId)) === null) {
462+
// the nano contract txs are executing the method as soon as they arrive in the node
463+
// and adding the first_block as mempool so we shouldn't consider this as a valid first block for confirmation
464+
while (includes([null, 'mempool'], await getTxFirstBlock(hWallet, txId))) {
463465
await delay(1000);
464466

465467
// If we've reached the requested timeout, break the while loop

__tests__/integration/nanocontracts/bet.test.ts

+78
Original file line numberDiff line numberDiff line change
@@ -344,6 +344,84 @@ describe('full cycle of bet nano contract', () => {
344344
// Get tx history with success
345345
const txHistory = await wallet.getTxHistory();
346346
expect(txHistory).toHaveLength(4);
347+
348+
// Now test getting nano state in a past block, after the second bet
349+
const bet2TxData = await wallet.getFullTxById(txBet2.hash);
350+
const firstBlock = bet2TxData.meta.first_block;
351+
const firstBlockHeight = bet2TxData.meta.first_block_height;
352+
353+
// Get NC state in the past, using txBet2 first block
354+
const ncStateFirstBlock = await ncApi.getNanoContractState(
355+
tx1.hash,
356+
[
357+
'token_uid',
358+
'total',
359+
'final_result',
360+
'oracle_script',
361+
'date_last_bet',
362+
`address_details.a'${address2}'`,
363+
`withdrawals.a'${address2}'`,
364+
`address_details.a'${address3}'`,
365+
`withdrawals.a'${address3}'`,
366+
],
367+
[],
368+
[],
369+
firstBlock
370+
);
371+
372+
expect(ncStateFirstBlock.fields.token_uid.value).toBe(NATIVE_TOKEN_UID);
373+
expect(ncStateFirstBlock.fields.date_last_bet.value).toBe(dateLastBet);
374+
expect(ncStateFirstBlock.fields.oracle_script.value).toBe(bufferToHex(outputScriptBuffer1));
375+
expect(ncStateFirstBlock.fields.final_result.value).toBeNull();
376+
expect(ncStateFirstBlock.fields.total.value).toBe(300);
377+
expect(ncStateFirstBlock.fields[`address_details.a'${address2}'`].value).toHaveProperty(
378+
'1x0',
379+
100
380+
);
381+
expect(ncStateFirstBlock.fields[`withdrawals.a'${address2}'`].value).toBeUndefined();
382+
expect(ncStateFirstBlock.fields[`address_details.a'${address3}'`].value).toHaveProperty(
383+
'2x0',
384+
200
385+
);
386+
expect(ncStateFirstBlock.fields[`withdrawals.a'${address3}'`].value).toBeUndefined();
387+
388+
// Get NC state in the past, using txBet2 first block height
389+
const ncStateFirstBlockHeight = await ncApi.getNanoContractState(
390+
tx1.hash,
391+
[
392+
'token_uid',
393+
'total',
394+
'final_result',
395+
'oracle_script',
396+
'date_last_bet',
397+
`address_details.a'${address2}'`,
398+
`withdrawals.a'${address2}'`,
399+
`address_details.a'${address3}'`,
400+
`withdrawals.a'${address3}'`,
401+
],
402+
[],
403+
[],
404+
null,
405+
firstBlockHeight
406+
);
407+
408+
expect(ncStateFirstBlockHeight.fields.token_uid.value).toBe(NATIVE_TOKEN_UID);
409+
expect(ncStateFirstBlockHeight.fields.date_last_bet.value).toBe(dateLastBet);
410+
expect(ncStateFirstBlockHeight.fields.oracle_script.value).toBe(
411+
bufferToHex(outputScriptBuffer1)
412+
);
413+
expect(ncStateFirstBlockHeight.fields.final_result.value).toBeNull();
414+
expect(ncStateFirstBlockHeight.fields.total.value).toBe(300);
415+
expect(ncStateFirstBlockHeight.fields[`address_details.a'${address2}'`].value).toHaveProperty(
416+
'1x0',
417+
100
418+
);
419+
expect(ncStateFirstBlockHeight.fields[`withdrawals.a'${address2}'`].value).toBeUndefined();
420+
expect(ncStateFirstBlockHeight.fields[`address_details.a'${address3}'`].value).toHaveProperty(
421+
'2x0',
422+
200
423+
);
424+
expect(ncStateFirstBlockHeight.fields[`withdrawals.a'${address3}'`].value).toBeUndefined();
347425
};
348426

349427
it('bet deposit', async () => {

__tests__/new/wallet-utxo.test.ts

+6-6
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,9 @@ class FakeHathorWallet {
2323
this[method] = jest.fn().mockImplementation(HathorWallet.prototype[method].bind(this));
2424
}
2525

26-
this.sendManyOutputsTransaction.mockImplementation(() => {
27-
return Promise.resolve({ hash: '123' });
28-
});
26+
this.sendManyOutputsSendTransaction.mockImplementation(() => ({
27+
run: jest.fn().mockReturnValue(Promise.resolve({ hash: '123' })),
28+
}));
2929

3030
// Prepare storage
3131
const store = new MemoryStore();
@@ -161,18 +161,18 @@ describe('UTXO Consolidation', () => {
161161

162162
test('correctly execute consolidateUtxos', async () => {
163163
const result = await hathorWallet.consolidateUtxos(destinationAddress);
164-
expect(hathorWallet.sendManyOutputsTransaction).toHaveBeenCalled();
164+
expect(hathorWallet.sendManyOutputsSendTransaction).toHaveBeenCalled();
165165
expect(result.total_utxos_consolidated).toBe(2);
166166
expect(result.total_amount).toBe(2);
167167
expect(result.txId).toBe('123');
168168
expect(result.utxos).toHaveLength(2);
169169
expect(result.utxos.some(utxo => utxo.locked)).toBeFalsy();
170170
// assert single output
171-
expect(hathorWallet.sendManyOutputsTransaction.mock.calls[0][0]).toEqual([
171+
expect(hathorWallet.sendManyOutputsSendTransaction.mock.calls[0][0]).toEqual([
172172
{ address: destinationAddress, value: 2, token: '00' },
173173
]);
174174
// assert 2 inputs only
175-
expect(hathorWallet.sendManyOutputsTransaction.mock.calls[0][1].inputs).toHaveLength(2);
175+
expect(hathorWallet.sendManyOutputsSendTransaction.mock.calls[0][1].inputs).toHaveLength(2);
176176
});
177177

178178
test('all HTR utxos locked by height', async () => {

src/api/nano.ts

+16-2
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {
1212
NanoContractBlueprintInformationAPIResponse,
1313
NanoContractHistoryAPIResponse,
1414
NanoContractStateAPIResponse,
15+
NanoContractStateAPIParameters,
1516
} from '../nano_contracts/types';
1617

1718
/**
@@ -28,6 +29,8 @@ const ncApi = {
2829
* @param fields Array of fields to get state
2930
* @param balances Array of balances to get state
3031
* @param calls Array of private method calls to execute in the nano contract and get the result
32+
* @param block_hash Hash of the block to get the state of the nano
33+
* @param block_height Height of the block to get the state of the nano
3134
*
3235
* @memberof ApiNanoContracts
3336
* @inner
@@ -36,9 +39,20 @@ const ncApi = {
3639
id: string,
3740
fields: string[],
3841
balances: string[],
39-
calls: string[]
42+
calls: string[],
43+
block_hash: string | null = null,
44+
block_height: number | null = null
4045
): Promise<NanoContractStateAPIResponse> {
41-
const data = { id, fields, balances, calls };
46+
const data: NanoContractStateAPIParameters = { id, fields, balances, calls };
47+
48+
if (block_hash) {
49+
data.block_hash = block_hash;
50+
}
51+
52+
if (block_height) {
53+
data.block_height = block_height;
54+
}
55+
4256
const axiosInstance = await createRequestInstance();
4357
try {
4458
const response = await axiosInstance.get(`nano_contract/state`, { params: data });

src/nano_contracts/types.ts

+9
Original file line numberDiff line numberDiff line change
@@ -98,3 +98,12 @@ export interface NanoContractStateAPIResponse {
9898
// Calls requested
9999
calls: Map<string, StateValueSuccess | StateValueError>;
100100
}
101+
102+
export interface NanoContractStateAPIParameters {
103+
id: string;
104+
fields: string[];
105+
balances: string[];
106+
calls: string[];
107+
block_hash?: string;
108+
block_height?: number;
109+
}

0 commit comments

Comments
 (0)