Skip to content

Commit

Permalink
added SIP48 implementation (#46)
Browse files Browse the repository at this point in the history
* feat: added SIP48 implementation

* chore: added unit tests, removed code smells
  • Loading branch information
ohager authored Mar 15, 2023
1 parent ddc0803 commit 3dcdc31
Show file tree
Hide file tree
Showing 28 changed files with 349 additions and 12 deletions.
5 changes: 4 additions & 1 deletion packages/core/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,10 @@ This version is a major breakthrough with a lots of significant and breaking cha
- `transferAssetOwnership`,
- `getTransactionByReferenceHash`
- `calculateDistributionFee`,
- `getAssetsByOwner`
- `getAssetsByOwner`,
- `searchAliasesByName`,
- `getTopLevelDomains`
- `buyTopLevelDomain`

- New Transaction Subtype Enums for Assets
- `getAccountTransactions` can resolve asset distributions now!
Expand Down
56 changes: 55 additions & 1 deletion packages/core/src/api/__tests__/aliasApi.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@ import {
getAliasesOnSale,
buyAlias,
sellAlias,
getAliasByName
getAliasByName, buyTopLevelDomain, getTopLevelDomains
} from '../factories';
import {mockSignAndBroadcastTransaction, createChainService} from '../../__tests__/helpers';
import {searchAliasesByName} from '../factories/alias/searchAliasesByName';
import {AttachmentMessage} from '../../typings/attachment';

describe('Alias Api', () => {

Expand Down Expand Up @@ -159,4 +161,56 @@ describe('Alias Api', () => {
expect(asset).toEqual({'transaction': 'transactionId'});
});
});

describe('searchAliasesByName', () => {
it('should search as expected', async () => {
httpMock = HttpMockBuilder.create().onGetReply(200, {'aliases': []},
'relPath?requestType=getAliasesByName&aliasName=aliasName&timestamp=10000&firstIndex=0&lastIndex=150'
).build();
const service = createChainService(httpMock, 'relPath');
const result = await searchAliasesByName(service)({
aliasName: 'aliasName',
timestamp: 10000,
firstIndex: 0,
lastIndex: 150
});
expect(result.aliases).toHaveLength(0);
});
});

describe('getTLDS', () => {
it('should get as expected', async () => {
httpMock = HttpMockBuilder.create().onGetReply(200, {'tlds': []},
'relPath?requestType=getTLDs&timestamp=10000&firstIndex=0&lastIndex=150'
).build();
const service = createChainService(httpMock, 'relPath');
const result = await getTopLevelDomains(service)({
timestamp: 10000,
firstIndex: 0,
lastIndex: 150
});
expect(result.tlds).toHaveLength(0);
});
});

describe('buyTopLevelDomain', () => {
it('should buy as expected', async () => {
httpMock = HttpMockBuilder.create().onPostReply(200, {
broadcasted: true,
unsignedTransactionBytes: 'unsignedHexMessage'
},
'relPath?requestType=setTLD&message=Some%20message&messageIsText=true&tld=tld&amountNQT=10000000000000&deadline=1440&feeNQT=100000&publicKey=senderPublicKey'
).build();
const service = createChainService(httpMock, 'relPath');
const result = await buyTopLevelDomain(service)({
feePlanck: '100000',
amountPlanck: '10000000000000',
tld: 'tld',
attachment: new AttachmentMessage({message: 'Some message', messageIsText: true}),
deadline: 1440,
senderPublicKey: 'senderPublicKey'
});
expect(result.unsignedTransactionBytes).toBe('unsignedHexMessage');
});
});
});
14 changes: 13 additions & 1 deletion packages/core/src/api/composeApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,16 @@ import {
setRewardRecipient,
getRewardRecipient, addCommitment, removeCommitment,
} from './factories/account';
import {getAliasById, getAliasByName, getAliasesOnSale, sellAlias, buyAlias} from './factories/alias';
import {
getAliasById,
getAliasByName,
getAliasesOnSale,
sellAlias,
buyAlias,
searchAliasesByName,
getTopLevelDomains,
buyTopLevelDomain
} from './factories/alias';
import {
callContractMethod,
getAllContractIds,
Expand Down Expand Up @@ -213,6 +222,9 @@ export function composeApi(settings: ApiSettings): Api {
buyAlias,
sellAlias,
getAliases,
searchAliasesByName,
getTopLevelDomains,
buyTopLevelDomain
}).withContractApi({
getContract,
getContractsByAccount,
Expand Down
34 changes: 34 additions & 0 deletions packages/core/src/api/factories/alias/buyTopLevelDomain.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/**
* Copyright (c) 2023 Signum Network
*/
import {ChainService} from '../../../service/chainService';
import {UnsignedTransaction} from '../../../typings/unsignedTransaction';
import {signIfPrivateKey} from '../../../internal/signIfPrivateKey';
import {DefaultDeadline} from '../../../constants';
import {BuyTopLevelDomainArgs} from '../../../typings/args';
import {createParametersFromAttachment} from '../../../internal';

/**
* Use with [[ApiComposer]] and belongs to [[AliasApi]].
*
* See details at [[AliasApi.buyTopLevelDomain]]
*
* @module core.api.factories
*/
export const buyTopLevelDomain = (service: ChainService) =>
(args: BuyTopLevelDomainArgs) =>
signIfPrivateKey(service, args, async (a: BuyTopLevelDomainArgs) => {
let parameters = {
tld: a.tld,
amountNQT: a.amountPlanck,
deadline: a.deadline || DefaultDeadline,
feeNQT: a.feePlanck,
publicKey: a.senderPublicKey,
referencedTransactionFullHash: a.referencedTransactionFullHash,
};

if (args.attachment) {
parameters = createParametersFromAttachment(a.attachment, parameters);
}
return service.send<UnsignedTransaction>('setTLD', parameters);
});
2 changes: 1 addition & 1 deletion packages/core/src/api/factories/alias/getAliasById.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,5 @@ import {AliasList} from '../../../typings/aliasList';
export const getAliasById = (service: ChainService):
(aliasId: string) => Promise<AliasList> =>
(aliasId: string): Promise<AliasList> => service.query('getAlias', {
alias:aliasId,
alias: aliasId,
});
8 changes: 5 additions & 3 deletions packages/core/src/api/factories/alias/getAliasByName.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/**
* Copyright (c) 2019 Burst Apps Team
* Modified (c) 2023 Signum Network
*/
import {ChainService} from '../../../service/chainService';
import {AliasList} from '../../../typings/aliasList';
Expand All @@ -11,7 +12,8 @@ import {AliasList} from '../../../typings/aliasList';
* @module core.api.factories
*/
export const getAliasByName = (service: ChainService):
(aliasName: string) => Promise<AliasList> =>
(aliasName: string): Promise<AliasList> => service.query('getAlias', {
aliasName
(aliasName: string, tld?: string) => Promise<AliasList> =>
(aliasName: string, tld?: string): Promise<AliasList> => service.query('getAlias', {
aliasName,
tld
});
16 changes: 16 additions & 0 deletions packages/core/src/api/factories/alias/getTopLevelDomains.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/**
* Copyright (c) 2023 Signum Network
*/
import {ChainService} from '../../../service/chainService';
import {GetTopLevelDomainsArgs} from '../../../typings/args/getTopLevelDomainsArgs';
import {TopLevelDomainList} from '../../../typings/topLevelDomainList';

/**
* Use with [[ApiComposer]] and belongs to [[AliasApi]].
*
* See details at [[AliasApi.getTopLevelDomains]]
* @module core.api.factories
*/
export const getTopLevelDomains = (service: ChainService):
(args: GetTopLevelDomainsArgs) => Promise<TopLevelDomainList> =>
(args: GetTopLevelDomainsArgs): Promise<TopLevelDomainList> => service.query('getTLDs', args);
3 changes: 3 additions & 0 deletions packages/core/src/api/factories/alias/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,6 @@ export * from './getAliasByName';
export * from './getAliasesOnSale';
export * from './buyAlias';
export * from './sellAlias';
export * from './getTopLevelDomains';
export * from './buyTopLevelDomain';
export * from './searchAliasesByName';
17 changes: 17 additions & 0 deletions packages/core/src/api/factories/alias/searchAliasesByName.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/**
* Copyright (c) 2019 Burst Apps Team
* Modified (c) 2023 Signum Network
*/
import {ChainService} from '../../../service/chainService';
import {AliasList} from '../../../typings/aliasList';
import { SearchAliasesByNameArgs } from '../../../typings/args/searchAliasesByNameArgs';

/**
* Use with [[ApiComposer]] and belongs to [[AliasApi]].
*
* See details at [[AliasApi.searchAliasesByName]]
* @module core.api.factories
*/
export const searchAliasesByName = (service: ChainService):
(args: SearchAliasesByNameArgs) => Promise<AliasList> =>
(args): Promise<AliasList> => service.query('getAliasesByName', args);
1 change: 1 addition & 0 deletions packages/core/src/constants/transactionArbitrarySubtype.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,6 @@ export enum TransactionArbitrarySubtype {
AccountInfo,
AliasSale,
AliasBuy,
TopLevelDomainAssignment
}

Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,22 @@ describe('rebuildTransactionPostData', () => {
});
});
});
describe('setTLD', () => {
const requestType = 'setTLD';
it('should rebuild data correctly', () => {
const transactionBytes = '01289da31c10140004d794aa453a5bbdb8d580f1d9a76b6d7a25cde0ed38c098550ea0f784d9317a000000000000000000a0724e18090000002d3101000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000055f4070086134aea12e0d25404cfe1531b543c7c010c313233737061636573686970';
const rebuiltData = {
tld: '123spaceship',
amountNQT: '10000000000000',
feeNQT: '20000000',
publicKey: '04d794aa453a5bbdb8d580f1d9a76b6d7a25cde0ed38c098550ea0f784d9317a',
deadline: 20
};
const output = rebuildTransactionPostData(transactionBytes);
expect(output.requestType).toEqual(requestType);
expect(output.rebuiltData).toEqual(rebuiltData);
});
});
describe('setAccountInfo', () => {
const requestType = 'setAccountInfo';
it('should rebuild data correctly - Plus very long and utf-8 data', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -282,4 +282,26 @@ describe('verifyTransaction', function () {
}).not.toThrow();
});
});
describe('setTLD', function () {
it('should pass verification as expected', () => {
const requestType = 'setTLD';
const formData = {
tld: '123spaceship',
amountNQT: '10000000000000',
feeNQT: '20000000',
publicKey: '04d794aa453a5bbdb8d580f1d9a76b6d7a25cde0ed38c098550ea0f784d9317a',
deadline: 20
};

const testResponse = {
'broadcasted': false,
'unsignedTransactionBytes': '01289da31c10140004d794aa453a5bbdb8d580f1d9a76b6d7a25cde0ed38c098550ea0f784d9317a000000000000000000a0724e18090000002d3101000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000055f4070086134aea12e0d25404cfe1531b543c7c010c313233737061636573686970',
'transactionJSON': {},
'requestProcessingTime': 8
};
expect(() => {
verifyTransaction(requestType, formData, testResponse);
}).not.toThrow();
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,9 @@ const AttachmentSpecV1: AttachmentSpec = new Map<string, AttachmentField[]>([
{type: 'ShortString*1', parameterName: 'description'},
{type: 'CreationBytes*1'},
]],
['setTLD', [
{type: 'ByteString*1', parameterName: 'tld'},
]],
]);

const AttachmentSpecV2: AttachmentSpec = new Map<string, AttachmentField[]>([
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ const DecodeRequestType = [
{type: 1, subtype: 5, requestType: 'setAccountInfo', hasAttachment: true},
{type: 1, subtype: 6, requestType: 'sellAlias', hasAttachment: true},
{type: 1, subtype: 7, requestType: 'buyAlias', hasAttachment: true},
{type: 1, subtype: 8, requestType: 'setTLD', hasAttachment: true},
{type: 2, subtype: 0, requestType: 'issueAsset', hasAttachment: true},
{type: 2, subtype: 1, requestType: 'transferAsset', hasAttachment: true},
{type: 2, subtype: 2, requestType: 'placeAskOrder', hasAttachment: true},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {rebuildTransactionPostData} from './rebuildTransactionPostData';
// Type 22 (automated transactions): OK
const methodsToVerify = new Set([
'sendMoney', 'sendMoneyMulti', 'sendMoneyMultiSame',
'sendMessage', 'setAlias', 'setAccountInfo',
'sendMessage', 'setAlias', 'setTLD', 'setAccountInfo',
'issueAsset', 'transferAsset', 'transferAssetOwnership', 'placeAskOrder', 'placeBidOrder', 'cancelAskOrder',
'mintAsset', 'addAssetTreasuryAccount', 'distributeToAssetHolders', 'cancelBidOrder', 'transferAssetMulti',
'setRewardRecipient', 'addCommitment', 'removeCommitment',
Expand Down
14 changes: 14 additions & 0 deletions packages/core/src/typings/alias.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,18 @@ export interface Alias {
*/
aliasURI: string;
timestamp: number;

/**
* The id of the Top Level Domain (tld) aka namespace for this alias. Default is '0', which points to 'signum'
*/
tld?: string;
/**
* The name of the Top Level Domain (tld) aka namespace for this alias. Default is 'signum'
*/
tldName?: string;

/**
* Numbers of aliases within the namespace/ top level domain.
*/
numberOfAlias?: number;
}
31 changes: 29 additions & 2 deletions packages/core/src/typings/api/aliasApi.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,17 @@
import {TransactionId} from '../transactionId';
import {Alias} from '../alias';
import {UnsignedTransaction} from '../unsignedTransaction';
import {SetAliasArgs} from '../args/setAliasArgs';
import {AliasList} from '../aliasList';
import {BuyAliasArgs, GetAliasesOnSaleArgs, SellAliasArgs} from '../args';
import {
BuyAliasArgs,
BuyTopLevelDomainArgs,
GetAliasesOnSaleArgs,
SellAliasArgs,
SetAliasArgs,
GetTopLevelDomainsArgs,
SearchAliasesByNameArgs
} from '../args';
import {TopLevelDomainList} from '../topLevelDomainList';

/**
* Alias API
Expand All @@ -27,10 +35,12 @@ export interface AliasApi {
/**
* Get alias by name, i.e. get basic account info for given alias name
* @param {string} aliasName The alias name
* @param {string} tld optional Top Level Domain. If not given, the default domain 'signum' is being used
* @return {Promise<Alias>} The Alias object
*/
getAliasByName: (
aliasName: string,
tld?: string,
) => Promise<Alias>;

/**
Expand Down Expand Up @@ -64,4 +74,21 @@ export interface AliasApi {
* @return The Transaction Id or Unsigned Bytes as Hex String if no private key was sent
*/
sellAlias: (args: SellAliasArgs) => Promise<TransactionId | UnsignedTransaction>;

/**
* Searches for aliases by their name or part of the name
* @param args The args
*/
searchAliasesByName: (args: SearchAliasesByNameArgs) => Promise<AliasList>;
/**
* Gets all registered Top Level Domains
* @param args The args
*/
getTopLevelDomains: (args: GetTopLevelDomainsArgs) => Promise<TopLevelDomainList>;

/**
* Buys a Top Level Domain (TLD)
* @param args The args
*/
buyTopLevelDomain: (args: BuyTopLevelDomainArgs) => Promise<TransactionId | UnsignedTransaction>;
}
2 changes: 2 additions & 0 deletions packages/core/src/typings/args/buyAliasArgs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@ import {DefaultSendArgs} from './defaultSendArgs';
* @param alias The alias Id
* @param aliasName Alternative to alias Id
* @param amountPlanck The amount for buy in Planck
* @param tld The name of the Top Level Domain (TLD) aka namespace where this alias belongs to
* @module core
*/
export interface BuyAliasArgs extends DefaultSendArgs {
aliasId: string;
aliasName?: string;
amountPlanck: string;
tld?: string;
}
14 changes: 14 additions & 0 deletions packages/core/src/typings/args/buyTopLevelDomainArgs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import {DefaultSendArgs} from './defaultSendArgs';


/**
* The argument object for [[AccountApi.setTopLevelDomain]]
*
* @param tld The name of the Top Level Domain (max. 40 chars only digits and letters)
* @param amountPlanck The amount in planck according SIP-48
* @module core
*/
export interface BuyTopLevelDomainArgs extends DefaultSendArgs {
tld: string;
amountPlanck: string;
}
Loading

0 comments on commit 3dcdc31

Please sign in to comment.