Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add /accounts/:address/validate endpoint #726

Merged
merged 34 commits into from
Oct 27, 2021
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
83376b6
add ValidateAddressService
TarikGul Oct 20, 2021
166e5c3
add ValidateAddressController
TarikGul Oct 20, 2021
54cf986
add AccountsValidate to polkadot, westend, and kusama
TarikGul Oct 20, 2021
c9c7f39
imports
TarikGul Oct 20, 2021
2b18fc5
add kusama chain config
TarikGul Oct 20, 2021
9ed2625
fix response
TarikGul Oct 20, 2021
a09a58f
fix networkInfo bug
TarikGul Oct 20, 2021
ec0682e
lint
TarikGul Oct 20, 2021
b308aed
get tests started
TarikGul Oct 21, 2021
0286a84
change the name of the service
TarikGul Oct 21, 2021
cfa65dd
cleanup, lint, fix build, test boilerplate
TarikGul Oct 22, 2021
a4307b1
update the tests
TarikGul Oct 22, 2021
e9c16e3
more tests
TarikGul Oct 22, 2021
213dc10
use hexToU8a for hex conversion
TarikGul Oct 22, 2021
2ef30d6
add tests for hex values
TarikGul Oct 22, 2021
6020802
cleanup
TarikGul Oct 22, 2021
b2f8174
docs
TarikGul Oct 22, 2021
cedc3e1
pull master, fix conflicts
TarikGul Oct 22, 2021
d1991c8
add AccountsValidate controller to other chains-config
TarikGul Oct 22, 2021
9d3dab6
remove networkId
TarikGul Oct 25, 2021
775ab83
update tests
TarikGul Oct 25, 2021
8b0964c
ValidateAddrResponse -> IValidateAddrResponse
TarikGul Oct 25, 2021
c35d4bd
typos
TarikGul Oct 25, 2021
98d1efa
ss58Decoded -> ss58Prefix
TarikGul Oct 25, 2021
a313139
inline docs for validate address
TarikGul Oct 25, 2021
d9903b0
update docs
TarikGul Oct 25, 2021
3de3b46
cleanup testws
TarikGul Oct 27, 2021
de9a232
Update src/services/accounts/AccountsValidateService.spec.ts
TarikGul Oct 27, 2021
f784667
Update src/services/accounts/AccountsValidateService.spec.ts
TarikGul Oct 27, 2021
a48642b
Update src/services/accounts/AccountsValidateService.spec.ts
TarikGul Oct 27, 2021
948de5f
Update src/services/accounts/AccountsValidateService.ts
TarikGul Oct 27, 2021
3cb824e
update docs
TarikGul Oct 27, 2021
4c9dc04
add proper error handling for base58decode
TarikGul Oct 27, 2021
b585222
docs
TarikGul Oct 27, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/dist/app.bundle.js

Large diffs are not rendered by default.

37 changes: 37 additions & 0 deletions docs/src/openapi-v1.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,30 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/Error'
/accounts/{accountId}/validate:
get:
tags:
- accounts
summary: Validate a given address for the connected network.
TarikGul marked this conversation as resolved.
Show resolved Hide resolved
description: Returns information regarding an address's validity for whichever network
sidecar is connected too. It will check if the address is valid, what the ss58 prefix is,
and what the networkId for that address is.
TarikGul marked this conversation as resolved.
Show resolved Hide resolved
operationId: getValidationByAccountId
parameters:
- name: accountId
in: path
description: SS58 or Hex address of the account.
required: true
schema:
format: SS58 or Hex
type: string
responses:
"200":
description: successful operation
content:
application/json:
schema:
$ref: '#/components/schemas/AccountValidation'
/blocks/{blockId}:
get:
tags:
Expand Down Expand Up @@ -1394,6 +1418,19 @@ components:
based on the portion of `totalEraRewardPoints` they have.
payouts:
$ref: '#/components/schemas/Payouts'
AccountValidation:
type: object
properties:
isValid:
type: boolean
description: Whether the given address is valid for the given network.
networkId:
type: string
description: Name of the network the address is associated with
ss58:
Copy link
Contributor

Choose a reason for hiding this comment

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

I think we should change this to ss58Prefix for clarity. SS58 is the name of the address format

Copy link
Contributor

Choose a reason for hiding this comment

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

And specify what happens when an address has no prefix

Copy link
Member Author

Choose a reason for hiding this comment

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

And specify what happens when an address has no prefix

I think any valid base58 will provide a "ss58Prefix" that we plug into checkAddressChecksum,

