From 7ddeea4c189da82381a351b1560d4639f6dfbc8d Mon Sep 17 00:00:00 2001 From: Dongil Seo Date: Thu, 19 Oct 2023 19:39:22 +0900 Subject: [PATCH 01/31] Add countDecimals() util function --- __tests__/ain.test.ts | 53 ++++++++++++++++++++++++++++++++++++++++++- src/wallet.ts | 19 ++++++++++++++++ 2 files changed, 71 insertions(+), 1 deletion(-) diff --git a/__tests__/ain.test.ts b/__tests__/ain.test.ts index 6c91916..a9ad969 100644 --- a/__tests__/ain.test.ts +++ b/__tests__/ain.test.ts @@ -1,5 +1,6 @@ // @ts-nocheck import Ain from '../src/ain'; +import Wallet from '../src/wallet'; import { TransactionBody, SetOperation, TransactionInput } from '../src/types'; import axios from 'axios'; import { fail, eraseProtoVer } from './test_util'; @@ -73,7 +74,57 @@ describe('ain-js', function() { }); describe('Wallet', function() { - let addresses = [] + it('countDecimals', function() { + expect(Wallet.countDecimals(0)).toBe(0); + expect(Wallet.countDecimals(1)).toBe(0); + expect(Wallet.countDecimals(10)).toBe(0); + expect(Wallet.countDecimals(100)).toBe(0); + expect(Wallet.countDecimals(1000)).toBe(0); + expect(Wallet.countDecimals(10000)).toBe(0); + expect(Wallet.countDecimals(100000)).toBe(0); + expect(Wallet.countDecimals(1000000)).toBe(0); + expect(Wallet.countDecimals(10000000)).toBe(0); + expect(Wallet.countDecimals(100000000)).toBe(0); + expect(Wallet.countDecimals(123456789)).toBe(0); + expect(Wallet.countDecimals(11)).toBe(0); + expect(Wallet.countDecimals(101)).toBe(0); + expect(Wallet.countDecimals(1001)).toBe(0); + expect(Wallet.countDecimals(10001)).toBe(0); + expect(Wallet.countDecimals(100001)).toBe(0); + expect(Wallet.countDecimals(1000001)).toBe(0); + expect(Wallet.countDecimals(10000001)).toBe(0); + expect(Wallet.countDecimals(100000001)).toBe(0); + expect(Wallet.countDecimals(1)).toBe(0); + expect(Wallet.countDecimals(0.1)).toBe(1); + expect(Wallet.countDecimals(0.01)).toBe(2); + expect(Wallet.countDecimals(0.001)).toBe(3); + expect(Wallet.countDecimals(0.0001)).toBe(4); + expect(Wallet.countDecimals(0.00001)).toBe(5); + expect(Wallet.countDecimals(0.000001)).toBe(6); + expect(Wallet.countDecimals(0.0000001)).toBe(7); + expect(Wallet.countDecimals(0.00000001)).toBe(8); + expect(Wallet.countDecimals(0.000000001)).toBe(9); + expect(Wallet.countDecimals(1.2)).toBe(1); + expect(Wallet.countDecimals(0.12)).toBe(2); + expect(Wallet.countDecimals(0.012)).toBe(3); + expect(Wallet.countDecimals(0.0012)).toBe(4); + expect(Wallet.countDecimals(0.00012)).toBe(5); + expect(Wallet.countDecimals(0.000012)).toBe(6); + expect(Wallet.countDecimals(0.0000012)).toBe(7); + expect(Wallet.countDecimals(0.00000012)).toBe(8); + expect(Wallet.countDecimals(0.000000012)).toBe(9); + expect(Wallet.countDecimals(0.0000000012)).toBe(10); + expect(Wallet.countDecimals(1.03)).toBe(2); + expect(Wallet.countDecimals(1.003)).toBe(3); + expect(Wallet.countDecimals(1.0003)).toBe(4); + expect(Wallet.countDecimals(1.00003)).toBe(5); + expect(Wallet.countDecimals(1.000003)).toBe(6); + expect(Wallet.countDecimals(1.0000003)).toBe(7); + expect(Wallet.countDecimals(1.00000003)).toBe(8); + expect(Wallet.countDecimals(1.000000003)).toBe(9); + expect(Wallet.countDecimals(1.0000000003)).toBe(10); + }); + it('create', function() { const beforeLength = ain.wallet.length; ain.wallet.create(2); diff --git a/src/wallet.ts b/src/wallet.ts index fef037b..94be99f 100755 --- a/src/wallet.ts +++ b/src/wallet.ts @@ -58,6 +58,25 @@ export default class Wallet { return this.accounts[checksummed].public_key; } + /** + * Counts the given number's decimal number. + * @param {number} value + * @returns {number} The decimal number. + */ + static countDecimals(value: number): number { + const decimalExponentRegex = /(\d*\.{0,1}\d*)e-(\d+)/gm; + + if (Math.floor(value) === value) { + return 0; + } + const valueString = value.toString(); + const matches = decimalExponentRegex.exec(valueString); + if (matches) { + return Number(matches[2]) + Wallet.countDecimals(Number(matches[1])); + } + return valueString.split('.')[1].length || 0; + } + /** * Creates new accounts and adds them to the wallet. * @param {number} numberOfAccounts The number of accounts to create. From 58b09d7e75a2afc36fb8ac5d3782ae0b97970c41 Mon Sep 17 00:00:00 2001 From: Dongil Seo Date: Thu, 19 Oct 2023 19:40:50 +0900 Subject: [PATCH 02/31] Allow up to 6 decimals in transfer values --- __tests__/ain.test.ts | 26 ++++++++++++++++++++++++++ src/wallet.ts | 5 +++++ 2 files changed, 31 insertions(+) diff --git a/__tests__/ain.test.ts b/__tests__/ain.test.ts index a9ad969..9d07004 100644 --- a/__tests__/ain.test.ts +++ b/__tests__/ain.test.ts @@ -273,6 +273,32 @@ describe('ain-js', function() { expect(balanceAfter).toBe(balanceBefore - 100); }); + it('transfer with a value of up to 6 decimal places', async function() { + const balanceBefore = await ain.wallet.getBalance(); + const response = await ain.wallet.transfer({ + to: '0xbA58D93edD8343C001eC5f43E620712Ba8C10813', + value: 0.000001, // of 6 decimal places + nonce: -1 }); + const balanceAfter = await ain.wallet.getBalance(); + expect(balanceAfter).toBe(balanceBefore - 0.000001); + }); + + it('transfer with a value of more than 6 decimal places', async function() { + const balanceBefore = await ain.wallet.getBalance(); + try { + const response = await ain.wallet.transfer({ + to: '0xbA58D93edD8343C001eC5f43E620712Ba8C10813', + value: 0.0000001, // of 7 decimal places + nonce: -1 }); + fail('should not happen'); + } catch(e) { + expect(e.message).toBe('Transfer value of more than 6 decimal places.'); + } finally { + const balanceAfter = await ain.wallet.getBalance(); + expect(balanceAfter).toBe(balanceBefore); + } + }); + it('chainId', function() { // chainId = 0 ain.setProvider(test_node_2, 0); diff --git a/src/wallet.ts b/src/wallet.ts index 94be99f..92f8d75 100755 --- a/src/wallet.ts +++ b/src/wallet.ts @@ -3,6 +3,7 @@ import Ain from './ain'; import { validateMnemonic, mnemonicToSeedSync } from 'bip39'; import Reference from './ain-db/ref'; const AIN_HD_DERIVATION_PATH = "m/44'/412'/0'/0/"; /* default wallet address for AIN */ +const MAX_TRANSFERABLE_DECIMALS = 6; /* The maximum transferable decimal places of values */ /** * A class for AI Network wallets. @@ -255,6 +256,10 @@ export default class Wallet { transfer(input: {to: string, value: number, from?: string, nonce?: number, gas_price?: number}, isDryrun: boolean = false): Promise { const address = this.getImpliedAddress(input.from); const toAddress = Ain.utils.toChecksumAddress(input.to); + const numDecimalPlaces = Wallet.countDecimals(input.value); + if (numDecimalPlaces > MAX_TRANSFERABLE_DECIMALS) { + throw Error(`Transfer value of more than ${MAX_TRANSFERABLE_DECIMALS} decimal places.`); + } const transferRef = this.ain.db.ref(`/transfer/${address}/${toAddress}`).push() as Reference; return transferRef.setValue({ ref: '/value', address, value: input.value, nonce: input.nonce, gas_price: input.gas_price }, isDryrun); From 0bb81356c911d485b0521d312762478398a3f34a Mon Sep 17 00:00:00 2001 From: Dongil Seo Date: Fri, 20 Oct 2023 16:07:19 +0900 Subject: [PATCH 03/31] Add more comments to decimal test cases --- __tests__/ain.test.ts | 98 ++++++++++++++++++++++--------------------- 1 file changed, 50 insertions(+), 48 deletions(-) diff --git a/__tests__/ain.test.ts b/__tests__/ain.test.ts index 9d07004..473f06d 100644 --- a/__tests__/ain.test.ts +++ b/__tests__/ain.test.ts @@ -75,54 +75,56 @@ describe('ain-js', function() { describe('Wallet', function() { it('countDecimals', function() { - expect(Wallet.countDecimals(0)).toBe(0); - expect(Wallet.countDecimals(1)).toBe(0); - expect(Wallet.countDecimals(10)).toBe(0); - expect(Wallet.countDecimals(100)).toBe(0); - expect(Wallet.countDecimals(1000)).toBe(0); - expect(Wallet.countDecimals(10000)).toBe(0); - expect(Wallet.countDecimals(100000)).toBe(0); - expect(Wallet.countDecimals(1000000)).toBe(0); - expect(Wallet.countDecimals(10000000)).toBe(0); - expect(Wallet.countDecimals(100000000)).toBe(0); - expect(Wallet.countDecimals(123456789)).toBe(0); - expect(Wallet.countDecimals(11)).toBe(0); - expect(Wallet.countDecimals(101)).toBe(0); - expect(Wallet.countDecimals(1001)).toBe(0); - expect(Wallet.countDecimals(10001)).toBe(0); - expect(Wallet.countDecimals(100001)).toBe(0); - expect(Wallet.countDecimals(1000001)).toBe(0); - expect(Wallet.countDecimals(10000001)).toBe(0); - expect(Wallet.countDecimals(100000001)).toBe(0); - expect(Wallet.countDecimals(1)).toBe(0); - expect(Wallet.countDecimals(0.1)).toBe(1); - expect(Wallet.countDecimals(0.01)).toBe(2); - expect(Wallet.countDecimals(0.001)).toBe(3); - expect(Wallet.countDecimals(0.0001)).toBe(4); - expect(Wallet.countDecimals(0.00001)).toBe(5); - expect(Wallet.countDecimals(0.000001)).toBe(6); - expect(Wallet.countDecimals(0.0000001)).toBe(7); - expect(Wallet.countDecimals(0.00000001)).toBe(8); - expect(Wallet.countDecimals(0.000000001)).toBe(9); - expect(Wallet.countDecimals(1.2)).toBe(1); - expect(Wallet.countDecimals(0.12)).toBe(2); - expect(Wallet.countDecimals(0.012)).toBe(3); - expect(Wallet.countDecimals(0.0012)).toBe(4); - expect(Wallet.countDecimals(0.00012)).toBe(5); - expect(Wallet.countDecimals(0.000012)).toBe(6); - expect(Wallet.countDecimals(0.0000012)).toBe(7); - expect(Wallet.countDecimals(0.00000012)).toBe(8); - expect(Wallet.countDecimals(0.000000012)).toBe(9); - expect(Wallet.countDecimals(0.0000000012)).toBe(10); - expect(Wallet.countDecimals(1.03)).toBe(2); - expect(Wallet.countDecimals(1.003)).toBe(3); - expect(Wallet.countDecimals(1.0003)).toBe(4); - expect(Wallet.countDecimals(1.00003)).toBe(5); - expect(Wallet.countDecimals(1.000003)).toBe(6); - expect(Wallet.countDecimals(1.0000003)).toBe(7); - expect(Wallet.countDecimals(1.00000003)).toBe(8); - expect(Wallet.countDecimals(1.000000003)).toBe(9); - expect(Wallet.countDecimals(1.0000000003)).toBe(10); + expect(Wallet.countDecimals(0)).toBe(0); // '0' + expect(Wallet.countDecimals(1)).toBe(0); // '1' + expect(Wallet.countDecimals(10)).toBe(0); // '10' + expect(Wallet.countDecimals(100)).toBe(0); // '100' + expect(Wallet.countDecimals(1000)).toBe(0); // '1000' + expect(Wallet.countDecimals(10000)).toBe(0); // '10000' + expect(Wallet.countDecimals(100000)).toBe(0); // '100000' + expect(Wallet.countDecimals(1000000)).toBe(0); // '1000000' + expect(Wallet.countDecimals(10000000)).toBe(0); // '10000000' + expect(Wallet.countDecimals(100000000)).toBe(0); // '100000000' + expect(Wallet.countDecimals(1000000000)).toBe(0); // '1000000000' + expect(Wallet.countDecimals(1234567890)).toBe(0); // '1234567890' + expect(Wallet.countDecimals(11)).toBe(0); // '11' + expect(Wallet.countDecimals(101)).toBe(0); // '101' + expect(Wallet.countDecimals(1001)).toBe(0); // '1001' + expect(Wallet.countDecimals(10001)).toBe(0); // '10001' + expect(Wallet.countDecimals(100001)).toBe(0); // '100001' + expect(Wallet.countDecimals(1000001)).toBe(0); // '1000001' + expect(Wallet.countDecimals(10000001)).toBe(0); // '10000001' + expect(Wallet.countDecimals(100000001)).toBe(0); // '100000001' + expect(Wallet.countDecimals(1000000001)).toBe(0); // '1000000001' + expect(Wallet.countDecimals(0.1)).toBe(1); // '0.1' + expect(Wallet.countDecimals(0.01)).toBe(2); // '0.01' + expect(Wallet.countDecimals(0.001)).toBe(3); // '0.001' + expect(Wallet.countDecimals(0.0001)).toBe(4); // '0.0001' + expect(Wallet.countDecimals(0.00001)).toBe(5); // '0.00001' + expect(Wallet.countDecimals(0.000001)).toBe(6); // '0.000001' + expect(Wallet.countDecimals(0.0000001)).toBe(7); // '1e-7' + expect(Wallet.countDecimals(0.00000001)).toBe(8); // '1e-8' + expect(Wallet.countDecimals(0.000000001)).toBe(9); // '1e-9' + expect(Wallet.countDecimals(0.0000000001)).toBe(10); // '1e-10' + expect(Wallet.countDecimals(1.2)).toBe(1); // '1.2' + expect(Wallet.countDecimals(0.12)).toBe(2); // '0.12' + expect(Wallet.countDecimals(0.012)).toBe(3); // '0.012' + expect(Wallet.countDecimals(0.0012)).toBe(4); // '0.0012' + expect(Wallet.countDecimals(0.00012)).toBe(5); // '0.00012' + expect(Wallet.countDecimals(0.000012)).toBe(6); // '0.000012' + expect(Wallet.countDecimals(0.0000012)).toBe(7); // '0.0000012' + expect(Wallet.countDecimals(0.00000012)).toBe(8); // '1.2e-7' + expect(Wallet.countDecimals(0.000000012)).toBe(9); // '1.2e-8' + expect(Wallet.countDecimals(0.0000000012)).toBe(10); // '1.2e-9' + expect(Wallet.countDecimals(1.03)).toBe(2); // '1.03' + expect(Wallet.countDecimals(1.003)).toBe(3); // '1.003' + expect(Wallet.countDecimals(1.0003)).toBe(4); // '1.0003' + expect(Wallet.countDecimals(1.00003)).toBe(5); // '1.00003' + expect(Wallet.countDecimals(1.000003)).toBe(6); // '1.000003' + expect(Wallet.countDecimals(1.0000003)).toBe(7); // '1.0000003' + expect(Wallet.countDecimals(1.00000003)).toBe(8); // '1.00000003' + expect(Wallet.countDecimals(1.000000003)).toBe(9); // '1.000000003' + expect(Wallet.countDecimals(1.0000000003)).toBe(10); // '1.0000000003' }); it('create', function() { From bbc38c77e596d8895824f40c27cff37d0da3a6bc Mon Sep 17 00:00:00 2001 From: Dongil Seo Date: Mon, 23 Oct 2023 09:54:03 +0900 Subject: [PATCH 04/31] Allow only positive transfer values --- __tests__/ain.test.ts | 36 ++++++++++++++++++++++++++++++++++-- src/wallet.ts | 3 +++ 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/__tests__/ain.test.ts b/__tests__/ain.test.ts index 473f06d..9f8a815 100644 --- a/__tests__/ain.test.ts +++ b/__tests__/ain.test.ts @@ -275,11 +275,43 @@ describe('ain-js', function() { expect(balanceAfter).toBe(balanceBefore - 100); }); + it('transfer with a zero value', async function() { + const balanceBefore = await ain.wallet.getBalance(); + try { + const response = await ain.wallet.transfer({ + to: '0xbA58D93edD8343C001eC5f43E620712Ba8C10813', + value: 0, // a zero value + nonce: -1 }); + fail('should not happen'); + } catch(e) { + expect(e.message).toBe('Non-positive transfer value.'); + } finally { + const balanceAfter = await ain.wallet.getBalance(); + expect(balanceAfter).toBe(balanceBefore); + } + }); + + it('transfer with a negative value', async function() { + const balanceBefore = await ain.wallet.getBalance(); + try { + const response = await ain.wallet.transfer({ + to: '0xbA58D93edD8343C001eC5f43E620712Ba8C10813', + value: -0.1, // a negative value + nonce: -1 }); + fail('should not happen'); + } catch(e) { + expect(e.message).toBe('Non-positive transfer value.'); + } finally { + const balanceAfter = await ain.wallet.getBalance(); + expect(balanceAfter).toBe(balanceBefore); + } + }); + it('transfer with a value of up to 6 decimal places', async function() { const balanceBefore = await ain.wallet.getBalance(); const response = await ain.wallet.transfer({ to: '0xbA58D93edD8343C001eC5f43E620712Ba8C10813', - value: 0.000001, // of 6 decimal places + value: 0.000001, // of 6 decimals nonce: -1 }); const balanceAfter = await ain.wallet.getBalance(); expect(balanceAfter).toBe(balanceBefore - 0.000001); @@ -290,7 +322,7 @@ describe('ain-js', function() { try { const response = await ain.wallet.transfer({ to: '0xbA58D93edD8343C001eC5f43E620712Ba8C10813', - value: 0.0000001, // of 7 decimal places + value: 0.0000001, // of 7 decimals nonce: -1 }); fail('should not happen'); } catch(e) { diff --git a/src/wallet.ts b/src/wallet.ts index 92f8d75..c4c0dab 100755 --- a/src/wallet.ts +++ b/src/wallet.ts @@ -256,6 +256,9 @@ export default class Wallet { transfer(input: {to: string, value: number, from?: string, nonce?: number, gas_price?: number}, isDryrun: boolean = false): Promise { const address = this.getImpliedAddress(input.from); const toAddress = Ain.utils.toChecksumAddress(input.to); + if (!(input.value > 0)) { + throw Error(`Non-positive transfer value.`); + } const numDecimalPlaces = Wallet.countDecimals(input.value); if (numDecimalPlaces > MAX_TRANSFERABLE_DECIMALS) { throw Error(`Transfer value of more than ${MAX_TRANSFERABLE_DECIMALS} decimal places.`); From 76e7c5a004d89a7485720661727164641fe8b753 Mon Sep 17 00:00:00 2001 From: Dongil Seo Date: Tue, 24 Oct 2023 21:36:25 +0900 Subject: [PATCH 05/31] Extend countDecimals() for negative values --- __tests__/ain.test.ts | 10 ++++++++++ src/wallet.ts | 8 ++++---- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/__tests__/ain.test.ts b/__tests__/ain.test.ts index 9f8a815..d3a260b 100644 --- a/__tests__/ain.test.ts +++ b/__tests__/ain.test.ts @@ -87,6 +87,8 @@ describe('ain-js', function() { expect(Wallet.countDecimals(100000000)).toBe(0); // '100000000' expect(Wallet.countDecimals(1000000000)).toBe(0); // '1000000000' expect(Wallet.countDecimals(1234567890)).toBe(0); // '1234567890' + expect(Wallet.countDecimals(-1)).toBe(0); // '-1' + expect(Wallet.countDecimals(-1000000000)).toBe(0); // '-1000000000' expect(Wallet.countDecimals(11)).toBe(0); // '11' expect(Wallet.countDecimals(101)).toBe(0); // '101' expect(Wallet.countDecimals(1001)).toBe(0); // '1001' @@ -96,6 +98,8 @@ describe('ain-js', function() { expect(Wallet.countDecimals(10000001)).toBe(0); // '10000001' expect(Wallet.countDecimals(100000001)).toBe(0); // '100000001' expect(Wallet.countDecimals(1000000001)).toBe(0); // '1000000001' + expect(Wallet.countDecimals(-11)).toBe(0); // '-11' + expect(Wallet.countDecimals(-1000000001)).toBe(0); // '-1000000001' expect(Wallet.countDecimals(0.1)).toBe(1); // '0.1' expect(Wallet.countDecimals(0.01)).toBe(2); // '0.01' expect(Wallet.countDecimals(0.001)).toBe(3); // '0.001' @@ -106,6 +110,8 @@ describe('ain-js', function() { expect(Wallet.countDecimals(0.00000001)).toBe(8); // '1e-8' expect(Wallet.countDecimals(0.000000001)).toBe(9); // '1e-9' expect(Wallet.countDecimals(0.0000000001)).toBe(10); // '1e-10' + expect(Wallet.countDecimals(-0.1)).toBe(1); // '-0.1' + expect(Wallet.countDecimals(-0.0000000001)).toBe(10); // '-1e-10' expect(Wallet.countDecimals(1.2)).toBe(1); // '1.2' expect(Wallet.countDecimals(0.12)).toBe(2); // '0.12' expect(Wallet.countDecimals(0.012)).toBe(3); // '0.012' @@ -116,6 +122,8 @@ describe('ain-js', function() { expect(Wallet.countDecimals(0.00000012)).toBe(8); // '1.2e-7' expect(Wallet.countDecimals(0.000000012)).toBe(9); // '1.2e-8' expect(Wallet.countDecimals(0.0000000012)).toBe(10); // '1.2e-9' + expect(Wallet.countDecimals(-1.2)).toBe(1); // '-1.2' + expect(Wallet.countDecimals(-0.0000000012)).toBe(10); // '-1.2e-9' expect(Wallet.countDecimals(1.03)).toBe(2); // '1.03' expect(Wallet.countDecimals(1.003)).toBe(3); // '1.003' expect(Wallet.countDecimals(1.0003)).toBe(4); // '1.0003' @@ -125,6 +133,8 @@ describe('ain-js', function() { expect(Wallet.countDecimals(1.00000003)).toBe(8); // '1.00000003' expect(Wallet.countDecimals(1.000000003)).toBe(9); // '1.000000003' expect(Wallet.countDecimals(1.0000000003)).toBe(10); // '1.0000000003' + expect(Wallet.countDecimals(-1.03)).toBe(2); // '-1.03' + expect(Wallet.countDecimals(-1.0000000003)).toBe(10); // '-1.0000000003' }); it('create', function() { diff --git a/src/wallet.ts b/src/wallet.ts index c4c0dab..2625653 100755 --- a/src/wallet.ts +++ b/src/wallet.ts @@ -60,12 +60,12 @@ export default class Wallet { } /** - * Counts the given number's decimal number. - * @param {number} value - * @returns {number} The decimal number. + * Counts the given number's decimals. + * @param {number} value The number. + * @returns {number} The decimal count. */ static countDecimals(value: number): number { - const decimalExponentRegex = /(\d*\.{0,1}\d*)e-(\d+)/gm; + const decimalExponentRegex = /^-{0,1}(\d*\.{0,1}\d*)e-(\d+)$/gm; if (Math.floor(value) === value) { return 0; From 36d2f663fbbb293d2e558c08d6a91e93b883c645 Mon Sep 17 00:00:00 2001 From: Dongil Seo Date: Wed, 25 Oct 2023 14:16:14 +0900 Subject: [PATCH 06/31] Tweak comments and names --- __tests__/ain.test.ts | 6 +++--- src/wallet.ts | 10 +++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/__tests__/ain.test.ts b/__tests__/ain.test.ts index d3a260b..ad86488 100644 --- a/__tests__/ain.test.ts +++ b/__tests__/ain.test.ts @@ -317,7 +317,7 @@ describe('ain-js', function() { } }); - it('transfer with a value of up to 6 decimal places', async function() { + it('transfer with a value of up to 6 decimals', async function() { const balanceBefore = await ain.wallet.getBalance(); const response = await ain.wallet.transfer({ to: '0xbA58D93edD8343C001eC5f43E620712Ba8C10813', @@ -327,7 +327,7 @@ describe('ain-js', function() { expect(balanceAfter).toBe(balanceBefore - 0.000001); }); - it('transfer with a value of more than 6 decimal places', async function() { + it('transfer with a value of more than 6 decimals', async function() { const balanceBefore = await ain.wallet.getBalance(); try { const response = await ain.wallet.transfer({ @@ -336,7 +336,7 @@ describe('ain-js', function() { nonce: -1 }); fail('should not happen'); } catch(e) { - expect(e.message).toBe('Transfer value of more than 6 decimal places.'); + expect(e.message).toBe('Transfer value of more than 6 decimals.'); } finally { const balanceAfter = await ain.wallet.getBalance(); expect(balanceAfter).toBe(balanceBefore); diff --git a/src/wallet.ts b/src/wallet.ts index 2625653..33f8cfc 100755 --- a/src/wallet.ts +++ b/src/wallet.ts @@ -2,8 +2,8 @@ import { Accounts, Account, TransactionBody, V3Keystore, V3KeystoreOptions } fro import Ain from './ain'; import { validateMnemonic, mnemonicToSeedSync } from 'bip39'; import Reference from './ain-db/ref'; -const AIN_HD_DERIVATION_PATH = "m/44'/412'/0'/0/"; /* default wallet address for AIN */ -const MAX_TRANSFERABLE_DECIMALS = 6; /* The maximum transferable decimal places of values */ +const AIN_HD_DERIVATION_PATH = "m/44'/412'/0'/0/"; // The hardware wallet derivation path of AIN +const MAX_TRANSFERABLE_DECIMALS = 6; // The maximum decimals of transferable values /** * A class for AI Network wallets. @@ -259,9 +259,9 @@ export default class Wallet { if (!(input.value > 0)) { throw Error(`Non-positive transfer value.`); } - const numDecimalPlaces = Wallet.countDecimals(input.value); - if (numDecimalPlaces > MAX_TRANSFERABLE_DECIMALS) { - throw Error(`Transfer value of more than ${MAX_TRANSFERABLE_DECIMALS} decimal places.`); + const decimalCount = Wallet.countDecimals(input.value); + if (decimalCount > MAX_TRANSFERABLE_DECIMALS) { + throw Error(`Transfer value of more than ${MAX_TRANSFERABLE_DECIMALS} decimals.`); } const transferRef = this.ain.db.ref(`/transfer/${address}/${toAddress}`).push() as Reference; return transferRef.setValue({ From dcb8cb279c267ad88ba30dd5764da4dd021e7337 Mon Sep 17 00:00:00 2001 From: Dongil Seo Date: Thu, 26 Oct 2023 13:33:54 +0900 Subject: [PATCH 07/31] Add a todo for hd derivation path --- src/wallet.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/wallet.ts b/src/wallet.ts index 33f8cfc..d5ca73c 100755 --- a/src/wallet.ts +++ b/src/wallet.ts @@ -2,6 +2,7 @@ import { Accounts, Account, TransactionBody, V3Keystore, V3KeystoreOptions } fro import Ain from './ain'; import { validateMnemonic, mnemonicToSeedSync } from 'bip39'; import Reference from './ain-db/ref'; +// TODO(platfowner): Migrate to Ethereum HD derivation path 'm/44'/60'/0'/0/'. const AIN_HD_DERIVATION_PATH = "m/44'/412'/0'/0/"; // The hardware wallet derivation path of AIN const MAX_TRANSFERABLE_DECIMALS = 6; // The maximum decimals of transferable values From e45c044b68a45bf55f01f79f160c45a9249df6fd Mon Sep 17 00:00:00 2001 From: platfowner Date: Thu, 22 Feb 2024 17:37:37 +0900 Subject: [PATCH 08/31] Add getStateProof() method --- __tests__/__snapshots__/ain.test.ts.snap | 47 ++++++++++++++++++++++++ __tests__/ain.test.ts | 11 ++++++ src/ain-db/ref.ts | 13 +++++++ src/types.ts | 7 ++++ 4 files changed, 78 insertions(+) diff --git a/__tests__/__snapshots__/ain.test.ts.snap b/__tests__/__snapshots__/ain.test.ts.snap index b476fa9..40d95dc 100644 --- a/__tests__/__snapshots__/ain.test.ts.snap +++ b/__tests__/__snapshots__/ain.test.ts.snap @@ -169,6 +169,53 @@ Object { } `; +exports[`ain-js Database getProofHash 1`] = `"0x88496dfee3566db91f487aa4cbf69a0c42a3e2a5d0a65bfd4897d699e8734785"`; + +exports[`ain-js Database getStateProof 1`] = ` +Object { + "#radix:6": Object { + "#radix_ph": "0xa06dd3aacf18938d94658591ce23d93e67f539748d3320c08cd873f038ea6e51", + }, + "#radix:7": Object { + "#radix:2756c6573": Object { + "#radix_ph": "0xbda5b6fd030d53bcd367787535cbcff5de449ab85565ddfa0cec3be0749318a4", + }, + "#radix:6616c756573": Object { + "#radix_ph": "0xd27dcab3ff99f783e2564dd324f642e2c1c88c69c37aa240fed6f27dbd591ec5", + "#state:values": Object { + "#radix:6": Object { + "#radix:1": Object { + "#radix_ph": "0x964ebfa47b1824c3ff0301dfa5f96923f8b4272a279d65b94198c5a299b07716", + }, + "#radix:26c6f636b636861696e5f706172616d73": Object { + "#radix_ph": "0xe10c863f031396b266a9747b2f5216983f76888d300db5cab3a262bec6e8f08a", + "#state:blockchain_params": Object { + "#state_ph": "0x88496dfee3566db91f487aa4cbf69a0c42a3e2a5d0a65bfd4897d699e8734785", + }, + }, + "#radix:36f6e73656e737573": Object { + "#radix_ph": "0x17243462699180f55e2aa8a29670052cc2cb12694554f088e5531a0f5ca20410", + }, + "#radix:46576656c6f70657273": Object { + "#radix_ph": "0x172e61328ba517136b218fa330bb5920cf8f3c42b1a93d0ae8b8f4f05bf13130", + }, + "#radix:d616e6167655f617070": Object { + "#radix_ph": "0x6e09a2bf321fedee02dd4dd24228332d82eda5e538aa6e8d1ccee26a7649a1c1", + }, + "#radix_ph": "0x6b4fce79db15e82c2e78e483ed8ac61188b81f02d10db65d63205ce374367d24", + }, + "#radix:7": Object { + "#radix_ph": "0x13148488ac9ca1ddba1b8ddc0bc76da61a288256f64f88250b505030df763a4b", + }, + "#state_ph": "0xa3459466b4f6d10efb63a825ff046338a4dc9fab8cf9b942ff7ff2ff840f5bc6", + }, + }, + "#radix_ph": "0x1f5f883f7da05be1e8763e515056f3712879b8b13c84d0dd7d62a3c4b1623349", + }, + "#state_ph": "0xa48820f13e509b091b1409da3108b383da642c80a1523851368f63029785e45a", +} +`; + exports[`ain-js Database matchFunction 1`] = ` Object { "matched_config": Object { diff --git a/__tests__/ain.test.ts b/__tests__/ain.test.ts index ad86488..36ea876 100644 --- a/__tests__/ain.test.ts +++ b/__tests__/ain.test.ts @@ -1240,6 +1240,17 @@ describe('ain-js', function() { }) }); + it('getStateProof', async function() { + await ain.db.ref('/values/blockchain_params').getStateProof() + .then(res => { + expect(res).toMatchSnapshot(); + }) + .catch(error => { + console.log("error:", error); + fail('should not happen'); + }) + }); + /*it('on and off', function(done) { try { ain.db.ref().on('value', (snap:any) => console.log) diff --git a/src/ain-db/ref.ts b/src/ain-db/ref.ts index e769764..f55601d 100755 --- a/src/ain-db/ref.ts +++ b/src/ain-db/ref.ts @@ -11,6 +11,7 @@ import { EvalRuleInput, EvalOwnerInput, MatchInput, + StateInfoInput, GetOptions, } from '../types'; import Ain from '../ain'; @@ -347,6 +348,18 @@ export default class Reference { return this._ain.provider.send('ain_matchOwner', request); } + /** + * Fetches the state proof of a global blockchain state path. + * @param {StateInfoInput} params The state info input. + * @returns {Promise} The return value of the blockchain API. + */ + getStateProof(params?: StateInfoInput): Promise { + const request = { + ref: Reference.extendPath(this.path, params ? params.ref : undefined) + } + return this._ain.provider.send('ain_getStateProof', request); + } + // TODO(liayoo): Add this function. ///** // * Attaches an listener for database events. diff --git a/src/types.ts b/src/types.ts index d8c8452..15fa14e 100755 --- a/src/types.ts +++ b/src/types.ts @@ -291,6 +291,13 @@ export interface MatchInput { is_global?: boolean; } +/** + * An interface for state information input. + */ +export interface StateInfoInput { + ref?: string; +} + /** * An interface for state usage information. */ From 5936543e20df43f0aed3123e1065ecbf86204a71 Mon Sep 17 00:00:00 2001 From: platfowner Date: Thu, 22 Feb 2024 18:00:29 +0900 Subject: [PATCH 09/31] Add getProofHash() method --- __tests__/__snapshots__/ain.test.ts.snap | 45 ------------------------ __tests__/ain.test.ts | 11 ++++++ src/ain-db/ref.ts | 12 +++++++ 3 files changed, 23 insertions(+), 45 deletions(-) diff --git a/__tests__/__snapshots__/ain.test.ts.snap b/__tests__/__snapshots__/ain.test.ts.snap index 40d95dc..799b26c 100644 --- a/__tests__/__snapshots__/ain.test.ts.snap +++ b/__tests__/__snapshots__/ain.test.ts.snap @@ -171,51 +171,6 @@ Object { exports[`ain-js Database getProofHash 1`] = `"0x88496dfee3566db91f487aa4cbf69a0c42a3e2a5d0a65bfd4897d699e8734785"`; -exports[`ain-js Database getStateProof 1`] = ` -Object { - "#radix:6": Object { - "#radix_ph": "0xa06dd3aacf18938d94658591ce23d93e67f539748d3320c08cd873f038ea6e51", - }, - "#radix:7": Object { - "#radix:2756c6573": Object { - "#radix_ph": "0xbda5b6fd030d53bcd367787535cbcff5de449ab85565ddfa0cec3be0749318a4", - }, - "#radix:6616c756573": Object { - "#radix_ph": "0xd27dcab3ff99f783e2564dd324f642e2c1c88c69c37aa240fed6f27dbd591ec5", - "#state:values": Object { - "#radix:6": Object { - "#radix:1": Object { - "#radix_ph": "0x964ebfa47b1824c3ff0301dfa5f96923f8b4272a279d65b94198c5a299b07716", - }, - "#radix:26c6f636b636861696e5f706172616d73": Object { - "#radix_ph": "0xe10c863f031396b266a9747b2f5216983f76888d300db5cab3a262bec6e8f08a", - "#state:blockchain_params": Object { - "#state_ph": "0x88496dfee3566db91f487aa4cbf69a0c42a3e2a5d0a65bfd4897d699e8734785", - }, - }, - "#radix:36f6e73656e737573": Object { - "#radix_ph": "0x17243462699180f55e2aa8a29670052cc2cb12694554f088e5531a0f5ca20410", - }, - "#radix:46576656c6f70657273": Object { - "#radix_ph": "0x172e61328ba517136b218fa330bb5920cf8f3c42b1a93d0ae8b8f4f05bf13130", - }, - "#radix:d616e6167655f617070": Object { - "#radix_ph": "0x6e09a2bf321fedee02dd4dd24228332d82eda5e538aa6e8d1ccee26a7649a1c1", - }, - "#radix_ph": "0x6b4fce79db15e82c2e78e483ed8ac61188b81f02d10db65d63205ce374367d24", - }, - "#radix:7": Object { - "#radix_ph": "0x13148488ac9ca1ddba1b8ddc0bc76da61a288256f64f88250b505030df763a4b", - }, - "#state_ph": "0xa3459466b4f6d10efb63a825ff046338a4dc9fab8cf9b942ff7ff2ff840f5bc6", - }, - }, - "#radix_ph": "0x1f5f883f7da05be1e8763e515056f3712879b8b13c84d0dd7d62a3c4b1623349", - }, - "#state_ph": "0xa48820f13e509b091b1409da3108b383da642c80a1523851368f63029785e45a", -} -`; - exports[`ain-js Database matchFunction 1`] = ` Object { "matched_config": Object { diff --git a/__tests__/ain.test.ts b/__tests__/ain.test.ts index 36ea876..4d56315 100644 --- a/__tests__/ain.test.ts +++ b/__tests__/ain.test.ts @@ -1242,6 +1242,17 @@ describe('ain-js', function() { it('getStateProof', async function() { await ain.db.ref('/values/blockchain_params').getStateProof() + .then(res => { + expect(res).not.toBeNull(); + }) + .catch(error => { + console.log("error:", error); + fail('should not happen'); + }) + }); + + it('getProofHash', async function() { + await ain.db.ref('/values/blockchain_params').getProofHash() .then(res => { expect(res).toMatchSnapshot(); }) diff --git a/src/ain-db/ref.ts b/src/ain-db/ref.ts index f55601d..16660f6 100755 --- a/src/ain-db/ref.ts +++ b/src/ain-db/ref.ts @@ -360,6 +360,18 @@ export default class Reference { return this._ain.provider.send('ain_getStateProof', request); } + /** + * Fetches the proof hash of a global blockchain state path. + * @param {StateInfoInput} params The state info input. + * @returns {Promise} The return value of the blockchain API. + */ + getProofHash(params?: StateInfoInput): Promise { + const request = { + ref: Reference.extendPath(this.path, params ? params.ref : undefined) + } + return this._ain.provider.send('ain_getProofHash', request); + } + // TODO(liayoo): Add this function. ///** // * Attaches an listener for database events. From c162f764717b9d9437eaa9dd01ccd267891f2a47 Mon Sep 17 00:00:00 2001 From: platfowner Date: Fri, 23 Feb 2024 15:07:37 +0900 Subject: [PATCH 10/31] Add getStateInfo() method --- __tests__/__snapshots__/ain.test.ts.snap | 12 ++++++++++++ __tests__/ain.test.ts | 13 ++++++++++++- __tests__/test_util.ts | 16 +++++++++++++--- src/ain-db/ref.ts | 12 ++++++++++++ 4 files changed, 49 insertions(+), 4 deletions(-) diff --git a/__tests__/__snapshots__/ain.test.ts.snap b/__tests__/__snapshots__/ain.test.ts.snap index 799b26c..1ef7e02 100644 --- a/__tests__/__snapshots__/ain.test.ts.snap +++ b/__tests__/__snapshots__/ain.test.ts.snap @@ -171,6 +171,18 @@ Object { exports[`ain-js Database getProofHash 1`] = `"0x88496dfee3566db91f487aa4cbf69a0c42a3e2a5d0a65bfd4897d699e8734785"`; +exports[`ain-js Database getStateInfo 1`] = ` +Object { + "#num_children": 1, + "#state_ph": "0x985a1f057d5047b1dee392127eb776571fbbe79da7ae6114f8f8f18c4f786135", + "#tree_bytes": 1840, + "#tree_height": 2, + "#tree_max_siblings": 1, + "#tree_size": 3, + "#version": "erased", +} +`; + exports[`ain-js Database matchFunction 1`] = ` Object { "matched_config": Object { diff --git a/__tests__/ain.test.ts b/__tests__/ain.test.ts index 4d56315..06d176a 100644 --- a/__tests__/ain.test.ts +++ b/__tests__/ain.test.ts @@ -3,7 +3,7 @@ import Ain from '../src/ain'; import Wallet from '../src/wallet'; import { TransactionBody, SetOperation, TransactionInput } from '../src/types'; import axios from 'axios'; -import { fail, eraseProtoVer } from './test_util'; +import { fail, eraseProtoVer, eraseStateVersion } from './test_util'; const { test_keystore, test_pw, @@ -1262,6 +1262,17 @@ describe('ain-js', function() { }) }); + it('getStateInfo', async function() { + await ain.db.ref('/rules/transfer/$from/$to/$key/value').getStateInfo() + .then(res => { + expect(eraseStateVersion(res)).toMatchSnapshot(); + }) + .catch(error => { + console.log("error:", error); + fail('should not happen'); + }) + }); + /*it('on and off', function(done) { try { ain.db.ref().on('value', (snap:any) => console.log) diff --git a/__tests__/test_util.ts b/__tests__/test_util.ts index 473ad7a..8b9dd47 100644 --- a/__tests__/test_util.ts +++ b/__tests__/test_util.ts @@ -1,6 +1,16 @@ +import { set } from 'lodash'; + export declare function fail(error?: any): never; -export function eraseProtoVer(retVal) { - retVal.protoVer = 'erased'; - return retVal; +export function eraseProtoVer(result) { + const erased = JSON.parse(JSON.stringify(result)); + set(erased, 'protoVer', 'erased'); + return erased; +} + +export function eraseStateVersion(result) { + const erased = JSON.parse(JSON.stringify(result)); + set(erased, '#version', 'erased'); + return erased; } + diff --git a/src/ain-db/ref.ts b/src/ain-db/ref.ts index 16660f6..43ac5d5 100755 --- a/src/ain-db/ref.ts +++ b/src/ain-db/ref.ts @@ -372,6 +372,18 @@ export default class Reference { return this._ain.provider.send('ain_getProofHash', request); } + /** + * Fetches the state information of a global blockchain state path. + * @param {StateInfoInput} params The state info input. + * @returns {Promise} The return value of the blockchain API. + */ + getStateInfo(params?: StateInfoInput): Promise { + const request = { + ref: Reference.extendPath(this.path, params ? params.ref : undefined) + } + return this._ain.provider.send('ain_getStateInfo', request); + } + // TODO(liayoo): Add this function. ///** // * Attaches an listener for database events. From 8fcf83d2b5892e66772ff7c02a5cda07b302c0d3 Mon Sep 17 00:00:00 2001 From: platfowner Date: Fri, 23 Feb 2024 18:14:27 +0900 Subject: [PATCH 11/31] Move getNonce() to wallet from signer --- __tests__/ain.test.ts | 5 +++++ src/signer/default-signer.ts | 23 +---------------------- src/wallet.ts | 20 ++++++++++++++++++++ 3 files changed, 26 insertions(+), 22 deletions(-) diff --git a/__tests__/ain.test.ts b/__tests__/ain.test.ts index 06d176a..5f3b51e 100644 --- a/__tests__/ain.test.ts +++ b/__tests__/ain.test.ts @@ -267,6 +267,11 @@ describe('ain-js', function() { expect(balance).toBeGreaterThan(0); }); + it('getNonce', async function() { + const nonce = await ain.wallet.getNonce(); + expect(nonce).toBeNull(); + }); + it('transfer with isDryrun = true', async function() { const balanceBefore = await ain.wallet.getBalance(); const response = await ain.wallet.transfer({ diff --git a/src/signer/default-signer.ts b/src/signer/default-signer.ts index cfa5a89..1936c43 100644 --- a/src/signer/default-signer.ts +++ b/src/signer/default-signer.ts @@ -124,31 +124,10 @@ export class DefaultSigner implements Signer { } let nonce = transactionInput.nonce; if (nonce === undefined) { - nonce = await this.getNonce({ address, from: "pending" }); + nonce = await this.wallet.getNonce({ address, from: "pending" }); } const timestamp = transactionInput.timestamp ? transactionInput.timestamp : Date.now(); const gasPrice = transactionInput.gas_price || 0; const billing = transactionInput.billing; return Object.assign(tx, { nonce, timestamp, gas_price: gasPrice, billing }); } - - /** - * Fetches an account's nonce value, which is the current transaction count of the account. - * @param {object} args The ferch options. - * It may contain a string 'address' value and a string 'from' value. - * The 'address' is the address of the account to get the nonce of, - * and the 'from' is the source of the data. - * It could be either the pending transaction pool ("pending") or - * the committed blocks ("committed"). The default value is "committed". - * @returns {Promise} The nonce value. - */ - getNonce(args: { address?: string, from?: string }): Promise { - if (!args) { args = {}; } - const address = args.address ? Ain.utils.toChecksumAddress(args.address) - : this.getAddress(args.address); - if (args.from !== undefined && args.from !== 'pending' && args.from !== 'committed') { - throw Error("'from' should be either 'pending' or 'committed'"); - } - return this.provider.send('ain_getNonce', { address, from: args.from }) - } -} \ No newline at end of file diff --git a/src/wallet.ts b/src/wallet.ts index d5ca73c..0361e3e 100755 --- a/src/wallet.ts +++ b/src/wallet.ts @@ -248,6 +248,26 @@ export default class Wallet { return this.ain.db.ref(`/accounts/${addr}/balance`).getValue(); } + /** + * Fetches an account's nonce value, which is the current transaction count of the account. + * @param {object} args The ferch options. + * It may contain a string 'address' value and a string 'from' value. + * The 'address' is the address of the account to get the nonce of, + * and the 'from' is the source of the data. + * It could be either the pending transaction pool ("pending") or + * the committed blocks ("committed"). The default value is "committed". + * @returns {Promise} The nonce value. + */ + getNonce(args: { address?: string, from?: string }): Promise { + if (!args) { args = {}; } + const address = args.address ? Ain.utils.toChecksumAddress(args.address) + : this.getImpliedAddress(args.address); + if (args.from !== undefined && args.from !== 'pending' && args.from !== 'committed') { + throw Error("'from' should be either 'pending' or 'committed'"); + } + return this.ain.provider.send('ain_getNonce', { address, from: args.from }) + } + /** * Sends a transfer transaction to the network. * @param {{to: string, value: number, from?: string, nonce?: number, gas_price?: number}} input The input parameters of the transaction. From c66214e9a55e313d1dfe6bb0415cba012cfbdeb3 Mon Sep 17 00:00:00 2001 From: platfowner Date: Fri, 23 Feb 2024 18:27:54 +0900 Subject: [PATCH 12/31] Add getTimestamp() method --- __tests__/ain.test.ts | 7 ++++++- src/signer/default-signer.ts | 1 + src/wallet.ts | 20 ++++++++++++++++++++ 3 files changed, 27 insertions(+), 1 deletion(-) diff --git a/__tests__/ain.test.ts b/__tests__/ain.test.ts index 5f3b51e..be2894a 100644 --- a/__tests__/ain.test.ts +++ b/__tests__/ain.test.ts @@ -269,7 +269,12 @@ describe('ain-js', function() { it('getNonce', async function() { const nonce = await ain.wallet.getNonce(); - expect(nonce).toBeNull(); + expect(nonce).toBe(0); + }); + + it('getTimestamp', async function() { + const timestamp = await ain.wallet.getTimestamp(); + expect(timestamp).toBe(0); }); it('transfer with isDryrun = true', async function() { diff --git a/src/signer/default-signer.ts b/src/signer/default-signer.ts index 1936c43..014f3bf 100644 --- a/src/signer/default-signer.ts +++ b/src/signer/default-signer.ts @@ -131,3 +131,4 @@ export class DefaultSigner implements Signer { const billing = transactionInput.billing; return Object.assign(tx, { nonce, timestamp, gas_price: gasPrice, billing }); } + }; diff --git a/src/wallet.ts b/src/wallet.ts index 0361e3e..12512e2 100755 --- a/src/wallet.ts +++ b/src/wallet.ts @@ -268,6 +268,26 @@ export default class Wallet { return this.ain.provider.send('ain_getNonce', { address, from: args.from }) } + /** + * Fetches an account's timestamp value, which is the current transaction count of the account. + * @param {object} args The ferch options. + * It may contain a string 'address' value and a string 'from' value. + * The 'address' is the address of the account to get the timestamp of, + * and the 'from' is the source of the data. + * It could be either the pending transaction pool ("pending") or + * the committed blocks ("committed"). The default value is "committed". + * @returns {Promise} The timestamp value. + */ + getTimestamp(args: { address?: string, from?: string }): Promise { + if (!args) { args = {}; } + const address = args.address ? Ain.utils.toChecksumAddress(args.address) + : this.getImpliedAddress(args.address); + if (args.from !== undefined && args.from !== 'pending' && args.from !== 'committed') { + throw Error("'from' should be either 'pending' or 'committed'"); + } + return this.ain.provider.send('ain_getTimestamp', { address, from: args.from }) + } + /** * Sends a transfer transaction to the network. * @param {{to: string, value: number, from?: string, nonce?: number, gas_price?: number}} input The input parameters of the transaction. From 1f450f92528d9fb56d9e69b59b597c4b0cc554d4 Mon Sep 17 00:00:00 2001 From: platfowner Date: Fri, 23 Feb 2024 18:44:24 +0900 Subject: [PATCH 13/31] Add getAddress() method --- __tests__/ain.test.ts | 7 +++++++ src/provider.ts | 8 ++++++++ 2 files changed, 15 insertions(+) diff --git a/__tests__/ain.test.ts b/__tests__/ain.test.ts index be2894a..1689557 100644 --- a/__tests__/ain.test.ts +++ b/__tests__/ain.test.ts @@ -73,6 +73,13 @@ describe('ain-js', function() { }); }); + describe('Provider', function() { + it('getAddress', async function() { + const address = await ain.provider.getAddress(); + expect(address).not.toBeNull(); + }); + }); + describe('Wallet', function() { it('countDecimals', function() { expect(Wallet.countDecimals(0)).toBe(0); // '0' diff --git a/src/provider.ts b/src/provider.ts index e1ec746..247a80e 100644 --- a/src/provider.ts +++ b/src/provider.ts @@ -37,6 +37,14 @@ export default class Provider { this.httpClient = axios.create(axiosConfig); } + /** + * Fetches the blockchain node's address. + * @returns {Promise} The address value. + */ + getAddress(): Promise { + return this.send('ain_getAddress', {}) + } + /** * Creates a JSON-RPC payload and sends it to the network. * @param {string} rpcMethod The JSON-RPC method. From e4062a0d373a6990857d77d6139112f11524fc1e Mon Sep 17 00:00:00 2001 From: platfowner Date: Mon, 26 Feb 2024 10:45:04 +0900 Subject: [PATCH 14/31] Rename: getTransaction -> getTransactionByHash --- __tests__/ain.test.ts | 8 ++++---- src/ain.ts | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/__tests__/ain.test.ts b/__tests__/ain.test.ts index 1689557..ad11a81 100644 --- a/__tests__/ain.test.ts +++ b/__tests__/ain.test.ts @@ -535,8 +535,8 @@ describe('ain-js', function() { }) }); - it('getTransaction for sendTransaction with isDryrun = true', async function () { - const tx = await ain.getTransaction(targetTxHash); + it('getTransactionByHash for sendTransaction with isDryrun = true', async function () { + const tx = await ain.getTransactionByHash(targetTxHash); expect(tx).toStrictEqual(null); // The tx is NOT in the blockchain. }); @@ -553,8 +553,8 @@ describe('ain-js', function() { }) }); - it('getTransaction for sendTransaction', async function () { - const tx = await ain.getTransaction(targetTxHash); + it('getTransactionByHash for sendTransaction', async function () { + const tx = await ain.getTransactionByHash(targetTxHash); expect(tx.transaction.tx_body.operation).toStrictEqual(targetTx); }); diff --git a/src/ain.ts b/src/ain.ts index 05b69c2..04e449e 100755 --- a/src/ain.ts +++ b/src/ain.ts @@ -129,7 +129,7 @@ export default class Ain { * @param {string} transactionHash The transaction hash. * @returns {Promise} */ - getTransaction(transactionHash: string): Promise { + getTransactionByHash(transactionHash: string): Promise { return this.provider.send('ain_getTransactionByHash', { hash: transactionHash }); } From ffed4f567c53343da2258fa5b104995c2b2ab12d Mon Sep 17 00:00:00 2001 From: platfowner Date: Mon, 26 Feb 2024 11:32:26 +0900 Subject: [PATCH 15/31] Add getTransactionByBlockHashAndIndex() method --- __tests__/ain.test.ts | 7 +++++++ src/ain.ts | 9 +++++++++ 2 files changed, 16 insertions(+) diff --git a/__tests__/ain.test.ts b/__tests__/ain.test.ts index ad11a81..240492f 100644 --- a/__tests__/ain.test.ts +++ b/__tests__/ain.test.ts @@ -781,6 +781,13 @@ describe('ain-js', function() { expect(thrownError.code).toEqual(30401); expect(thrownError.message).toEqual('Invalid batch transaction format.'); }); + + it('getTransactionByBlockHashAndIndex', async function () { + // TODO(platfowner): Migrate to getLastBlock(). + const genesisBlockHash = '0x31075a91beeea98fe8030c848d40b592411f2533c77d347d7937be84eae83745'; + const tx = await ain.getTransactionByBlockHashAndIndex(genesisBlockHash, 0); + expect(tx).not.toBeNull(); + }); }); describe('Database', function() { diff --git a/src/ain.ts b/src/ain.ts index 04e449e..acf9595 100755 --- a/src/ain.ts +++ b/src/ain.ts @@ -133,6 +133,15 @@ export default class Ain { return this.provider.send('ain_getTransactionByHash', { hash: transactionHash }); } + /** + * Fetches a transaction's information with a block hash and an index. + * @param {string} transactionHash The transaction hash. + * @returns {Promise} + */ + getTransactionByBlockHashAndIndex(blockHash: string, index: Number): Promise { + return this.provider.send('ain_getTransactionByBlockHashAndIndex', { block_hash: blockHash, index }); + } + /** * Fetches a blockchain app's state usage information with an app name. * @param {string} appName The blockchain app name. From 5f4cac8ecc3d3f9b96ffaca957f7109d05f10644 Mon Sep 17 00:00:00 2001 From: platfowner Date: Mon, 26 Feb 2024 11:50:12 +0900 Subject: [PATCH 16/31] Add getTransactionByBlockNumberAndIndex() method --- __tests__/ain.test.ts | 6 ++++++ src/ain.ts | 9 +++++++++ 2 files changed, 15 insertions(+) diff --git a/__tests__/ain.test.ts b/__tests__/ain.test.ts index 240492f..fee08f7 100644 --- a/__tests__/ain.test.ts +++ b/__tests__/ain.test.ts @@ -788,6 +788,12 @@ describe('ain-js', function() { const tx = await ain.getTransactionByBlockHashAndIndex(genesisBlockHash, 0); expect(tx).not.toBeNull(); }); + + it('getTransactionByBlockNumberAndIndex', async function () { + const genesisBlockNumber = 0; + const tx = await ain.getTransactionByBlockNumberAndIndex(genesisBlockNumber, 0); + expect(tx).not.toBeNull(); + }); }); describe('Database', function() { diff --git a/src/ain.ts b/src/ain.ts index acf9595..28de9f5 100755 --- a/src/ain.ts +++ b/src/ain.ts @@ -141,6 +141,15 @@ export default class Ain { getTransactionByBlockHashAndIndex(blockHash: string, index: Number): Promise { return this.provider.send('ain_getTransactionByBlockHashAndIndex', { block_hash: blockHash, index }); } + + /** + * Fetches a transaction's information with a block hash and an index. + * @param {string} transactionHash The transaction hash. + * @returns {Promise} + */ + getTransactionByBlockNumberAndIndex(blockNumber: Number, index: Number): Promise { + return this.provider.send('ain_getTransactionByBlockNumberAndIndex', { block_number: blockNumber, index }); + } /** * Fetches a blockchain app's state usage information with an app name. From 876094d41939caf314de65d41eb62170a3e62074 Mon Sep 17 00:00:00 2001 From: platfowner Date: Mon, 26 Feb 2024 11:59:04 +0900 Subject: [PATCH 17/31] Add getPendingTransactions() method --- __tests__/ain.test.ts | 5 +++++ src/ain.ts | 13 +++++++++++-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/__tests__/ain.test.ts b/__tests__/ain.test.ts index fee08f7..73e78e9 100644 --- a/__tests__/ain.test.ts +++ b/__tests__/ain.test.ts @@ -782,6 +782,11 @@ describe('ain-js', function() { expect(thrownError.message).toEqual('Invalid batch transaction format.'); }); + it('getPendingTransactions', async function () { + const txs = await ain.getPendingTransactions(); + expect(txs).not.toBeNull(); + }); + it('getTransactionByBlockHashAndIndex', async function () { // TODO(platfowner): Migrate to getLastBlock(). const genesisBlockHash = '0x31075a91beeea98fe8030c848d40b592411f2533c77d347d7937be84eae83745'; diff --git a/src/ain.ts b/src/ain.ts index 28de9f5..ace2e8e 100755 --- a/src/ain.ts +++ b/src/ain.ts @@ -124,6 +124,13 @@ export default class Ain { {[byHash ? 'hash' : 'number']: blockHashOrBlockNumber}); } + /** + * Fetches pending transaction. + * @returns {Promise} + */ + getPendingTransactions(): Promise { + return this.provider.send('ain_getPendingTransactions', {}); + } /** * Fetches a transaction's information with a transaction hash. * @param {string} transactionHash The transaction hash. @@ -135,7 +142,8 @@ export default class Ain { /** * Fetches a transaction's information with a block hash and an index. - * @param {string} transactionHash The transaction hash. + * @param {string} blockHash The block hash. + * @param {number} index The transaction index in the block * @returns {Promise} */ getTransactionByBlockHashAndIndex(blockHash: string, index: Number): Promise { @@ -144,7 +152,8 @@ export default class Ain { /** * Fetches a transaction's information with a block hash and an index. - * @param {string} transactionHash The transaction hash. + * @param {string} blockNumber The block number. + * @param {number} index The transaction index in the block * @returns {Promise} */ getTransactionByBlockNumberAndIndex(blockNumber: Number, index: Number): Promise { From 99bd536d713bc1523185db28d7df83cf897cb124 Mon Sep 17 00:00:00 2001 From: platfowner Date: Mon, 26 Feb 2024 12:05:53 +0900 Subject: [PATCH 18/31] Add getTransactionPoolSizeUtilization() method --- __tests__/ain.test.ts | 5 +++++ src/ain.ts | 9 +++++++++ 2 files changed, 14 insertions(+) diff --git a/__tests__/ain.test.ts b/__tests__/ain.test.ts index 73e78e9..79493b5 100644 --- a/__tests__/ain.test.ts +++ b/__tests__/ain.test.ts @@ -787,6 +787,11 @@ describe('ain-js', function() { expect(txs).not.toBeNull(); }); + it('getTransactionPoolSizeUtilization', async function () { + const txs = await ain.getTransactionPoolSizeUtilization(); + expect(txs).not.toBeNull(); + }); + it('getTransactionByBlockHashAndIndex', async function () { // TODO(platfowner): Migrate to getLastBlock(). const genesisBlockHash = '0x31075a91beeea98fe8030c848d40b592411f2533c77d347d7937be84eae83745'; diff --git a/src/ain.ts b/src/ain.ts index ace2e8e..be0f7aa 100755 --- a/src/ain.ts +++ b/src/ain.ts @@ -131,6 +131,15 @@ export default class Ain { getPendingTransactions(): Promise { return this.provider.send('ain_getPendingTransactions', {}); } + + /** + * Fetches transaction pool size utilization. + * @returns {Promise} + */ + getTransactionPoolSizeUtilization(): Promise { + return this.provider.send('ain_getTransactionPoolSizeUtilization', {}); + } + /** * Fetches a transaction's information with a transaction hash. * @param {string} transactionHash The transaction hash. From bd8d90dfdc30dd219d8eef5b337256391711eefc Mon Sep 17 00:00:00 2001 From: platfowner Date: Mon, 26 Feb 2024 12:34:34 +0900 Subject: [PATCH 19/31] Update test with getBlock() method call --- __tests__/ain.test.ts | 6 +++--- src/ain.ts | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/__tests__/ain.test.ts b/__tests__/ain.test.ts index 79493b5..7a34292 100644 --- a/__tests__/ain.test.ts +++ b/__tests__/ain.test.ts @@ -793,9 +793,9 @@ describe('ain-js', function() { }); it('getTransactionByBlockHashAndIndex', async function () { - // TODO(platfowner): Migrate to getLastBlock(). - const genesisBlockHash = '0x31075a91beeea98fe8030c848d40b592411f2533c77d347d7937be84eae83745'; - const tx = await ain.getTransactionByBlockHashAndIndex(genesisBlockHash, 0); + const genesisBlockNumber = 0; + const genesisBlock = await ain.getBlock(genesisBlockNumber); + const tx = await ain.getTransactionByBlockHashAndIndex(genesisBlock.hash, 0); expect(tx).not.toBeNull(); }); diff --git a/src/ain.ts b/src/ain.ts index be0f7aa..d2abcfb 100755 --- a/src/ain.ts +++ b/src/ain.ts @@ -126,17 +126,17 @@ export default class Ain { /** * Fetches pending transaction. - * @returns {Promise} + * @returns {Promise} */ - getPendingTransactions(): Promise { + getPendingTransactions(): Promise { return this.provider.send('ain_getPendingTransactions', {}); } /** * Fetches transaction pool size utilization. - * @returns {Promise} + * @returns {Promise} */ - getTransactionPoolSizeUtilization(): Promise { + getTransactionPoolSizeUtilization(): Promise { return this.provider.send('ain_getTransactionPoolSizeUtilization', {}); } From aaa43850937877093acd77ea9fff29c09a603089 Mon Sep 17 00:00:00 2001 From: platfowner Date: Mon, 26 Feb 2024 16:39:55 +0900 Subject: [PATCH 20/31] Add getLastBlock() method --- __tests__/ain.test.ts | 7 +++++++ src/ain.ts | 8 ++++++++ 2 files changed, 15 insertions(+) diff --git a/__tests__/ain.test.ts b/__tests__/ain.test.ts index 7a34292..ccfd9a3 100644 --- a/__tests__/ain.test.ts +++ b/__tests__/ain.test.ts @@ -478,6 +478,13 @@ describe('ain-js', function() { await waitUntilTxFinalized(createApps.result.tx_hash); }); + it('getLastBlock', async function () { + const block = await ain.getLastBlock() + expect(block).not.toBeNull(); + expect(block.hash).not.toBeNull(); + expect(block.number).not.toBeNull(); + }); + it('getBlock', async function () { const block = await ain.getBlock(3) const hash = block.hash || ""; diff --git a/src/ain.ts b/src/ain.ts index d2abcfb..48e0a84 100755 --- a/src/ain.ts +++ b/src/ain.ts @@ -84,6 +84,14 @@ export default class Ain { this.signer = signer; } + /** + * Fetches the last block. + * @returns {Promise} + */ + getLastBlock(): Promise { + return this.provider.send('ain_getLastBlock', {}); + } + /** * Fetches a block with a block hash or block number. * @param {string | number} blockHashOrBlockNumber The block hash or block number. From f34ac27db2a96cf1d553aed57b2a37c654a5784b Mon Sep 17 00:00:00 2001 From: platfowner Date: Tue, 27 Feb 2024 10:16:03 +0900 Subject: [PATCH 21/31] Add getLastBlockNumber() method --- __tests__/ain.test.ts | 6 ++++++ src/ain.ts | 8 ++++++++ 2 files changed, 14 insertions(+) diff --git a/__tests__/ain.test.ts b/__tests__/ain.test.ts index ccfd9a3..6f82d9d 100644 --- a/__tests__/ain.test.ts +++ b/__tests__/ain.test.ts @@ -485,6 +485,12 @@ describe('ain-js', function() { expect(block.number).not.toBeNull(); }); + it('getLastBlockNumber', async function () { + const number = await ain.getLastBlockNumber() + expect(number).not.toBeNull(); + expect(number).toBeGreaterThan(0); + }); + it('getBlock', async function () { const block = await ain.getBlock(3) const hash = block.hash || ""; diff --git a/src/ain.ts b/src/ain.ts index 48e0a84..ab26a52 100755 --- a/src/ain.ts +++ b/src/ain.ts @@ -92,6 +92,14 @@ export default class Ain { return this.provider.send('ain_getLastBlock', {}); } + /** + * Fetches the last block number. + * @returns {Promise} + */ + getLastBlockNumber(): Promise { + return this.provider.send('ain_getLastBlockNumber', {}); + } + /** * Fetches a block with a block hash or block number. * @param {string | number} blockHashOrBlockNumber The block hash or block number. From 143c44b1c96cbd2c63ae54d0dd996a049f59c903 Mon Sep 17 00:00:00 2001 From: platfowner Date: Tue, 27 Feb 2024 10:18:04 +0900 Subject: [PATCH 22/31] Split getBlock() method to getBlockByNumber() and getBlockByHash() --- __tests__/ain.test.ts | 30 ++++++++++++++++++++++-------- src/ain.ts | 28 +++++++++++++++++++--------- 2 files changed, 41 insertions(+), 17 deletions(-) diff --git a/__tests__/ain.test.ts b/__tests__/ain.test.ts index 6f82d9d..1396d72 100644 --- a/__tests__/ain.test.ts +++ b/__tests__/ain.test.ts @@ -482,7 +482,7 @@ describe('ain-js', function() { const block = await ain.getLastBlock() expect(block).not.toBeNull(); expect(block.hash).not.toBeNull(); - expect(block.number).not.toBeNull(); + expect(block.number).toBeGreaterThan(0); }); it('getLastBlockNumber', async function () { @@ -491,21 +491,35 @@ describe('ain-js', function() { expect(number).toBeGreaterThan(0); }); - it('getBlock', async function () { - const block = await ain.getBlock(3) - const hash = block.hash || ""; - expect(await ain.getBlock(hash)).toStrictEqual(block); + it('getBlockByNumber', async function () { + const lastBlock = await ain.getLastBlock() + expect(lastBlock).not.toBeNull(); + expect(lastBlock.number).not.toBeNull(); + const block = await ain.getBlockByNumber(lastBlock.number) + expect(block).not.toBeNull(); + expect(block.number).toBe(lastBlock.number); + expect(block.hash).toBe(lastBlock.hash); + }); + + it('getBlockByHash', async function () { + const lastBlock = await ain.getLastBlock() + expect(lastBlock).not.toBeNull(); + expect(lastBlock.hash).not.toBeNull(); + const block = await ain.getBlockByHash(lastBlock.hash) + expect(block).not.toBeNull(); + expect(block.number).toBe(lastBlock.number); + expect(block.hash).toBe(lastBlock.hash); }); it('getProposer', async function () { const proposer = await ain.getProposer(1); - const hash = (await ain.getBlock(1)).hash || ""; + const hash = (await ain.getBlockByNumber(1)).hash || ""; expect(await ain.getProposer(hash)).toBe(proposer); }); it('getValidators', async function () { const validators = await ain.getValidators(4); - const hash = (await ain.getBlock(4)).hash || ""; + const hash = (await ain.getBlockByNumber(4)).hash || ""; expect(await ain.getValidators(hash)).toStrictEqual(validators); }); @@ -807,7 +821,7 @@ describe('ain-js', function() { it('getTransactionByBlockHashAndIndex', async function () { const genesisBlockNumber = 0; - const genesisBlock = await ain.getBlock(genesisBlockNumber); + const genesisBlock = await ain.getBlockByNumber(genesisBlockNumber); const tx = await ain.getTransactionByBlockHashAndIndex(genesisBlock.hash, 0); expect(tx).not.toBeNull(); }); diff --git a/src/ain.ts b/src/ain.ts index ab26a52..3b2cdd3 100755 --- a/src/ain.ts +++ b/src/ain.ts @@ -101,19 +101,29 @@ export default class Ain { } /** - * Fetches a block with a block hash or block number. - * @param {string | number} blockHashOrBlockNumber The block hash or block number. + * Fetches a block with a block number. + * @param {number} blockNumber The block number. * @param {boolean} returnTransactionObjects If it's true, returns a block with full transaction objects. * Otherwise, returns a block with only transaction hashes. * @returns {Promise} */ - getBlock(blockHashOrBlockNumber: string | number, returnTransactionObjects?: boolean): Promise { - const byHash = typeof blockHashOrBlockNumber === 'string' - const rpcMethod = byHash ? 'ain_getBlockByHash' : 'ain_getBlockByNumber'; - const data = Object.assign({}, - { getFullTransactions: !!returnTransactionObjects, - [byHash ? 'hash' : 'number']: blockHashOrBlockNumber }); - return this.provider.send(rpcMethod, data); + getBlockByNumber(blockNumber: number, returnTransactionObjects?: boolean): Promise { + const data = + Object.assign({}, { getFullTransactions: !!returnTransactionObjects, number: blockNumber }); + return this.provider.send('ain_getBlockByNumber', data); + } + + /** + * Fetches a block with a block hash. + * @param {string} blockHash The block hash. + * @param {boolean} returnTransactionObjects If it's true, returns a block with full transaction objects. + * Otherwise, returns a block with only transaction hashes. + * @returns {Promise} + */ + getBlockByHash(blockHash: string, returnTransactionObjects?: boolean): Promise { + const data = + Object.assign({}, { getFullTransactions: !!returnTransactionObjects, hash: blockHash }); + return this.provider.send('ain_getBlockByHash', data); } /** From 6bfbc8cefe2d7d83f9f70a9745564d02dd236caa Mon Sep 17 00:00:00 2001 From: platfowner Date: Tue, 27 Feb 2024 13:58:50 +0900 Subject: [PATCH 23/31] Add getBlockList() method --- __tests__/ain.test.ts | 15 +++++++++++++-- src/ain.ts | 11 +++++++++++ 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/__tests__/ain.test.ts b/__tests__/ain.test.ts index 1396d72..2752e38 100644 --- a/__tests__/ain.test.ts +++ b/__tests__/ain.test.ts @@ -492,7 +492,7 @@ describe('ain-js', function() { }); it('getBlockByNumber', async function () { - const lastBlock = await ain.getLastBlock() + const lastBlock = await ain.getLastBlock(); expect(lastBlock).not.toBeNull(); expect(lastBlock.number).not.toBeNull(); const block = await ain.getBlockByNumber(lastBlock.number) @@ -502,7 +502,7 @@ describe('ain-js', function() { }); it('getBlockByHash', async function () { - const lastBlock = await ain.getLastBlock() + const lastBlock = await ain.getLastBlock(); expect(lastBlock).not.toBeNull(); expect(lastBlock.hash).not.toBeNull(); const block = await ain.getBlockByHash(lastBlock.hash) @@ -511,6 +511,17 @@ describe('ain-js', function() { expect(block.hash).toBe(lastBlock.hash); }); + it('getBlockList', async function () { + const lastBlockNumber = await ain.getLastBlockNumber(); + expect(lastBlockNumber).not.toBeNull(); + expect(lastBlockNumber).toBeGreaterThanOrEqual(0); + const blockList = await ain.getBlockList(lastBlockNumber - 1, lastBlockNumber + 1) + expect(blockList).not.toBeNull(); + expect(blockList.length).toBe(2); + expect(blockList[0].number).toBe(lastBlockNumber - 1); + expect(blockList[1].number).toBe(lastBlockNumber); + }); + it('getProposer', async function () { const proposer = await ain.getProposer(1); const hash = (await ain.getBlockByNumber(1)).hash || ""; diff --git a/src/ain.ts b/src/ain.ts index 3b2cdd3..36e3111 100755 --- a/src/ain.ts +++ b/src/ain.ts @@ -126,6 +126,17 @@ export default class Ain { return this.provider.send('ain_getBlockByHash', data); } + /** + * Fetches blocks with a block number range. + * @param {number} from The begining block number (inclusive). + * @param {number} to The ending block number (exclusive). + * Otherwise, returns a block with only transaction hashes. + * @returns {Promise>} + */ + getBlockList(from: number, to: number): Promise> { + return this.provider.send('ain_getBlockList', { from, to }); + } + /** * Fetches the forger's address of a block with a block hash or block number. * @param {string | number} blockHashOrBlockNumber The block hash or block number. From ca03856c9cc8a39eb0a89c84f4dfd885a0cb5e2a Mon Sep 17 00:00:00 2001 From: platfowner Date: Wed, 28 Feb 2024 10:07:38 +0900 Subject: [PATCH 24/31] Add getBlockHeadersList() method --- __tests__/ain.test.ts | 11 +++++++++++ src/ain.ts | 11 +++++++++++ 2 files changed, 22 insertions(+) diff --git a/__tests__/ain.test.ts b/__tests__/ain.test.ts index 2752e38..b1dd907 100644 --- a/__tests__/ain.test.ts +++ b/__tests__/ain.test.ts @@ -522,6 +522,17 @@ describe('ain-js', function() { expect(blockList[1].number).toBe(lastBlockNumber); }); + it('getBlockHeadersList', async function () { + const lastBlockNumber = await ain.getLastBlockNumber(); + expect(lastBlockNumber).not.toBeNull(); + expect(lastBlockNumber).toBeGreaterThanOrEqual(0); + const blockList = await ain.getBlockHeadersList(lastBlockNumber - 1, lastBlockNumber + 1) + expect(blockList).not.toBeNull(); + expect(blockList.length).toBe(2); + expect(blockList[0].number).toBe(lastBlockNumber - 1); + expect(blockList[1].number).toBe(lastBlockNumber); + }); + it('getProposer', async function () { const proposer = await ain.getProposer(1); const hash = (await ain.getBlockByNumber(1)).hash || ""; diff --git a/src/ain.ts b/src/ain.ts index 36e3111..f2ef4fa 100755 --- a/src/ain.ts +++ b/src/ain.ts @@ -137,6 +137,17 @@ export default class Ain { return this.provider.send('ain_getBlockList', { from, to }); } + /** + * Fetches block headers with a block number range. + * @param {number} from The begining block number (inclusive). + * @param {number} to The ending block number (exclusive). + * Otherwise, returns a block with only transaction hashes. + * @returns {Promise>} + */ + getBlockHeadersList(from: number, to: number): Promise> { + return this.provider.send('ain_getBlockHeadersList', { from, to }); + } + /** * Fetches the forger's address of a block with a block hash or block number. * @param {string | number} blockHashOrBlockNumber The block hash or block number. From 70aaafbddb07e4506ceecb2ccabb0f4b47c94987 Mon Sep 17 00:00:00 2001 From: platfowner Date: Wed, 28 Feb 2024 10:18:51 +0900 Subject: [PATCH 25/31] Add getBlockTransactionCountByNumber() method --- __tests__/ain.test.ts | 8 ++++++++ src/ain.ts | 11 +++++++++-- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/__tests__/ain.test.ts b/__tests__/ain.test.ts index b1dd907..1b96216 100644 --- a/__tests__/ain.test.ts +++ b/__tests__/ain.test.ts @@ -533,6 +533,14 @@ describe('ain-js', function() { expect(blockList[1].number).toBe(lastBlockNumber); }); + it('getBlockTransactionCountByNumber', async function () { + const lastBlockNumber = await ain.getLastBlockNumber(); + expect(lastBlockNumber).not.toBeNull(); + expect(lastBlockNumber).toBeGreaterThanOrEqual(0); + const txCount = await ain.getBlockTransactionCountByNumber(lastBlockNumber) + expect(txCount).not.toBeNull(); + }); + it('getProposer', async function () { const proposer = await ain.getProposer(1); const hash = (await ain.getBlockByNumber(1)).hash || ""; diff --git a/src/ain.ts b/src/ain.ts index f2ef4fa..9409e7a 100755 --- a/src/ain.ts +++ b/src/ain.ts @@ -130,7 +130,6 @@ export default class Ain { * Fetches blocks with a block number range. * @param {number} from The begining block number (inclusive). * @param {number} to The ending block number (exclusive). - * Otherwise, returns a block with only transaction hashes. * @returns {Promise>} */ getBlockList(from: number, to: number): Promise> { @@ -141,13 +140,21 @@ export default class Ain { * Fetches block headers with a block number range. * @param {number} from The begining block number (inclusive). * @param {number} to The ending block number (exclusive). - * Otherwise, returns a block with only transaction hashes. * @returns {Promise>} */ getBlockHeadersList(from: number, to: number): Promise> { return this.provider.send('ain_getBlockHeadersList', { from, to }); } + /** + * Fetches block transaction count with a block number. + * @param {number} number The block number. + * @returns {Promise} + */ + getBlockTransactionCountByNumber(number: number): Promise { + return this.provider.send('ain_getBlockTransactionCountByNumber', { number }); + } + /** * Fetches the forger's address of a block with a block hash or block number. * @param {string | number} blockHashOrBlockNumber The block hash or block number. From d3b6ab01651bf7baff62f1bc4b1ebea6eeecd817 Mon Sep 17 00:00:00 2001 From: platfowner Date: Wed, 28 Feb 2024 10:26:25 +0900 Subject: [PATCH 26/31] Add getBlockTransactionCountByHash() method --- __tests__/ain.test.ts | 8 ++++++++ src/ain.ts | 9 +++++++++ 2 files changed, 17 insertions(+) diff --git a/__tests__/ain.test.ts b/__tests__/ain.test.ts index 1b96216..b7e7a0d 100644 --- a/__tests__/ain.test.ts +++ b/__tests__/ain.test.ts @@ -541,6 +541,14 @@ describe('ain-js', function() { expect(txCount).not.toBeNull(); }); + it('getBlockTransactionCountByHash', async function () { + const lastBlock= await ain.getLastBlock(); + expect(lastBlock).not.toBeNull(); + expect(lastBlock.hash).not.toBeNull(); + const txCount = await ain.getBlockTransactionCountByHash(lastBlock.hash) + expect(txCount).not.toBeNull(); + }); + it('getProposer', async function () { const proposer = await ain.getProposer(1); const hash = (await ain.getBlockByNumber(1)).hash || ""; diff --git a/src/ain.ts b/src/ain.ts index 9409e7a..36f5bc8 100755 --- a/src/ain.ts +++ b/src/ain.ts @@ -155,6 +155,15 @@ export default class Ain { return this.provider.send('ain_getBlockTransactionCountByNumber', { number }); } + /** + * Fetches block transaction count with a block hash. + * @param {string} hash The block hash. + * @returns {Promise} + */ + getBlockTransactionCountByHash(hash: string): Promise { + return this.provider.send('ain_getBlockTransactionCountByHash', { hash }); + } + /** * Fetches the forger's address of a block with a block hash or block number. * @param {string | number} blockHashOrBlockNumber The block hash or block number. From 250a214a73415ad484c4df09cd7dc0ee1b206091 Mon Sep 17 00:00:00 2001 From: platfowner Date: Mon, 4 Mar 2024 14:57:29 +0900 Subject: [PATCH 27/31] Split getProposer() to getProposerByNumber() and getProposerByHash() --- __tests__/ain.test.ts | 18 ++++++++++++++---- src/ain.ts | 20 +++++++++++++------- 2 files changed, 27 insertions(+), 11 deletions(-) diff --git a/__tests__/ain.test.ts b/__tests__/ain.test.ts index b7e7a0d..df56451 100644 --- a/__tests__/ain.test.ts +++ b/__tests__/ain.test.ts @@ -549,10 +549,20 @@ describe('ain-js', function() { expect(txCount).not.toBeNull(); }); - it('getProposer', async function () { - const proposer = await ain.getProposer(1); - const hash = (await ain.getBlockByNumber(1)).hash || ""; - expect(await ain.getProposer(hash)).toBe(proposer); + it('getProposerByNumber', async function () { + const lastBlock = await ain.getLastBlock(); + expect(lastBlock).not.toBeNull(); + expect(lastBlock.number).not.toBeNull(); + const proposer = await ain.getProposerByNumber(lastBlock.number); + expect(proposer).toBe(lastBlock.proposer); + }); + + it('getProposerByHash', async function () { + const lastBlock = await ain.getLastBlock(); + expect(lastBlock).not.toBeNull(); + expect(lastBlock.hash).not.toBeNull(); + const proposer = await ain.getProposerByHash(lastBlock.hash); + expect(proposer).toBe(lastBlock.proposer); }); it('getValidators', async function () { diff --git a/src/ain.ts b/src/ain.ts index 36f5bc8..86555bb 100755 --- a/src/ain.ts +++ b/src/ain.ts @@ -165,15 +165,21 @@ export default class Ain { } /** - * Fetches the forger's address of a block with a block hash or block number. - * @param {string | number} blockHashOrBlockNumber The block hash or block number. + * Fetches the forger's address of a block with a block number. + * @param {number} blockNumber The block number. * @returns {Promise} */ - getProposer(blockHashOrBlockNumber: string | number): Promise { - const byHash = typeof blockHashOrBlockNumber === 'string' - const rpcMethod = byHash ? 'ain_getProposerByHash' : 'ain_getProposerByNumber'; - return this.provider.send(rpcMethod, - {[byHash ? 'hash' : 'number']: blockHashOrBlockNumber}); + getProposerByNumber(blockNumber: number): Promise { + return this.provider.send('ain_getProposerByNumber', { number: blockNumber }); + } + + /** + * Fetches the forger's address of a block with a block hash. + * @param {string} blockHash The block hash. + * @returns {Promise} + */ + getProposerByHash(blockHash: string): Promise { + return this.provider.send('ain_getProposerByHash', { hash: blockHash }); } /** From b734f7a4d5687483c8d49fd5dbb027b1c4467bce Mon Sep 17 00:00:00 2001 From: platfowner Date: Mon, 4 Mar 2024 15:14:05 +0900 Subject: [PATCH 28/31] Split getValidators() to getValidatorsByNumber() and getValidatorsByHash() --- __tests__/ain.test.ts | 22 ++++++++++++++++------ src/ain.ts | 30 ++++++++++++++++++------------ 2 files changed, 34 insertions(+), 18 deletions(-) diff --git a/__tests__/ain.test.ts b/__tests__/ain.test.ts index df56451..99b8ae6 100644 --- a/__tests__/ain.test.ts +++ b/__tests__/ain.test.ts @@ -549,6 +549,22 @@ describe('ain-js', function() { expect(txCount).not.toBeNull(); }); + it('getValidatorsByNumber', async function () { + const lastBlock = await ain.getLastBlock(); + expect(lastBlock).not.toBeNull(); + expect(lastBlock.number).not.toBeNull(); + const validators = await ain.getValidatorsByNumber(lastBlock.number); + expect(validators).toStrictEqual(lastBlock.validators); + }); + + it('getValidatorsByHash', async function () { + const lastBlock = await ain.getLastBlock(); + expect(lastBlock).not.toBeNull(); + expect(lastBlock.number).not.toBeNull(); + const validators = await ain.getValidatorsByHash(lastBlock.hash); + expect(validators).toStrictEqual(lastBlock.validators); + }); + it('getProposerByNumber', async function () { const lastBlock = await ain.getLastBlock(); expect(lastBlock).not.toBeNull(); @@ -565,12 +581,6 @@ describe('ain-js', function() { expect(proposer).toBe(lastBlock.proposer); }); - it('getValidators', async function () { - const validators = await ain.getValidators(4); - const hash = (await ain.getBlockByNumber(4)).hash || ""; - expect(await ain.getValidators(hash)).toStrictEqual(validators); - }); - // TODO(liayoo): add getTransactionResult method and test case for it. // it('getTransactionResult', async function() { // expect(await ain.getTransactionResult('0xabcdefghijklmnop')).toMatchSnapshot(); diff --git a/src/ain.ts b/src/ain.ts index 86555bb..1bff0ca 100755 --- a/src/ain.ts +++ b/src/ain.ts @@ -164,6 +164,24 @@ export default class Ain { return this.provider.send('ain_getBlockTransactionCountByHash', { hash }); } + /** + * Fetches the validator list of a block with a block number. + * @param {number} blockNumber The block number. + * @returns {Promise} + */ + getValidatorsByNumber(blockNumber: number): Promise { + return this.provider.send('ain_getValidatorsByNumber', { number: blockNumber }); + } + + /** + * Fetches the validator list of a block with a block hash. + * @param {string} blockHash The block hash. + * @returns {Promise} + */ + getValidatorsByHash(blockHash: string): Promise { + return this.provider.send('ain_getValidatorsByHash', { hash: blockHash }); + } + /** * Fetches the forger's address of a block with a block number. * @param {number} blockNumber The block number. @@ -182,18 +200,6 @@ export default class Ain { return this.provider.send('ain_getProposerByHash', { hash: blockHash }); } - /** - * Fetches the validator list of a block with a block hash or block number. - * @param {string | number} blockHashOrBlockNumber The block hash or block number. - * @returns {Promise} - */ - getValidators(blockHashOrBlockNumber: string | number): Promise { - const byHash = typeof blockHashOrBlockNumber === 'string' - const rpcMethod = byHash ? 'ain_getValidatorsByHash' : 'ain_getValidatorsByNumber'; - return this.provider.send(rpcMethod, - {[byHash ? 'hash' : 'number']: blockHashOrBlockNumber}); - } - /** * Fetches pending transaction. * @returns {Promise} From bad198a9befff864978df543817dd9e395d8814f Mon Sep 17 00:00:00 2001 From: platfowner Date: Mon, 4 Mar 2024 15:37:29 +0900 Subject: [PATCH 29/31] Add getValidatorInfo() method --- __tests__/ain.test.ts | 8 ++++++++ src/ain.ts | 17 +++++++++++++---- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/__tests__/ain.test.ts b/__tests__/ain.test.ts index 99b8ae6..f586760 100644 --- a/__tests__/ain.test.ts +++ b/__tests__/ain.test.ts @@ -549,6 +549,14 @@ describe('ain-js', function() { expect(txCount).not.toBeNull(); }); + it('getValidatorInfo', async function () { + const lastBlock = await ain.getLastBlock(); + expect(lastBlock).not.toBeNull(); + expect(lastBlock.proposer).not.toBeNull(); + const info = await ain.getValidatorInfo(lastBlock.proposer); + expect(info).not.toBeNull(); + }); + it('getValidatorsByNumber', async function () { const lastBlock = await ain.getLastBlock(); expect(lastBlock).not.toBeNull(); diff --git a/src/ain.ts b/src/ain.ts index 1bff0ca..a32a950 100755 --- a/src/ain.ts +++ b/src/ain.ts @@ -164,21 +164,30 @@ export default class Ain { return this.provider.send('ain_getBlockTransactionCountByHash', { hash }); } + /** + * Fetches the information of the given validator address. + * @param {string} address The validator address. + * @returns {Promise} + */ + getValidatorInfo(address: string): Promise { + return this.provider.send('ain_getValidatorInfo', { address }); + } + /** * Fetches the validator list of a block with a block number. * @param {number} blockNumber The block number. - * @returns {Promise} + * @returns {Promise} */ - getValidatorsByNumber(blockNumber: number): Promise { + getValidatorsByNumber(blockNumber: number): Promise { return this.provider.send('ain_getValidatorsByNumber', { number: blockNumber }); } /** * Fetches the validator list of a block with a block hash. * @param {string} blockHash The block hash. - * @returns {Promise} + * @returns {Promise} */ - getValidatorsByHash(blockHash: string): Promise { + getValidatorsByHash(blockHash: string): Promise { return this.provider.send('ain_getValidatorsByHash', { hash: blockHash }); } From c1fd6a958e9fbb44d935b3e8fabad27d16e87d9d Mon Sep 17 00:00:00 2001 From: platfowner Date: Mon, 4 Mar 2024 16:13:02 +0900 Subject: [PATCH 30/31] Tweak comments --- src/ain.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ain.ts b/src/ain.ts index a32a950..49c4411 100755 --- a/src/ain.ts +++ b/src/ain.ts @@ -192,7 +192,7 @@ export default class Ain { } /** - * Fetches the forger's address of a block with a block number. + * Fetches the block proproser's address of a block with a block number. * @param {number} blockNumber The block number. * @returns {Promise} */ @@ -201,7 +201,7 @@ export default class Ain { } /** - * Fetches the forger's address of a block with a block hash. + * Fetches the block proproser's address of a block with a block hash. * @param {string} blockHash The block hash. * @returns {Promise} */ From f31a4e890f27c4d6725b166e8004babea8c018a5 Mon Sep 17 00:00:00 2001 From: platfowner Date: Fri, 22 Mar 2024 02:54:52 +0000 Subject: [PATCH 31/31] Upgrade version to 1.7.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index fbc305e..bb8aa59 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@ainblockchain/ain-js", - "version": "1.6.3", + "version": "1.7.0", "description": "", "main": "lib/ain.js", "scripts": {