Skip to content

feat: new SignedData and method argument parser #862

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

Open
wants to merge 26 commits into
base: feat/nc-args-serialization
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
04956df
feat: method argument value class
r4mmer May 12, 2025
16b4634
chore: run tests
r4mmer May 12, 2025
d560690
feat: SignedData serialization
r4mmer May 13, 2025
dbb40cf
chore: linter changes
r4mmer May 13, 2025
c1d6531
tests(integration): fix expected values
r4mmer May 13, 2025
680823c
chore: linter changes
r4mmer May 13, 2025
1bf05cb
feat: add toMatchBuffer into integration tests
r4mmer May 13, 2025
328d2a1
feat: methodArgs completeness
r4mmer May 13, 2025
f30b1fd
chore:linter changes
r4mmer May 13, 2025
91f551c
chore: add branch to list for CI
r4mmer May 13, 2025
1fb8b27
feat: address validation from zod
r4mmer May 13, 2025
9a29d84
test: update test values for Address
r4mmer May 13, 2025
fd1a81d
chore: linter changes
r4mmer May 13, 2025
ba68fdd
fix: hex string regex
r4mmer May 13, 2025
dc55248
chore: wip
r4mmer May 14, 2025
090bfcb
feat: remove ncId from SignedData serialization
r4mmer May 14, 2025
cb8b8fa
tests: bet nano contract
r4mmer May 15, 2025
3bc3831
chore: docstrings
r4mmer May 15, 2025
bece292
chore: remove console log
r4mmer May 15, 2025
5732f04
chore: linter changes
r4mmer May 15, 2025
1e454c0
Merge branch 'feat/nc-args-serialization' into feat/nc-args-class
r4mmer May 16, 2025
853b229
Merge branch 'feat/nc-args-serialization' into feat/nc-args-class
r4mmer May 16, 2025
845f510
chore: review changes
r4mmer May 16, 2025
8f5290d
feat: Address should not be on the bytes enum
r4mmer May 16, 2025
1cc26fb
chore: remove ncId from SignedData
r4mmer May 16, 2025
29cae54
Merge branch 'feat/nc-args-serialization' into feat/nc-args-class
r4mmer May 16, 2025
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
4 changes: 4 additions & 0 deletions .github/workflows/integration-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,17 @@ on:
- master
- release
- release-candidate
- feat/nc-args-serialization
- feat/nc-args-class
Comment on lines +8 to +9
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These will be removed before merging the PR

tags:
- v*
pull_request:
branches:
- release
- release-candidate
- master
- feat/nc-args-serialization
- feat/nc-args-class

env:
TEST_WALLET_START_TIMEOUT: '180000' # 3 minutes
Expand Down
4 changes: 4 additions & 0 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,17 @@ on:
- master
- release
- release-candidate
- feat/nc-args-serialization
- feat/nc-args-class
tags:
- v*
pull_request:
branches:
- release
- release-candidate
- master
- feat/nc-args-serialization
- feat/nc-args-class
jobs:
linter:
runs-on: 'ubuntu-latest'
Expand Down
4 changes: 4 additions & 0 deletions .github/workflows/unit-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,17 @@ on:
- master
- release
- release-candidate
- feat/nc-args-serialization
- feat/nc-args-class
tags:
- v*
pull_request:
branches:
- release
- release-candidate
- master
- feat/nc-args-serialization
- feat/nc-args-class
jobs:
test:
runs-on: 'ubuntu-latest'
Expand Down
1 change: 1 addition & 0 deletions __tests__/integration/configuration/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ services:
"--unsafe-mode", "nano-testnet-alpha",
"--data", "./tmp",
"--nc-indices",
"--nc-exec-logs", "all",
]
environment:
HATHOR_CONFIG_YAML: privnet/conf/privnet.yml
Expand Down
102 changes: 72 additions & 30 deletions __tests__/integration/nanocontracts/bet.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import {
} from '../../../src/errors';
import { OutputType } from '../../../src/wallet/types';
import NanoContractTransactionParser from '../../../src/nano_contracts/parser';
import { NanoContractSignedData } from '../../../src/nano_contracts/types';