const ss58Length = (decoded[0] & 0b0100_0000) ? 2 : 1;
// ss58Decoded is the prefix
const ss58Decoded = ss58Length === 1
    ? decoded[0]
    : ((decoded[0] & 0b0011_1111) << 2) | (decoded[1] >> 6) | ((decoded[1] & 0b0011_1111) << 8);

so in this case do you think we should just say if the address is not valid a prefix may still be returned.

Copy link
Member Author

Choose a reason for hiding this comment

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

Or if we receive a non valid address we just return null for the ss58Prefix

type: string
description: SS58 prefix of the given address
format: unsignedInteger
AccountVestingInfo:
type: object
description: Sidecar version's <= v10.0.0 have a`vesting` return value that defaults to an object for
Expand Down
1 change: 1 addition & 0 deletions src/chains-config/defaultControllers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export const defaultControllers: ControllerConfig = {
'AccountsBalanceInfo',
'AccountsStakingInfo',
'AccountsStakingPayouts',
'AccountsValidate',
'AccountsVestingInfo',
'Blocks',
'BlocksExtrinsics',
Expand Down
1 change: 1 addition & 0 deletions src/chains-config/dockMainnetControllers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { getBlockWeight } from './metadata-consts';
export const dockMainnetControllers: ControllerConfig = {
controllers: [
'AccountsBalanceInfo',
'AccountsValidate',
'Blocks',
'BlocksExtrinsics',
'NodeNetwork',
Expand Down
1 change: 1 addition & 0 deletions src/chains-config/dockPoSMainnetControllers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export const dockPoSMainnetControllers: ControllerConfig = {
'AccountsBalanceInfo',
'AccountsStakingInfo',
'AccountsStakingPayouts',
'AccountsValidate',
'Blocks',
'BlocksExtrinsics',
'NodeNetwork',
Expand Down
1 change: 1 addition & 0 deletions src/chains-config/dockPoSTestnetControllers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export const dockTestnetControllers: ControllerConfig = {
'AccountsBalanceInfo',
'AccountsStakingInfo',
'AccountsStakingPayouts',
'AccountsValidate',
'Blocks',
'BlocksExtrinsics',
'NodeNetwork',
Expand Down
1 change: 1 addition & 0 deletions src/chains-config/kiltControllers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export const kiltControllers: ControllerConfig = {
controllers: [
'AccountsBalanceInfo',
'AccountsStakingInfo',
'AccountsValidate',
'Blocks',
'BlocksExtrinsics',
'NodeNetwork',
Expand Down
1 change: 1 addition & 0 deletions src/chains-config/kulupuControllers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { initLRUCache } from './cache/lruCache';
export const kulupuControllers: ControllerConfig = {
controllers: [
'AccountsBalanceInfo',
'AccountsValidate',
'Blocks',
'BlocksExtrinsics',
'NodeNetwork',
Expand Down
1 change: 1 addition & 0 deletions src/chains-config/kusamaControllers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export const kusamaControllers: ControllerConfig = {
'AccountsBalanceInfo',
'AccountsStakingInfo',
'AccountsStakingPayouts',
'AccountsValidate',
'AccountsVestingInfo',
'Blocks',
'BlocksExtrinsics',
Expand Down
1 change: 1 addition & 0 deletions src/chains-config/mandalaControllers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export const mandalaControllers: ControllerConfig = {
'AccountsBalanceInfo',
'AccountsStakingInfo',
'AccountsStakingPayouts',
'AccountsValidate',
'AccountsVestingInfo',
'Blocks',
'BlocksExtrinsics',
Expand Down
1 change: 1 addition & 0 deletions src/chains-config/polkadotControllers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export const polkadotControllers: ControllerConfig = {
'AccountsBalanceInfo',
'AccountsStakingInfo',
'AccountsStakingPayouts',
'AccountsValidate',
'AccountsVestingInfo',
'Blocks',
'BlocksExtrinsics',
Expand Down
1 change: 1 addition & 0 deletions src/chains-config/polymeshControllers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export const polymeshControllers: ControllerConfig = {
controllers: [
'AccountsStakingPayouts',
'AccountsStakingInfo',
'AccountsValidate',
'Blocks',
'BlocksExtrinsics',
'NodeNetwork',
Expand Down
1 change: 1 addition & 0 deletions src/chains-config/shidenControllers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export const shidenControllers: ControllerConfig = {
controllers: [
'AccountsBalanceInfo',
'AccountsVestingInfo',
'AccountsValidate',
'Blocks',
'BlocksExtrinsics',
'BlocksTrace',
Expand Down
1 change: 1 addition & 0 deletions src/chains-config/soraControllers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export const soraControllers: ControllerConfig = {
'AccountsBalanceInfo',
'AccountsStakingInfo',
'AccountsStakingPayouts',
'AccountsValidate',
'Blocks',
'BlocksExtrinsics',
'NodeNetwork',
Expand Down
1 change: 1 addition & 0 deletions src/chains-config/statemineControllers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { initLRUCache } from './cache/lruCache';
export const statemineControllers: ControllerConfig = {
controllers: [
'AccountsAssets',
'AccountsValidate',
'Blocks',
'BlocksExtrinsics',
'NodeNetwork',
Expand Down
1 change: 1 addition & 0 deletions src/chains-config/statemintControllers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { initLRUCache } from './cache/lruCache';
export const statemintControllers: ControllerConfig = {
controllers: [
'AccountsAssets',
'AccountsValidate',
'Blocks',
'BlocksExtrinsics',
'NodeNetwork',
Expand Down
1 change: 1 addition & 0 deletions src/chains-config/westendControllers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export const westendControllers: ControllerConfig = {
'AccountsBalanceInfo',
'AccountsStakingInfo',
'AccountsStakingPayouts',
'AccountsValidate',
'AccountsVestingInfo',
'Blocks',
'BlocksExtrinsics',
Expand Down
23 changes: 23 additions & 0 deletions src/controllers/accounts/AccountsValidateController.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { ApiPromise } from '@polkadot/api';
import { RequestHandler } from 'express';

import { AccountsValidateService } from '../../services/accounts';
import AbstractController from '../AbstractController';

export default class ValidateAddressController extends AbstractController<AccountsValidateService> {
constructor(api: ApiPromise) {
super(api, '/accounts/:address/validate', new AccountsValidateService(api));
this.initRoutes();
}

protected initRoutes(): void {
this.safeMountAsyncGetHandlers([['', this.validateAddress]]);
TarikGul marked this conversation as resolved.
Show resolved Hide resolved
}

private validateAddress: RequestHandler = ({ params: { address } }, res) => {
ValidateAddressController.sanitizedSend(
res,
this.service.validateAddress(address)
);
};
}
1 change: 1 addition & 0 deletions src/controllers/accounts/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ export { default as AccountsAssets } from './AccountsAssetsController';
export { default as AccountsBalanceInfo } from './AccountsBalanceInfoController';
export { default as AccountsStakingInfo } from './AccountsStakingInfoController';
export { default as AccountsStakingPayouts } from './AccountsStakingPayoutsController';
export { default as AccountsValidate } from './AccountsValidateController';
export { default as AccountsVestingInfo } from './AccountsVestingInfoController';
2 changes: 2 additions & 0 deletions src/controllers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
AccountsBalanceInfo,
AccountsStakingInfo,
AccountsStakingPayouts,
AccountsValidate,
AccountsVestingInfo,
} from './accounts';
import { Blocks, BlocksExtrinsics, BlocksTrace } from './blocks';
Expand Down Expand Up @@ -31,6 +32,7 @@ export const controllers = {
AccountsAssets,
AccountsBalanceInfo,
AccountsStakingInfo,
AccountsValidate,
AccountsVestingInfo,
AccountsStakingPayouts,
PalletsAssets,
Expand Down
145 changes: 145 additions & 0 deletions src/services/accounts/AccountsValidateService.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
import { ApiPromise } from '@polkadot/api';

import { sanitizeNumbers } from '../../sanitize';
import { AccountsValidateService } from './AccountsValidateService';

describe('Validate addresses', () => {
const mockApi = {
registry: {
chainSS58: 0,
},
} as unknown as ApiPromise;
const validateService = new AccountsValidateService(mockApi);

afterEach(() => {
// Reset the `mockApi.registry.chainSS58` to be 0 before each test
(mockApi.registry.chainSS58 as unknown) = 0;
});
TarikGul marked this conversation as resolved.
Show resolved Hide resolved

it('Should verify a polkadot address when connected to polkadot', () => {
TarikGul marked this conversation as resolved.
Show resolved Hide resolved
const expectedResponse = {
isValid: true,
networkId: 'polkadot',
ss58: '0',
};
const polkadotAddr = '1xN1Q5eKQmS5AzASdjt6R6sHF76611vKR4PFpFjy1kXau4m';

expect(
sanitizeNumbers(validateService.validateAddress(polkadotAddr))
).toStrictEqual(expectedResponse);
});

it('Should verify a kusama address when connected to kusama', () => {
TarikGul marked this conversation as resolved.
Show resolved Hide resolved
const expectedResponse = {
isValid: true,
networkId: 'kusama',
ss58: '2',
};
const kusamaAddr = 'DXgXPAT5zWtPHo6FhVvrDdiaDPgCNGxhJAeVBYLtiwW9hAc';
(mockApi.registry.chainSS58 as unknown) = 2;

expect(
sanitizeNumbers(validateService.validateAddress(kusamaAddr))
).toStrictEqual(expectedResponse);
});

it('Should verify a kulupu address when connected to kulup', () => {
TarikGul marked this conversation as resolved.
Show resolved Hide resolved
const expectedResponse = {
isValid: true,
networkId: 'kulupu',
ss58: '16',
};
const kulupuAddr = '2cYv9Gk6U4m4a7Taw9pG8qMfd1Pnxw6FLTvV6kYZNhGL6M9y';
(mockApi.registry.chainSS58 as unknown) = 16;

expect(
sanitizeNumbers(validateService.validateAddress(kulupuAddr))
).toStrictEqual(expectedResponse);
});

it('Should verify a valid default substrate address', () => {
const expectedResponse = {
isValid: true,
networkId: 'substrate',
ss58: '42',
};
const substrateAddr = '5EnxxUmEbw8DkENKiYuZ1DwQuMoB2UWEQJZZXrTsxoz7SpgG';
(mockApi.registry.chainSS58 as unknown) = 42;

expect(
sanitizeNumbers(validateService.validateAddress(substrateAddr))
).toStrictEqual(expectedResponse);
});

it('Should give the correct response with a valid ss58 addr on the wrong network', () => {
TarikGul marked this conversation as resolved.
Show resolved Hide resolved
const expectedResponse = {
isValid: false,
networkId: 'polkadot',
ss58: '0',
};
const polkadotAddr = '1xN1Q5eKQmS5AzASdjt6R6sHF76611vKR4PFpFjy1kXau4m';
(mockApi.registry.chainSS58 as unknown) = 2;

expect(
sanitizeNumbers(validateService.validateAddress(polkadotAddr))
).toStrictEqual(expectedResponse);
});

it('Should give the correct response for a polkadot hex value', () => {
const expectedResponse = {
isValid: true,
networkId: 'polkadot',
ss58: '0',
};
const polkadotHex =
'0x002a39366f6620a6c2e2fed5990a3d419e6a19dd127fc7a50b515cf17e2dc5cc592312';

expect(
sanitizeNumbers(validateService.validateAddress(polkadotHex))
).toStrictEqual(expectedResponse);
});

it('Should give the correct response for a kusama hex value', () => {
const expectedResponse = {
isValid: true,
networkId: 'kusama',
ss58: '2',
};
const kusamaHex =
'0x02ce046d43fc4c0fb8b3b754028515e5020f5f1d8d620b4ef0f983c5df34b1952909e9';
(mockApi.registry.chainSS58 as unknown) = 2;

expect(
sanitizeNumbers(validateService.validateAddress(kusamaHex))
).toStrictEqual(expectedResponse);
});

it('Should give the correct response for a karura hex value', () => {
const expectedResponse = {
isValid: true,
networkId: 'karura',
ss58: '8',
};
const karuraHex =
'0x086d6f646c6163612f6364707400000000000000000000000000000000000000008333';
(mockApi.registry.chainSS58 as unknown) = 8;

expect(
sanitizeNumbers(validateService.validateAddress(karuraHex))
).toStrictEqual(expectedResponse);
});

Copy link
Contributor

Choose a reason for hiding this comment

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

should have a test for a hex value with no prefix and a ss58 value with no prefix to show it works and what the expected prefix is (null?)

Copy link
Member Author

Choose a reason for hiding this comment

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

Is an ss58 address with no prefix just a base58 address?

Copy link
Contributor

Choose a reason for hiding this comment

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

I think

Copy link
Member Author

Choose a reason for hiding this comment

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

Yea so to build on that too, every base58 address will always have a prefix as well

it('Should return the correct response for an invalid hex value', () => {
const expectedResponse = {
isValid: false,
networkId: null,
ss58: null,
};
const invalidAddr =
'0x2a39366f6620a6c2e2fed5990a3d419e6a19dd127fc7a50b515cf17e2dc5cc59';

expect(
sanitizeNumbers(validateService.validateAddress(invalidAddr))
).toStrictEqual(expectedResponse);
});
});
Loading