Skip to content
This repository has been archived by the owner on Apr 21, 2023. It is now read-only.

[node-rewards-program] task: add service to communicate with API #679

Merged
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/app/services/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,4 +55,8 @@ servicesModule.service('Voting', VotingService);
import CatapultOptinService from './catapultOptin.service'
servicesModule.service('CatapultOptin', CatapultOptinService);

// Set Node Rewards Program service
import NodeRewardsProgramService from './nodeRewardsProgram.service'
servicesModule.service('NodeRewardsProgram', NodeRewardsProgramService);

export default servicesModule;
173 changes: 173 additions & 0 deletions src/app/services/nodeRewardsProgram.service.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
import nem from 'nem-sdk';

const NODE_REWARDS_PROGRAM_API_BASE_URL = 'https://supernodesapi.nem.io';

/** Service to enroll in the Node Rewards Program and to get the status related information */
class NodeRewardsProgram {

/**
* Initialize dependencies and properties
*
* @params {services} - Angular services to inject
*/
constructor($localStorage, $filter, Wallet) {
'ngInject';

// Service dependencies region //

this._storage = $localStorage;
this._$filter = $filter;
this._Wallet = Wallet;

// End dependencies region //

// Service properties region //

this.apiBaseUrl = NODE_REWARDS_PROGRAM_API_BASE_URL;
this.common = nem.model.objects.get('common');

// End properties region //
}

// Service methods region //

/**
* Prepare the enroll transaction
*
* @param {string} mainPublicKey - Delegated harvesting public key.
* @param {string} host - IP or domain of the node.
* @param {string} enrollmentAddress - Node Rewards Program enrollment address.
* @param {string} [multisigAccountAddress] - Multisig account address. For multisig enrollment only.

Choose a reason for hiding this comment

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

why square brackets? [multisigAccountAddress]

Copy link
Author

Choose a reason for hiding this comment

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

This is an optional parameter, according to the JSDoc syntax.

*
* @return {Promise<object>} - Prepared enroll transaction
*/
async prepareEnrollTransaction(mainPublicKey, host, enrollmentAddress, multisigAccountAddress) {
// Create a new transaction object
const transferTransaction = nem.model.objects.get('transferTransaction');

// Set enrollment address as recipient
transferTransaction.recipient = enrollmentAddress.toUpperCase().replace(/-/g, '');

// Set multisig, if selected
if (multisigAccountAddress) {
transferTransaction.isMultisig = true;
transferTransaction.multisigAccount = multisigAccountAddress.toUpperCase().replace(/-/g, '');
}
Jaguar0625 marked this conversation as resolved.
Show resolved Hide resolved

// Set the message
const hash = await this.getCodewordHash(mainPublicKey);
transferTransaction.message = `enroll ${host} ${hash}`;

// Set no mosaics
transferTransaction.mosaics = null;

return nem.model.transactions.prepare('transferTransaction')(this.common, transferTransaction, this._Wallet.network);
}

Choose a reason for hiding this comment

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

I think we can keep this in the controller.
since this method didn't require fetch

what do you think?

Choose a reason for hiding this comment

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

@AnthonyLaw Can you elaborate on this? (Which class are you referring to and why?)

Choose a reason for hiding this comment

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

@AnthonyLaw Can you elaborate on this? (Which class are you referring to and why?)

sure, I mean prepareEnrollTransaction method, not class.

for my understanding, *.service.js should just make a request to the endpoint NODE_REWARDS_PROGRAM_API_BASE_URL.

but prepareEnrollTransaction just prepares the transaction object, it does not have any interaction with another endpoint.

so I think preparing transaction object should happen in nodeRewardsProgram.controller.

Choose a reason for hiding this comment

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

I think this is not the case.

for my understanding, *.service.js should just make a request to the endpoint NODE_REWARDS_PROGRAM_API_BASE_URL

Please see this answer => https://stackoverflow.com/a/27398868/3190131

And this => https://docs.angularjs.org/guide/services


/**
* Checks if an address is the current enrollment address.
*
* @param {string} address - Address to check.
*
* @return {Promise<boolean>} - If the address to check matches the current enrollment address.
*/
async checkEnrollmentAddress(address) {
const response = await fetch(this.apiBaseUrl + `/enrollment/check/address/${address}`);
Jaguar0625 marked this conversation as resolved.
Show resolved Hide resolved

if (!response.ok) {
throw Error('failed_to_validate_enroll_address');
}
Jaguar0625 marked this conversation as resolved.
Show resolved Hide resolved

const status = await response.text();

return status === 'true';
}

/**
* Checks if a signer public key is enrolled in the current period.
*
* @param {string} publicKey - Delegated harvesting public key to check.
*
* @return {Promise<boolean>} - If the public key is enrolled in the current period.
*/
async checkEnrollmentStatus(publicKey) {
const successEnrollmentResponse = await fetch(this.apiBaseUrl + `/enrollment/successes/${publicKey}?count=1`);

if (!successEnrollmentResponse.ok) {
throw Error('failed_to_get_success_enrollments');
}

const successEnrollments = await successEnrollmentResponse.json();

if (successEnrollments.length === 0) {
return false;
}

const latestSuccessEnrollment = successEnrollments[0];
const enrollAddress = latestSuccessEnrollment.recipientAddress;

return this.checkEnrollmentAddress(enrollAddress);
AnthonyLaw marked this conversation as resolved.
Show resolved Hide resolved
}

/**
* Gets codeword dependent hash for the current period given a public key.
*
* @param {string} publicKey - Signer public key to use in hash calculation.
*
* @return {Promise<string>} - The result of keccak_256(codeword || public_key) as a hex string, which should be used in enrollment messages.
Jaguar0625 marked this conversation as resolved.
Show resolved Hide resolved
*/
async getCodewordHash(publicKey) {
const response = await fetch(this.apiBaseUrl + `/codeword/${publicKey}`);

if (!response.ok) {
throw Error('failed_to_get_codeword_hash');
}

return await response.text();
}

/**
* Gets payout information for a single node.
*
* @param {string} nodeId - Id of the node to search.
* @param {number} pageNumber - Page number to return.
*
* @return {Promise<Array<object>>} - List of payouts.
*/
async getNodePayouts(nodeId, pageNumber) {
const count = 15;
const offset = count * pageNumber;

const response = await fetch(this.apiBaseUrl + `/node/${nodeId}/payouts?count=${count}&offset=${offset}`);

if (!response.ok) {
throw Error('failed_to_get_payouts_page');
}
Jaguar0625 marked this conversation as resolved.
Show resolved Hide resolved

return await response.json();
}

/**
* Gets detailed information about a single node.
*
* @param {string} nodeId - Id of the node to search. Can be one of:
* - Database node id
* - Hex encoded node main public key
* - Node IP (or host)
*
* @return {Promise<object>} - Node info.
*/
async getNodeInfo(nodeId) {
const response = await fetch(this.apiBaseUrl + `/node/${nodeId}`);

if (!response.ok) {
throw Error('failed_to_get_node_info');
}

return await response.json();
}

// End methods region //
}

export default NodeRewardsProgram;
Loading