let fundsTx;
const builtInBlueprintId = '3cb032600bdf7db784800e4ea911b10676fa2f67591f82bb62628c234e771595';
Expand Down Expand Up @@ -94,6 +95,7 @@ describe('full cycle of bet nano contract', () => {

// Create NC
const oracleData = getOracleBuffer(address1, network);
console.debug('Call to initialize');
const tx1 = await wallet.createAndSendNanoContractTransaction(
NANO_CONTRACTS_INITIALIZE_METHOD,
address0,
Expand All @@ -120,11 +122,25 @@ describe('full cycle of bet nano contract', () => {
tx1Parser.parseAddress();
await tx1Parser.parseArguments();
expect(tx1Parser.address?.base58).toBe(address0);
expect(tx1Parser.parsedArgs).toStrictEqual([
{ name: 'oracle_script', type: 'TxOutputScript', parsed: oracleData },
{ name: 'token_uid', type: 'TokenUid', parsed: Buffer.from(NATIVE_TOKEN_UID, 'hex') },
{ name: 'date_last_bet', type: 'Timestamp', parsed: dateLastBet },
]);
expect(tx1Parser.parsedArgs).not.toBeNull();
if (tx1Parser.parsedArgs === null) {
throw new Error('Could not parse args');
}
expect(tx1Parser.parsedArgs).toHaveLength(3);
expect(tx1Parser.parsedArgs[0]).toMatchObject({
name: 'oracle_script',
type: 'TxOutputScript',
});
// @ts-expect-error toMatchBuffer is defined in setupTests.js
expect(tx1Parser.parsedArgs[0].value).toMatchBuffer(oracleData);
expect(tx1Parser.parsedArgs[1]).toMatchObject({ name: 'token_uid', type: 'TokenUid' });
// @ts-expect-error toMatchBuffer is defined in setupTests.js
expect(tx1Parser.parsedArgs[1].value).toMatchBuffer(Buffer.from(NATIVE_TOKEN_UID, 'hex'));
expect(tx1Parser.parsedArgs[2]).toMatchObject({
name: 'date_last_bet',
type: 'Timestamp',
value: dateLastBet,
});

// First validate some bet arguments error handling
const address2 = await wallet.getAddressAtIndex(2);
Expand Down Expand Up @@ -162,6 +178,7 @@ describe('full cycle of bet nano contract', () => {
).rejects.toThrow(NanoContractTransactionError);

// Bet 100 to address 2
console.debug('First call to bet');
const txBet = await wallet.createAndSendNanoContractTransaction('bet', address2, {
ncId: tx1.hash,
args: [address2, '1x0'],
Expand Down Expand Up @@ -196,10 +213,17 @@ describe('full cycle of bet nano contract', () => {
txBetParser.parseAddress();
await txBetParser.parseArguments();
expect(txBetParser.address?.base58).toBe(address2);
expect(txBetParser.parsedArgs).toStrictEqual([
{ name: 'address', type: 'Address', parsed: address2 },
{ name: 'score', type: 'str', parsed: '1x0' },
]);
expect(txBetParser.parsedArgs).not.toBeNull();
if (txBetParser.parsedArgs === null) {
throw new Error('Could not parse args');
}
expect(txBetParser.parsedArgs).toHaveLength(2);
expect(txBetParser.parsedArgs[0]).toMatchObject({
name: 'address',
type: 'Address',
value: address2,
});
expect(txBetParser.parsedArgs[1]).toMatchObject({ name: 'score', type: 'str', value: '1x0' });

const utxos2 = await wallet.getUtxos();
// We must have one utxo in the address 0 of 900 HTR
Expand All @@ -210,6 +234,7 @@ describe('full cycle of bet nano contract', () => {

// Bet 200 to address 3
const address3 = await wallet.getAddressAtIndex(3);
console.debug('Second call to bet');
const txBet2 = await wallet.createAndSendNanoContractTransaction('bet', address3, {
ncId: tx1.hash,
args: [address3, '2x0'],
Expand Down Expand Up @@ -244,10 +269,17 @@ describe('full cycle of bet nano contract', () => {
txBet2Parser.parseAddress();
await txBet2Parser.parseArguments();
expect(txBet2Parser.address?.base58).toBe(address3);
expect(txBet2Parser.parsedArgs).toStrictEqual([
{ name: 'address', type: 'Address', parsed: address3 },
{ name: 'score', type: 'str', parsed: '2x0' },
]);
expect(txBet2Parser.parsedArgs).not.toBeNull();
if (txBet2Parser.parsedArgs === null) {
throw new Error('Could not parse args');
}
expect(txBet2Parser.parsedArgs).toHaveLength(2);
expect(txBet2Parser.parsedArgs[0]).toMatchObject({
name: 'address',
type: 'Address',
value: address3,
});
expect(txBet2Parser.parsedArgs[1]).toMatchObject({ name: 'score', type: 'str', value: '2x0' });

// Get nc history
const txIds = [tx1.hash, txBet.hash, txBet2.hash];
Expand Down Expand Up @@ -295,7 +327,8 @@ describe('full cycle of bet nano contract', () => {
const nanoSerializer = new Serializer(network);
const result = '1x0';
const resultSerialized = nanoSerializer.serializeFromType(result, 'str');
const inputData = await getOracleInputData(oracleData, resultSerialized, wallet);
const inputData = await getOracleInputData(oracleData, tx1.hash, resultSerialized, wallet);
console.debug('Call to set_result');
const txSetResult = await wallet.createAndSendNanoContractTransaction('set_result', address1, {
ncId: tx1.hash,
args: [`${bufferToHex(inputData)},${result},str`],
Expand All @@ -316,18 +349,27 @@ describe('full cycle of bet nano contract', () => {
network,
txSetResultData.tx.nc_args
);
txSetResultParser.parseAddress(network);
txSetResultParser.parseAddress();
await txSetResultParser.parseArguments();
expect(txSetResultParser.address.base58).toBe(address1);
expect(txSetResultParser.parsedArgs).toStrictEqual([
{
name: 'result',
type: 'SignedData[str]',
parsed: `${bufferToHex(inputData)},${result},str`,
},
]);
expect(txSetResultParser.address?.base58).toBe(address1);
expect(txSetResultParser.parsedArgs).not.toBeNull();
if (txSetResultParser.parsedArgs === null) {
throw new Error('Could not parse args');
}
expect(txSetResultParser.parsedArgs).toHaveLength(1);
expect(txSetResultParser.parsedArgs[0]).toMatchObject({
name: 'result',
type: 'SignedData[str]',
});
expect((txSetResultParser.parsedArgs[0].value as NanoContractSignedData).type).toEqual('str');
expect(
(txSetResultParser.parsedArgs[0].value as NanoContractSignedData).signature
// @ts-expect-error toMatchBuffer is defined in setupTests.js
).toMatchBuffer(inputData);
expect((txSetResultParser.parsedArgs[0].value as NanoContractSignedData).value).toEqual(result);

// Try to withdraw to address 2, success
console.debug('Call to withdraw');
const txWithdrawal = await wallet.createAndSendNanoContractTransaction('withdraw', address2, {
ncId: tx1.hash,
actions: [
Expand All @@ -351,14 +393,14 @@ describe('full cycle of bet nano contract', () => {

const txWithdrawalParser = new NanoContractTransactionParser(
blueprintId,
'set_result',
'withdraw',
txWithdrawalData.tx.nc_pubkey,
network,
txWithdrawalData.tx.nc_args
);
txWithdrawalParser.parseAddress(network);
txWithdrawalParser.parseAddress();
await txWithdrawalParser.parseArguments();
expect(txWithdrawalParser.address.base58).toBe(address2);
expect(txWithdrawalParser.address?.base58).toBe(address2);
expect(txWithdrawalParser.parsedArgs).toBe(null);

// Get state again
Expand Down Expand Up @@ -486,7 +528,7 @@ describe('full cycle of bet nano contract', () => {

jest.spyOn(wallet.storage, 'processHistory');
expect(wallet.storage.processHistory.mock.calls.length).toBe(0);
await waitTxConfirmed(wallet, txWithdrawal2.hash);
await waitTxConfirmed(wallet, txWithdrawal2.hash, null);
const txWithdrawal2Data = await wallet.getFullTxById(txWithdrawal2.hash);

// The tx became voided after the block because of the nano execution
Expand Down Expand Up @@ -519,16 +561,16 @@ describe('full cycle of bet nano contract', () => {
// Add funds and validate address meta
await GenesisWalletHelper.injectFunds(ocbWallet, address0, 1000n);
const address0Meta = await ocbWallet.storage.store.getAddressMeta(address0);
expect(address0Meta.numTransactions).toBe(1);
expect(address0Meta?.numTransactions).toBe(1);

// Use the bet blueprint code
const code = fs.readFileSync('./__tests__/integration/configuration/bet.py', 'utf8');
const tx = await ocbWallet.createAndSendOnChainBlueprintTransaction(code, address10);
// Wait for the tx to be confirmed, so we can use the on chain blueprint
await waitTxConfirmed(ocbWallet, tx.hash);
await waitTxConfirmed(ocbWallet, tx.hash!, null);
// We must have one transaction in the address10 now
const newAddress10Meta = await ocbWallet.storage.store.getAddressMeta(address10);
expect(newAddress10Meta.numTransactions).toBe(1);
expect(newAddress10Meta?.numTransactions).toBe(1);
// Execute the bet blueprint tests
await executeTests(ocbWallet, tx.hash);
});
Expand Down
Loading
Loading