-
-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #10 from Adamant-im/feature/add-delegate
Feature/add delegate methods
- Loading branch information
Showing
6 changed files
with
232 additions
and
11 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
const axios = require('axios'); | ||
const logger = require('../helpers/logger'); | ||
const keys = require('../helpers/keys'); | ||
const constants = require('../helpers/constants'); | ||
const transactionFormer = require('../helpers/transactionFormer'); | ||
const validator = require('../helpers/validator'); | ||
|
||
const DEFAULT_NEW_DELEGATE_RETRIES = 4; // How much re-tries for send tokens requests by default. Total 4+1 tries | ||
|
||
module.exports = (nodeManager) => { | ||
/** | ||
* Registers user account as delegate | ||
* @param {string} passPhrase Senders's passPhrase. Sender's address will be derived from it. | ||
* @param {string} username Delegate name you want to register with. | ||
* It must be unique in ADAMANT blockchain. It should not be similar to ADAMANT address. | ||
* Delegate name can only contain alphanumeric characters and symbols !@$&_. | ||
* @param {number} maxRetries How much times to retry request | ||
* @returns {Promise} Request results | ||
*/ | ||
return async (passPhrase, username, maxRetries = DEFAULT_NEW_DELEGATE_RETRIES, retryNo = 0) => { | ||
|
||
let transaction; | ||
|
||
try { | ||
if (!validator.validatePassPhrase(passPhrase)) { | ||
return validator.badParameter('passPhrase'); | ||
} | ||
|
||
const keyPair = keys.createKeypairFromPassPhrase(passPhrase); | ||
|
||
if (!validator.validateDelegateName(username)) { | ||
return validator.badParameter('username'); | ||
} | ||
|
||
const type = constants.transactionTypes.DELEGATE; | ||
|
||
const data = { | ||
type, | ||
keyPair, | ||
username, | ||
}; | ||
|
||
transaction = transactionFormer.createTransaction(type, data); | ||
|
||
} catch (e) { | ||
return validator.badParameter('#exception_catched#', e); | ||
} | ||
|
||
const url = nodeManager.node() + '/api/delegates'; | ||
|
||
try { | ||
const response = await axios.post(url, transaction); | ||
|
||
return validator.formatRequestResults(response, true); | ||
} catch (error) { | ||
const logMessage = `[ADAMANT js-api] New delegate request: Request to ${url} failed with ${error.response ? error.response.status : undefined} status code, ${error.toString()}${error.response && error.response.data ? '. Message: ' + error.response.data.toString().trim() : ''}. Try ${retryNo+1} of ${maxRetries+1}.`; | ||
|
||
if (retryNo < maxRetries) { | ||
logger.log(`${logMessage} Retrying…`); | ||
|
||
return nodeManager.changeNodes() | ||
.then(() => ( | ||
module.exports(nodeManager)(passPhrase, addressOrPublicKey, amount, isAmountInADM, maxRetries, ++retryNo) | ||
)); | ||
} | ||
|
||
logger.warn(`${logMessage} No more attempts, returning error.`); | ||
|
||
return validator.formatRequestResults(error, false); | ||
} | ||
} | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
const axios = require('axios'); | ||
const get = require('./get'); | ||
const logger = require('../helpers/logger'); | ||
const keys = require('../helpers/keys'); | ||
const constants = require('../helpers/constants'); | ||
const transactionFormer = require('../helpers/transactionFormer'); | ||
const validator = require('../helpers/validator'); | ||
|
||
const DEFAULT_VOTE_FOR_DELEGATE_RETRIES = 4; // How much re-tries for send tokens requests by default. Total 4+1 tries | ||
|
||
const publicKeysCache = { }; | ||
|
||
module.exports = (nodeManager) => { | ||
/** | ||
* Creates votes for delegate transaction, signs it, and broadcasts to ADAMANT network | ||
* See https://github.com/Adamant-im/adamant/wiki/Transaction-Types#type-3-vote-for-delegate-transaction | ||
* @param {string} passPhrase Senders's passPhrase. Sender's address will be derived from it. | ||
* @param {string[]} votes PublicKeys, ADM addresses and delegate names for upvote and downvote. | ||
* It would be more efficient to pass publicKey, otherwise the api will make additional queries | ||
* @param {number} maxRetries How much times to retry request | ||
* @returns {Promise} Request results | ||
*/ | ||
return async (passPhrase, votes, maxRetries = DEFAULT_VOTE_FOR_DELEGATE_RETRIES, retryNo = 0) => { | ||
|
||
let transaction; | ||
|
||
try { | ||
if (!validator.validatePassPhrase(passPhrase)) { | ||
return validator.badParameter('passPhrase'); | ||
} | ||
|
||
const keyPair = keys.createKeypairFromPassPhrase(passPhrase); | ||
|
||
const uniqueVotes = []; | ||
|
||
for (let i = votes.length - 1; i >= 0; i--) { | ||
const vote = votes[i]; | ||
const voteName = vote.slice(1); | ||
const voteDirection = vote.charAt(0); | ||
|
||
const cachedPublicKey = publicKeysCache[voteName]; | ||
|
||
if (cachedPublicKey) { | ||
votes[i] = `${voteDirection}${cachedPublicKey}`; | ||
} else { | ||
if (validator.validateAdmVoteForAddress(vote)) { | ||
const res = await get(nodeManager)('/accounts', { address: voteName }); | ||
|
||
if (res.success) { | ||
const publicKey = res.data.account.publicKey; | ||
|
||
votes[i] = `${voteDirection}${publicKey}`; | ||
publicKeysCache[voteName] = publicKey; | ||
} else { | ||
logger.warn(`[ADAMANT js-api] Failed to get public key for ${vote}. ${res.errorMessage}.`); | ||
|
||
return validator.badParameter('votes'); | ||
} | ||
} else if (validator.validateAdmVoteForDelegateName(vote)) { | ||
const res = await get(nodeManager)('/delegates/get', { username: voteName }); | ||
|
||
if (res.success) { | ||
const publicKey = res.data.delegate.publicKey; | ||
|
||
votes[i] = `${voteDirection}${publicKey}`; | ||
publicKeysCache[voteName] = publicKey; | ||
} else { | ||
logger.warn(`[ADAMANT js-api] Failed to get public key for ${vote}. ${res.errorMessage}.`); | ||
|
||
return validator.badParameter('votes'); | ||
} | ||
} else if (!validator.validateAdmVoteForPublicKey(vote)) { | ||
return validator.badParameter('votes'); | ||
} | ||
} | ||
|
||
// Exclude duplicates | ||
const foundCopy = uniqueVotes.find((v) => v.slice(1) === votes[i].slice(1)); | ||
|
||
if (!foundCopy) { | ||
uniqueVotes.push(votes[i]); | ||
} | ||
} | ||
|
||
const type = constants.transactionTypes.VOTE; | ||
|
||
const data = { | ||
type, | ||
keyPair, | ||
votes: uniqueVotes, | ||
}; | ||
|
||
transaction = transactionFormer.createTransaction(type, data); | ||
} catch (error) { | ||
return validator.badParameter('#exception_catched#', error) | ||
} | ||
|
||
const url = nodeManager.node() + '/api/accounts/delegates'; | ||
|
||
try { | ||
const response = await axios.post(url, transaction); | ||
|
||
return validator.formatRequestResults(response, true); | ||
} catch(error) { | ||
const logMessage = `[ADAMANT js-api] Vote for delegate request: Request to ${url} failed with ${error.response ? error.response.status : undefined} status code, ${error.toString()}${error.response && error.response.data ? '. Message: ' + error.response.data.toString().trim() : ''}. Try ${retryNo+1} of ${maxRetries+1}.`; | ||
|
||
if (retryNo < maxRetries) { | ||
logger.log(`${logMessage} Retrying…`); | ||
|
||
return nodeManager.changeNodes() | ||
.then(() => ( | ||
module.exports(nodeManager)(passPhrase, addressOrPublicKey, amount, isAmountInADM, maxRetries, ++retryNo) | ||
)); | ||
} | ||
|
||
logger.warn(`${logMessage} No more attempts, returning error.`); | ||
|
||
return validator.formatRequestResults(error, false); | ||
} | ||
} | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,6 @@ | ||
{ | ||
"name": "adamant-api", | ||
"version": "1.3.0", | ||
"version": "1.4.0", | ||
"description": "REST API for ADAMANT Blockchain", | ||
"main": "index.js", | ||
"scripts": { | ||
|
@@ -9,14 +9,14 @@ | |
"author": "RomanS, Aleksei Lebedev <[email protected]> (https://adamant.im)", | ||
"license": "GPL-3.0", | ||
"dependencies": { | ||
"axios": "^0.23.0", | ||
"bignumber.js": "^9.0.1", | ||
"axios": "^0.25.0", | ||
"bignumber.js": "^9.0.2", | ||
"bitcoinjs-lib": "^5.2.0", | ||
"bitcore-mnemonic": "^8.25.10", | ||
"bitcore-mnemonic": "^8.25.25", | ||
"bytebuffer": "^5.0.1", | ||
"coininfo": "^5.1.0", | ||
"ed2curve": "^0.3.0", | ||
"ethereumjs-util": "^7.1.3", | ||
"ethereumjs-util": "^7.1.4", | ||
"hdkey": "^2.0.1", | ||
"socket.io-client": "^2.4.0", | ||
"sodium-browserify-tweetnacl": "^0.2.6" | ||
|