mirror of
https://github.com/transatoshi-mw/grin-web-wallet.git
synced 2025-10-06 15:52:47 +00:00
978 lines
26 KiB
JavaScript
Executable File
978 lines
26 KiB
JavaScript
Executable File
// Use strict
|
|
"use strict";
|
|
|
|
|
|
// Classes
|
|
|
|
// Recent heights class
|
|
class RecentHeights {
|
|
|
|
// Public
|
|
|
|
// Constructor
|
|
constructor(node) {
|
|
|
|
// Set node
|
|
this.node = node;
|
|
|
|
// Set heights
|
|
this.heights = [];
|
|
|
|
// Set current heights
|
|
this.currentHeights = [];
|
|
|
|
// Set heights changed
|
|
this.heightsChanged = false;
|
|
|
|
// Set initial heights obtained
|
|
this.initialHeightsObtained = new InitialHeightsObtained();
|
|
|
|
// Create database
|
|
Database.createDatabase(function(database, currentVersion, databaseTransaction) {
|
|
|
|
// Create or get recent heights object store
|
|
var recentHeightsObjectStore = (currentVersion === Database.NO_CURRENT_VERSION) ? database.createObjectStore(RecentHeights.OBJECT_STORE_NAME, {
|
|
|
|
// Key path
|
|
"keyPath": [
|
|
|
|
// Wallet type
|
|
Database.toKeyPath(RecentHeights.DATABASE_WALLET_TYPE_NAME),
|
|
|
|
// Network type
|
|
Database.toKeyPath(RecentHeights.DATABASE_NETWORK_TYPE_NAME),
|
|
|
|
// Height
|
|
Database.toKeyPath(RecentHeights.DATABASE_HEIGHT_NAME)
|
|
]
|
|
|
|
}) : databaseTransaction.objectStore(RecentHeights.OBJECT_STORE_NAME);
|
|
|
|
// Check if no database version exists
|
|
if(currentVersion === Database.NO_CURRENT_VERSION) {
|
|
|
|
// Create index to search recent heights object store by wallet type and network type
|
|
recentHeightsObjectStore.createIndex(RecentHeights.DATABASE_WALLET_TYPE_AND_NETWORK_TYPE_NAME, [
|
|
|
|
// Wallet Type
|
|
Database.toKeyPath(RecentHeights.DATABASE_WALLET_TYPE_NAME),
|
|
|
|
// Network Type
|
|
Database.toKeyPath(RecentHeights.DATABASE_NETWORK_TYPE_NAME)
|
|
], {
|
|
|
|
// Unique
|
|
"unique": false
|
|
});
|
|
}
|
|
});
|
|
|
|
// Set self
|
|
var self = this;
|
|
|
|
// Once database is initialized
|
|
Database.onceInitialized(function() {
|
|
|
|
// Return promise
|
|
return new Promise(function(resolve, reject) {
|
|
|
|
// Return getting the recent heights with the wallet type and network type in the database
|
|
return Database.getResults(RecentHeights.OBJECT_STORE_NAME, Database.GET_ALL_RESULTS, Database.GET_ALL_RESULTS, RecentHeights.DATABASE_WALLET_TYPE_AND_NETWORK_TYPE_NAME, IDBKeyRange.only([
|
|
|
|
// Wallet type
|
|
Consensus.getWalletType(),
|
|
|
|
// Network type
|
|
Consensus.getNetworkType()
|
|
|
|
])).then(function(results) {
|
|
|
|
// Go through all recent heights while not exceeding the max number of recent heights
|
|
for(var i = 0; i < results["length"] && self.heights["length"] < RecentHeights.MAXIMUM_NUMBER_OF_RECENT_HEIGHTS; ++i) {
|
|
|
|
// Get height from result
|
|
var height = RecentHeights.getHeightFromResult(results[i]);
|
|
|
|
// Check if height is valid
|
|
if(height.getHeight().isGreaterThanOrEqualTo(Consensus.FIRST_BLOCK_HEIGHT) === true)
|
|
|
|
// Append height to list of heights
|
|
self.heights.push(height);
|
|
}
|
|
|
|
// Sort heights in descending order
|
|
self.heights.sort(function(firstHeight, secondHeight) {
|
|
|
|
// Check if first height is less than the second height
|
|
if(firstHeight.getHeight().isLessThan(secondHeight.getHeight()) === true)
|
|
|
|
// Return sort greater than
|
|
return Common.SORT_GREATER_THAN;
|
|
|
|
// Check if first height is greater than the second height
|
|
if(firstHeight.getHeight().isGreaterThan(secondHeight.getHeight()) === true)
|
|
|
|
// Return sort less than
|
|
return Common.SORT_LESS_THAN;
|
|
|
|
// Return sort equal
|
|
return Common.SORT_EQUAL;
|
|
});
|
|
|
|
// Store current heights
|
|
self.storeCurrentHeights();
|
|
|
|
// Resolve
|
|
resolve();
|
|
|
|
// Catch errors
|
|
}).catch(function(error) {
|
|
|
|
// Reject
|
|
reject();
|
|
});
|
|
});
|
|
});
|
|
}
|
|
|
|
// Get highest verified height
|
|
getHighestVerifiedHeight(tipHeight) {
|
|
|
|
// Retore current heights
|
|
this.restoreCurrentHeights();
|
|
|
|
// Set self
|
|
var self = this;
|
|
|
|
// Return promise
|
|
return new Promise(function(resolve, reject) {
|
|
|
|
// Set get initial heights
|
|
var getInitialHeights = new Promise(function(resolve, reject) {
|
|
|
|
// Return getting if initial heights were obtained
|
|
return self.initialHeightsObtained.getObtained().then(function(obtained) {
|
|
|
|
// Check if initial heights weren't obtained
|
|
if(obtained === false) {
|
|
|
|
// Clear heights
|
|
self.heights = [];
|
|
|
|
// Return saving heights
|
|
return self.saveHeights(tipHeight).then(function() {
|
|
|
|
// Return setting that initial heights were obtained
|
|
return self.initialHeightsObtained.setObtained().then(function() {
|
|
|
|
// Resolve
|
|
resolve();
|
|
|
|
// Catch errors
|
|
}).catch(function(error) {
|
|
|
|
// Clear heights
|
|
self.heights = [];
|
|
|
|
// Return deleting recent heights with the wallet type and network type in the database
|
|
return Database.deleteResultsWithValue(RecentHeights.OBJECT_STORE_NAME, RecentHeights.DATABASE_WALLET_TYPE_AND_NETWORK_TYPE_NAME, IDBKeyRange.only([
|
|
|
|
// Wallet type
|
|
Consensus.getWalletType(),
|
|
|
|
// Network type
|
|
Consensus.getNetworkType()
|
|
|
|
]), Database.CREATE_NEW_TRANSACTION, Database.STRICT_DURABILITY).catch(function(error) {
|
|
|
|
// Finally
|
|
}).finally(function() {
|
|
|
|
// Reject error
|
|
reject(error);
|
|
});
|
|
});
|
|
|
|
// Catch errors
|
|
}).catch(function(error) {
|
|
|
|
// Clear heights
|
|
self.heights = [];
|
|
|
|
// Return deleting recent heights with the wallet type and network type in the database
|
|
return Database.deleteResultsWithValue(RecentHeights.OBJECT_STORE_NAME, RecentHeights.DATABASE_WALLET_TYPE_AND_NETWORK_TYPE_NAME, IDBKeyRange.only([
|
|
|
|
// Wallet type
|
|
Consensus.getWalletType(),
|
|
|
|
// Network type
|
|
Consensus.getNetworkType()
|
|
|
|
]), Database.CREATE_NEW_TRANSACTION, Database.STRICT_DURABILITY).catch(function(error) {
|
|
|
|
// Finally
|
|
}).finally(function() {
|
|
|
|
// Reject error
|
|
reject(error);
|
|
});
|
|
});
|
|
}
|
|
|
|
// Otherwise
|
|
else {
|
|
|
|
// Resolve
|
|
resolve();
|
|
}
|
|
|
|
// Catch errors
|
|
}).catch(function(error) {
|
|
|
|
// Reject error
|
|
reject(error);
|
|
});
|
|
});
|
|
|
|
// Return getting initial heights
|
|
return getInitialHeights.then(function() {
|
|
|
|
// Set highest verified height to no verified height
|
|
var highestVerifiedHeight = RecentHeights.NO_VERIFIED_HEIGHT;
|
|
|
|
// Set reorg occurred to true if heights exist
|
|
var reorgOccurred = self.heights["length"] !== 0;
|
|
|
|
// Set verifying height to promise
|
|
var verifyingHeight = new Promise(function(resolve, reject) {
|
|
|
|
// Resolve
|
|
resolve();
|
|
});
|
|
|
|
// Initialize verifying heights
|
|
var verifyingHeights = [verifyingHeight];
|
|
|
|
// Go through all heights from highest to lowest
|
|
var verifiedHeightFound = false;
|
|
for(let i = 0; i < self.heights["length"]; ++i) {
|
|
|
|
// Get height
|
|
let height = self.heights[i];
|
|
|
|
// Set verifying height to verify current height after previous height is done being verified
|
|
verifyingHeight = verifyingHeight.then(function() {
|
|
|
|
// Return promise
|
|
return new Promise(function(resolve, reject) {
|
|
|
|
// Check if a verified height was already found
|
|
if(verifiedHeightFound === true)
|
|
|
|
// Resolve
|
|
resolve();
|
|
|
|
// Otherwise
|
|
else {
|
|
|
|
// Return verifying height
|
|
return self.verifyHeight(height, tipHeight).then(function(verified) {
|
|
|
|
// Check if height is verified
|
|
if(verified === true) {
|
|
|
|
// Set highest verified height to height
|
|
highestVerifiedHeight = height.getHeight();
|
|
|
|
// Remove invalid heights
|
|
self.heights.splice(0, i);
|
|
|
|
// Set that a verified height was found
|
|
verifiedHeightFound = true;
|
|
|
|
// Check if highest height is verified
|
|
if(i === 0)
|
|
|
|
// Clear reorg occurred
|
|
reorgOccurred = false;
|
|
}
|
|
|
|
// Otherwise check if no saved heights are valid
|
|
else if(i === self.heights["length"] - 1)
|
|
|
|
// Clear all heights
|
|
self.heights = [];
|
|
|
|
// Resolve
|
|
resolve();
|
|
|
|
// Catch errors
|
|
}).catch(function(error) {
|
|
|
|
// Reject error
|
|
reject(error);
|
|
});
|
|
}
|
|
});
|
|
|
|
// Catch errors
|
|
}).catch(function(error) {
|
|
|
|
// Return promise
|
|
return new Promise(function(resolve, reject) {
|
|
|
|
// Reject error
|
|
reject(error);
|
|
});
|
|
});
|
|
|
|
// Append verifying height to list
|
|
verifyingHeights.push(verifyingHeight);
|
|
}
|
|
|
|
// Wait until a verified height has been found
|
|
return Promise.all(verifyingHeights).then(function() {
|
|
|
|
// Resolve highest verified height
|
|
resolve([
|
|
|
|
// Highest verified height
|
|
highestVerifiedHeight,
|
|
|
|
// Reorg occurred
|
|
reorgOccurred
|
|
]);
|
|
|
|
// Catch errors
|
|
}).catch(function(error) {
|
|
|
|
// Reject error
|
|
reject(error);
|
|
});
|
|
|
|
// Catch errors
|
|
}).catch(function(error) {
|
|
|
|
// Reject error
|
|
reject(error);
|
|
});
|
|
});
|
|
}
|
|
|
|
// Get highest height
|
|
getHighestHeight() {
|
|
|
|
// Return highest current height without verifying it again or no height if not available
|
|
return (this.currentHeights["length"] !== 0) ? this.currentHeights[0].getHeight() : RecentHeights.NO_HEIGHT;
|
|
}
|
|
|
|
// Save heights
|
|
saveHeights(tipHeight) {
|
|
|
|
// Store current heights
|
|
this.storeCurrentHeights();
|
|
|
|
// Set self
|
|
var self = this;
|
|
|
|
// Return promise
|
|
return new Promise(function(resolve, reject) {
|
|
|
|
// Return updating heights
|
|
return self.updateHeights(tipHeight).then(function() {
|
|
|
|
// Store current heights
|
|
self.storeCurrentHeights();
|
|
|
|
// Check if heights changed
|
|
if(self.heightsChanged === true) {
|
|
|
|
// Clear heights changed
|
|
self.heightsChanged = false;
|
|
|
|
// Return creating database transaction
|
|
return Database.createTransaction(RecentHeights.OBJECT_STORE_NAME, Database.READ_AND_WRITE_MODE, Database.STRICT_DURABILITY).then(function(transaction) {
|
|
|
|
// Return deleting recent heights with the wallet type and network type in the database
|
|
return Database.deleteResultsWithValue(RecentHeights.OBJECT_STORE_NAME, RecentHeights.DATABASE_WALLET_TYPE_AND_NETWORK_TYPE_NAME, IDBKeyRange.only([
|
|
|
|
// Wallet type
|
|
Consensus.getWalletType(),
|
|
|
|
// Network type
|
|
Consensus.getNetworkType()
|
|
|
|
]), transaction, Database.STRICT_DURABILITY).then(function() {
|
|
|
|
// Return saving all recent heights in the database
|
|
return Database.saveResults(RecentHeights.OBJECT_STORE_NAME, self.heights.map(function(height) {
|
|
|
|
// Return height as result
|
|
return {
|
|
|
|
// Wallet Type
|
|
[Database.toKeyPath(RecentHeights.DATABASE_WALLET_TYPE_NAME)]: Consensus.getWalletType(),
|
|
|
|
// Network type
|
|
[Database.toKeyPath(RecentHeights.DATABASE_NETWORK_TYPE_NAME)]: Consensus.getNetworkType(),
|
|
|
|
// Height
|
|
[Database.toKeyPath(RecentHeights.DATABASE_HEIGHT_NAME)]: height.getHeight().toFixed(),
|
|
|
|
// Hash
|
|
[Database.toKeyPath(RecentHeights.DATABASE_HASH_NAME)]: height.getHash()
|
|
};
|
|
|
|
}), [], transaction, Database.STRICT_DURABILITY).then(function() {
|
|
|
|
// Return committing database transaction
|
|
return Database.commitTransaction(transaction).then(function() {
|
|
|
|
// Resolve
|
|
resolve();
|
|
|
|
// Catch errors
|
|
}).catch(function(error) {
|
|
|
|
// Return aborting database transaction
|
|
return Database.abortTransaction(transaction).then(function() {
|
|
|
|
// Reject error
|
|
reject("The database failed.");
|
|
|
|
// Catch errors
|
|
}).catch(function(error) {
|
|
|
|
// Trigger a fatal error
|
|
new FatalError(FatalError.DATABASE_ERROR);
|
|
});
|
|
});
|
|
|
|
// Catch errors
|
|
}).catch(function(error) {
|
|
|
|
// Return aborting database transaction
|
|
return Database.abortTransaction(transaction).then(function() {
|
|
|
|
// Reject error
|
|
reject("The database failed.");
|
|
|
|
// Catch errors
|
|
}).catch(function(error) {
|
|
|
|
// Trigger a fatal error
|
|
new FatalError(FatalError.DATABASE_ERROR);
|
|
});
|
|
});
|
|
|
|
// Catch errors
|
|
}).catch(function(error) {
|
|
|
|
// Return aborting database transaction
|
|
return Database.abortTransaction(transaction).then(function() {
|
|
|
|
// Reject error
|
|
reject("The database failed.");
|
|
|
|
// Catch errors
|
|
}).catch(function(error) {
|
|
|
|
// Trigger a fatal error
|
|
new FatalError(FatalError.DATABASE_ERROR);
|
|
});
|
|
});
|
|
|
|
// Catch errors
|
|
}).catch(function(error) {
|
|
|
|
// Reject error
|
|
reject("The database failed.");
|
|
});
|
|
}
|
|
|
|
// Otherwise
|
|
else
|
|
|
|
// Resolve
|
|
resolve();
|
|
|
|
// Catch errors
|
|
}).catch(function(error) {
|
|
|
|
// Reject error
|
|
reject(error);
|
|
});
|
|
});
|
|
}
|
|
|
|
// No verified height
|
|
static get NO_VERIFIED_HEIGHT() {
|
|
|
|
// Return no verified height
|
|
return null;
|
|
}
|
|
|
|
// No height
|
|
static get NO_HEIGHT() {
|
|
|
|
// Return no height
|
|
return null;
|
|
}
|
|
|
|
// Highest verified height index
|
|
static get HIGHEST_VERIFIED_HEIGHT_INDEX() {
|
|
|
|
// Return highest verified hight index
|
|
return 0;
|
|
}
|
|
|
|
// Reorg occurred index
|
|
static get REORG_OCCURRED_INDEX() {
|
|
|
|
// Return reorg occurred index
|
|
return RecentHeights.HIGHEST_VERIFIED_HEIGHT_INDEX + 1;
|
|
}
|
|
|
|
// Header hash length
|
|
static get HEADER_HASH_LENGTH() {
|
|
|
|
// Return header hash length
|
|
return 32;
|
|
}
|
|
|
|
// Private
|
|
|
|
// Verify height
|
|
verifyHeight(height, tipHeight) {
|
|
|
|
// Set self
|
|
var self = this;
|
|
|
|
// Return promise
|
|
return new Promise(function(resolve, reject) {
|
|
|
|
// Check if tip height is greater than or equal to the height
|
|
if(tipHeight.getHeight().isGreaterThanOrEqualTo(height.getHeight()) === true) {
|
|
|
|
// Set get hash
|
|
var getHash = new Promise(function(resolve, reject) {
|
|
|
|
// Check if height is equal to the tip height
|
|
if(height.getHeight().isEqualTo(tipHeight.getHeight()) === true) {
|
|
|
|
// Set hash to tip height's hash
|
|
var hash = tipHeight.getHash();
|
|
|
|
// Resolve hash
|
|
resolve(hash);
|
|
}
|
|
|
|
// Otherwise
|
|
else {
|
|
|
|
// Return getting node's header at height
|
|
return self.node.getHeader(height.getHeight()).then(function(header) {
|
|
|
|
// Resolve header
|
|
resolve((header !== Node.NO_HEADER_FOUND) ? header["hash"] : header);
|
|
|
|
// Catch errors
|
|
}).catch(function(error) {
|
|
|
|
// Reject error
|
|
reject(error);
|
|
});
|
|
}
|
|
});
|
|
|
|
// Return getting hash
|
|
return getHash.then(function(hash) {
|
|
|
|
// Check if no header for the height
|
|
if(hash === Node.NO_HEADER_FOUND)
|
|
|
|
// Resolve false
|
|
resolve(false);
|
|
|
|
// Otherwise
|
|
else
|
|
|
|
// Resolve if height's hash didn't change
|
|
resolve(Common.arraysAreEqual(hash, height.getHash()) === true);
|
|
|
|
// Catch errors
|
|
}).catch(function(error) {
|
|
|
|
// Reject error
|
|
reject(error);
|
|
});
|
|
}
|
|
|
|
// Otherwise
|
|
else
|
|
|
|
// Resolve false
|
|
resolve(false);
|
|
});
|
|
}
|
|
|
|
// Update heights
|
|
updateHeights(tipHeight) {
|
|
|
|
// Set self
|
|
var self = this;
|
|
|
|
// Return promise
|
|
return new Promise(function(resolve, reject) {
|
|
|
|
// Initialize new heights
|
|
var newHeights = [];
|
|
|
|
// Initialize get heights
|
|
var getHeights = [];
|
|
|
|
// Go through the max number of recent heights or until a first block height is used
|
|
var firstBlockHeightUsed = false;
|
|
for(let i = 0; i < RecentHeights.MAXIMUM_NUMBER_OF_RECENT_HEIGHTS && firstBlockHeightUsed === false; ++i) {
|
|
|
|
// Get minimum and maximum age in seconds for the height at this index
|
|
var minimumAgeInSeconds = (i !== 0) ? RecentHeights.getMinimumAgeAtIndex(i - 1) : 0;
|
|
|
|
var maximumAgeInSeconds = RecentHeights.getMinimumAgeAtIndex(i) - 1;
|
|
|
|
// Get ideal height from minimum age
|
|
let idealHeight = tipHeight.getHeight().minus(Math.ceil(minimumAgeInSeconds / Consensus.BLOCK_TIME_SECONDS));
|
|
|
|
// Check if heights exist
|
|
if(self.heights["length"] !== 0) {
|
|
|
|
// Go through all heights
|
|
for(var j = 0; j < self.heights["length"]; ++j) {
|
|
|
|
// Get height
|
|
var height = self.heights[j];
|
|
|
|
// Get height's age in seconds
|
|
var ageInSeconds = tipHeight.getHeight().minus(height.getHeight()).multipliedBy(Consensus.BLOCK_TIME_SECONDS);
|
|
|
|
// Check if height isn't too new or old for this index or height and ideal height are both the first block height
|
|
if((ageInSeconds.isGreaterThanOrEqualTo(minimumAgeInSeconds) === true && ageInSeconds.isLessThanOrEqualTo(maximumAgeInSeconds) === true) || (idealHeight.isLessThanOrEqualTo(Consensus.FIRST_BLOCK_HEIGHT) === true && height.getHeight().isEqualTo(Consensus.FIRST_BLOCK_HEIGHT) === true)) {
|
|
|
|
// Check if indexes differ
|
|
if(i !== j)
|
|
|
|
// Set heights changed
|
|
self.heightsChanged = true;
|
|
|
|
// Check if height is equal to the first block height
|
|
if(height.getHeight().isEqualTo(Consensus.FIRST_BLOCK_HEIGHT) === true)
|
|
|
|
// Set first block height used
|
|
firstBlockHeightUsed = true;
|
|
|
|
// Set height in new heights at index
|
|
newHeights[i] = height;
|
|
|
|
// Break
|
|
break;
|
|
}
|
|
|
|
// Otherwise check if no heights have the correct age for this index
|
|
else if(j === self.heights["length"] - 1) {
|
|
|
|
// Check if ideal height is less than or equal to the first block height
|
|
if(idealHeight.isLessThanOrEqualTo(Consensus.FIRST_BLOCK_HEIGHT) === true) {
|
|
|
|
// Check if first block height isn't used
|
|
if(firstBlockHeightUsed === false) {
|
|
|
|
// Set first block height used
|
|
firstBlockHeightUsed = true;
|
|
|
|
// Set ideal height to the first block height
|
|
idealHeight = new BigNumber(Consensus.FIRST_BLOCK_HEIGHT);
|
|
|
|
// Set heights changed
|
|
self.heightsChanged = true;
|
|
}
|
|
|
|
// Otherwise
|
|
else
|
|
|
|
// Break
|
|
break;
|
|
}
|
|
|
|
// Otherwise
|
|
else
|
|
|
|
// Set heights changed
|
|
self.heightsChanged = true;
|
|
|
|
// Append get height to list
|
|
getHeights.push(new Promise(function(resolve, reject) {
|
|
|
|
// Check if the ideal height is equal to the tip height
|
|
if(idealHeight.isEqualTo(tipHeight.getHeight()) === true) {
|
|
|
|
// Set new height at index to the tip height
|
|
newHeights[i] = new Height(tipHeight.getHeight(), tipHeight.getHash());
|
|
|
|
// Resolve
|
|
resolve();
|
|
}
|
|
|
|
// Otherwise
|
|
else {
|
|
|
|
// Return getting node's header for the ideal height
|
|
return self.node.getHeader(idealHeight).then(function(header) {
|
|
|
|
// Check if no header exists for the height
|
|
if(header === Node.NO_HEADER_FOUND)
|
|
|
|
// Reject error
|
|
reject("Height not found.");
|
|
|
|
// Otherwise
|
|
else {
|
|
|
|
// Set height in new heights at index
|
|
newHeights[i] = new Height(idealHeight, header["hash"]);
|
|
|
|
// Resolve
|
|
resolve();
|
|
}
|
|
|
|
// Catch errors
|
|
}).catch(function(error) {
|
|
|
|
// Reject error
|
|
reject(error);
|
|
});
|
|
}
|
|
}));
|
|
}
|
|
}
|
|
}
|
|
|
|
// Otherwise
|
|
else {
|
|
|
|
// Check if ideal height is less than or equal to the first block height
|
|
if(idealHeight.isLessThanOrEqualTo(Consensus.FIRST_BLOCK_HEIGHT) === true) {
|
|
|
|
// Check if first block height isn't used
|
|
if(firstBlockHeightUsed === false) {
|
|
|
|
// Set first block height used
|
|
firstBlockHeightUsed = true;
|
|
|
|
// Set ideal height to the first block height
|
|
idealHeight = new BigNumber(Consensus.FIRST_BLOCK_HEIGHT);
|
|
|
|
// Set heights changed
|
|
self.heightsChanged = true;
|
|
}
|
|
|
|
// Otherwise
|
|
else
|
|
|
|
// Break
|
|
break;
|
|
}
|
|
|
|
// Otherwise
|
|
else
|
|
|
|
// Set heights changed
|
|
self.heightsChanged = true;
|
|
|
|
// Append getting height to list
|
|
getHeights.push(new Promise(function(resolve, reject) {
|
|
|
|
// Check if the ideal height is equal to the tip height
|
|
if(idealHeight.isEqualTo(tipHeight.getHeight()) === true) {
|
|
|
|
// Set new height at index to the tip height
|
|
newHeights[i] = new Height(tipHeight.getHeight(), tipHeight.getHash());
|
|
|
|
// Resolve
|
|
resolve();
|
|
}
|
|
|
|
// Otherwise
|
|
else {
|
|
|
|
// Return getting node's header for the ideal height
|
|
return self.node.getHeader(idealHeight).then(function(header) {
|
|
|
|
// Check if no header exists for the height
|
|
if(header === Node.NO_HEADER_FOUND)
|
|
|
|
// Reject
|
|
reject("Height not found.");
|
|
|
|
// Otherwise
|
|
else {
|
|
|
|
// Set height in new heights at index
|
|
newHeights[i] = new Height(idealHeight, header["hash"]);
|
|
|
|
// Resolve
|
|
resolve();
|
|
}
|
|
|
|
// Catch errors
|
|
}).catch(function(error) {
|
|
|
|
// Reject error
|
|
reject(error);
|
|
});
|
|
}
|
|
}));
|
|
}
|
|
}
|
|
|
|
// Return waiting for all heights to be obtained
|
|
return Promise.all(getHeights).then(function() {
|
|
|
|
// Get tip of new heights
|
|
var tipNewHeight = newHeights[0];
|
|
|
|
// Check if the tip of the new heights isn't equal to the tip height
|
|
if(tipNewHeight.getHeight().isEqualTo(tipHeight.getHeight()) === false || Common.arraysAreEqual(tipNewHeight.getHash(), tipHeight.getHash()) === false) {
|
|
|
|
// Set tip of new heights to tip height
|
|
newHeights[0] = new Height(tipHeight.getHeight(), tipHeight.getHash());
|
|
|
|
// Set heights changed
|
|
self.heightsChanged = true;
|
|
}
|
|
|
|
// Set heights to new heights
|
|
self.heights = newHeights;
|
|
|
|
// Resolve
|
|
resolve();
|
|
|
|
// Catch errors
|
|
}).catch(function(error) {
|
|
|
|
// Reject error
|
|
reject(error);
|
|
});
|
|
});
|
|
}
|
|
|
|
// Store current heights
|
|
storeCurrentHeights() {
|
|
|
|
// Clear current heights
|
|
this.currentHeights = [];
|
|
|
|
// Go through all heights
|
|
for(var i = 0; i < this.heights["length"]; ++i) {
|
|
|
|
// Get height
|
|
var height = this.heights[i];
|
|
|
|
// Copy height to current heights
|
|
this.currentHeights.push(new Height(height.getHeight(), height.getHash()));
|
|
}
|
|
}
|
|
|
|
// Retore current heights
|
|
restoreCurrentHeights() {
|
|
|
|
// Clear heights
|
|
this.heights = [];
|
|
|
|
// Go through all current heights
|
|
for(var i = 0; i < this.currentHeights["length"]; ++i) {
|
|
|
|
// Get current height
|
|
var currentHeight = this.currentHeights[i];
|
|
|
|
// Copy current height to heights
|
|
this.heights.push(new Height(currentHeight.getHeight(), currentHeight.getHash()));
|
|
}
|
|
}
|
|
|
|
// Get minimum age at index
|
|
static getMinimumAgeAtIndex(index) {
|
|
|
|
// Return minimum age at index in seconds
|
|
return Math.pow((index > 2) ? 3 : 2, (index > 2) ? index - 1 : index) * Consensus.BLOCK_TIME_SECONDS;
|
|
}
|
|
|
|
// Get height from result
|
|
static getHeightFromResult(result) {
|
|
|
|
// Return height from result
|
|
return new Height(
|
|
|
|
// Height
|
|
new BigNumber(result[Database.toKeyPath(RecentHeights.DATABASE_HEIGHT_NAME)]),
|
|
|
|
// Hash
|
|
result[Database.toKeyPath(RecentHeights.DATABASE_HASH_NAME)]
|
|
);
|
|
}
|
|
|
|
// Object store name
|
|
static get OBJECT_STORE_NAME() {
|
|
|
|
// Return object store name
|
|
return "Recent Heights";
|
|
}
|
|
|
|
// Database wallet type name
|
|
static get DATABASE_WALLET_TYPE_NAME() {
|
|
|
|
// Return database wallet type name
|
|
return "Wallet Type";
|
|
}
|
|
|
|
// Database network type name
|
|
static get DATABASE_NETWORK_TYPE_NAME() {
|
|
|
|
// Return database network type name
|
|
return "Network Type";
|
|
}
|
|
|
|
// Database height name
|
|
static get DATABASE_HEIGHT_NAME() {
|
|
|
|
// Return database height name
|
|
return "Height";
|
|
}
|
|
|
|
// Database hash name
|
|
static get DATABASE_HASH_NAME() {
|
|
|
|
// Return database hash name
|
|
return "Hash";
|
|
}
|
|
|
|
// Database wallet type and network type name
|
|
static get DATABASE_WALLET_TYPE_AND_NETWORK_TYPE_NAME() {
|
|
|
|
// Return database wallet type and network type name
|
|
return "Wallet Type And Network Type";
|
|
}
|
|
|
|
// Maximum number of recent heights
|
|
static get MAXIMUM_NUMBER_OF_RECENT_HEIGHTS() {
|
|
|
|
// Return the maximum number of recent heights
|
|
return 13;
|
|
}
|
|
}
|
|
|
|
|
|
// Main function
|
|
|
|
// Set global object's recent heights
|
|
globalThis["RecentHeights"] = RecentHeights;
|