Base commit with data from AEON.

master
Alexander Blair 7 years ago committed by Alexander Blair
parent 2b44273e94
commit ca87ba3595

@ -0,0 +1,153 @@
"use strict";
const bignum = require('bignum');
const cnUtil = require('cryptonote-util');
const multiHashing = require('multi-hashing');
const crypto = require('crypto');
const debug = require('debug')('coinFuncs');
let hexChars = new RegExp("[0-9a-f]+");
function Coin(data){
this.bestExchange = global.config.payout.bestExchange;
this.data = data;
let instanceId = crypto.randomBytes(4);
this.coinDevAddress = "WmsSWgtT1JPg5e3cK41hKXSHVpKW7e47bjgiKmWZkYrhSS5LhRemNyqayaSBtAQ6517eo5PtH9wxHVmM78JDZSUu2W8PqRiNs"; // Developer Address
this.poolDevAddress = "KgseWakG2bMXHGJSsAUfzL1HykCyvD4m8gd9qgcuyZ1ufy8PqUCKRxEfAv3nahfdTrCjZByiWoCiRiohxq4u2rf2RgQ1pcJ"; // Snipa Address
this.blockedAddresses = [
this.coinDevAddress,
this.poolDevAddress,
];
this.exchangeAddresses = [
]; // These are addresses that MUST have a paymentID to perform logins with.
this.prefix = 111;
this.supportsAutoExchange = false;
this.niceHashDiff = 200000;
this.getBlockHeaderByID = function(blockId, callback){
global.support.rpcDaemon('getblockheaderbyheight', {"height": blockId}, function (body) {
if (body.hasOwnProperty('result')){
return callback(body.result.block_header);
} else {
console.error(JSON.stringify(body));
}
});
};
this.getBlockHeaderByHash = function(blockHash, callback){
global.support.rpcDaemon('getblockheaderbyhash', {"hash": blockHash}, function (body) {
if (typeof(body) !== 'undefined' && body.hasOwnProperty('result')){
return callback(body.result.block_header);
} else {
console.error(JSON.stringify(body));
return callback(false);
}
});
};
this.getLastBlockHeader = function(callback){
global.support.rpcDaemon('getlastblockheader', [], function (body) {
if (typeof(body) !== 'undefined' && body.hasOwnProperty('result')){
return callback(body.result.block_header);
} else {
console.error(JSON.stringify(body));
}
});
};
this.getBlockTemplate = function(walletAddress, callback){
global.support.rpcDaemon('getblocktemplate', {
reserve_size: 17,
wallet_address: walletAddress
}, function(body){
return callback(body);
});
};
this.baseDiff = function(){
return bignum('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF', 16);
};
this.validateAddress = function(address){
// This function should be able to be called from the async library, as we need to BLOCK ever so slightly to verify the address.
address = new Buffer(address);
if (cnUtil.address_decode(address) === this.prefix){
return true;
}
return cnUtil.address_decode_integrated(address) === this.intPrefix;
};
this.convertBlob = function(blobBuffer){
return cnUtil.convert_blob(blobBuffer);
};
this.constructNewBlob = function(blockTemplate, NonceBuffer){
return cnUtil.construct_block_blob(blockTemplate, NonceBuffer);
};
this.getBlockID = function(blockBuffer){
return cnUtil.get_block_id(blockBuffer);
};
this.BlockTemplate = function(template) {
/*
Generating a block template is a simple thing. Ask for a boatload of information, and go from there.
Important things to consider.
The reserved space is 13 bytes long now in the following format:
Assuming that the extraNonce starts at byte 130:
|130-133|134-137|138-141|142-145|
|minerNonce/extraNonce - 4 bytes|instanceId - 4 bytes|clientPoolNonce - 4 bytes|clientNonce - 4 bytes|
This is designed to allow a single block template to be used on up to 4 billion poolSlaves (clientPoolNonce)
Each with 4 billion clients. (clientNonce)
While being unique to this particular pool thread (instanceId)
With up to 4 billion clients (minerNonce/extraNonce)
Overkill? Sure. But that's what we do here. Overkill.
*/
// Set this.blob equal to the BT blob that we get from upstream.
this.blob = template.blocktemplate_blob;
this.idHash = crypto.createHash('md5').update(template.blocktemplate_blob).digest('hex');
// Set this.diff equal to the known diff for this block.
this.difficulty = template.difficulty;
// Set this.height equal to the known height for this block.
this.height = template.height;
// Set this.reserveOffset to the byte location of the reserved offset.
this.reserveOffset = template.reserved_offset;
// Set this.buffer to the binary decoded version of the BT blob.
this.buffer = new Buffer(this.blob, 'hex');
// Copy the Instance ID to the reserve offset + 4 bytes deeper. Copy in 4 bytes.
instanceId.copy(this.buffer, this.reserveOffset + 4, 0, 3);
// Generate a clean, shiny new buffer.
this.previous_hash = new Buffer(32);
// Copy in bytes 7 through 39 to this.previous_hash from the current BT.
this.buffer.copy(this.previous_hash, 0, 7, 39);
// Reset the Nonce. - This is the per-miner/pool nonce
this.extraNonce = 0;
// The clientNonceLocation is the location at which the client pools should set the nonces for each of their clients.
this.clientNonceLocation = this.reserveOffset + 12;
// The clientPoolLocation is for multi-thread/multi-server pools to handle the nonce for each of their tiers.
this.clientPoolLocation = this.reserveOffset + 8;
this.nextBlob = function () {
// Write a 32 bit integer, big-endian style to the 0 byte of the reserve offset.
this.buffer.writeUInt32BE(++this.extraNonce, this.reserveOffset);
// Convert the blob into something hashable.
return global.coinFuncs.convertBlob(this.buffer).toString('hex');
};
// Make it so you can get the raw block blob out.
this.nextBlobWithChildNonce = function () {
// Write a 32 bit integer, big-endian style to the 0 byte of the reserve offset.
this.buffer.writeUInt32BE(++this.extraNonce, this.reserveOffset);
// Don't convert the blob to something hashable. You bad.
return this.buffer.toString('hex');
};
};
this.cryptoNight = multiHashing.cryptonight_light;
}
module.exports = Coin;

@ -0,0 +1,254 @@
"use strict";
const async = require("async");
const debug = require("debug")("payments");
let hexChars = new RegExp("[0-9a-f]+");
let extraPaymentRound = false;
let paymentTimer = null;
let paymentQueue = async.queue(function (paymentDetails, callback) {
if (paymentTimer !== null){
clearInterval(paymentTimer);
paymentTimer = null;
}
debug("Making payment based on: " + JSON.stringify(paymentDetails));
let transferFunc = 'transfer';
global.support.rpcWallet(transferFunc, paymentDetails, function (body) {
debug("Payment made: " + JSON.stringify(body));
if (body.hasOwnProperty('error')) {
if (body.error.message === "not enough money"){
console.error("Issue making payments, not enough money, will try later");
if(!extraPaymentRound){
setTimeout(function(){
makePayments();
}, global.config.payout.timerRetry * 60 * 1000);
}
extraPaymentRound = true;
return callback(false);
} else {
console.error("Issue making payments" + JSON.stringify(body.error));
console.error("Will not make more payments until the payment daemon is restarted!");
//toAddress, subject, body
global.support.sendEmail(global.config.general.adminEmail, "Payment daemon unable to make payment",
"Hello,\r\nThe payment daemon has hit an issue making a payment: " + JSON.stringify(body.error) +
". Please investigate and restart the payment daemon as appropriate");
return;
}
}
if (paymentDetails.hasOwnProperty('payment_id')) {
console.log("Payment made to " + paymentDetails.destinations[0].address + " with PaymentID: " + paymentDetails.payment_id + " For: " + global.support.coinToDecimal(paymentDetails.destinations[0].amount) + " XMR with a " + global.support.coinToDecimal(body.result.fee) + " XMR Mining Fee");
return callback(body.result);
} else {
if (transferFunc === 'transfer') {
console.log("Payment made out to multiple people, total fee: " + global.support.coinToDecimal(body.result.fee) + " XMR");
}
let intCount = 0;
paymentDetails.destinations.forEach(function (details) {
console.log("Payment made to: " + details.address + " For: " + global.support.coinToDecimal(details.amount) + " XMR");
intCount += 1;
if (intCount === paymentDetails.destinations.length) {
return callback(body.result);
}
});
}
});
}, 1);
paymentQueue.drain = function(){
extraPaymentRound = false;
if (global.config.payout.timer > 35791){
console.error("Payout timer is too high. Please use a value under 35791 to avoid overflows.");
} else {
paymentTimer = setInterval(makePayments, global.config.payout.timer * 60 * 1000);
}
global.database.setCache('lastPaymentCycle', Math.floor(Date.now()/1000));
};
function Payee(amount, address, paymentID, bitcoin) {
this.amount = amount;
this.address = address;
this.paymentID = paymentID;
this.bitcoin = bitcoin;
this.blockID = 0;
this.poolType = '';
this.transactionID = 0;
this.sqlID = 0;
if (paymentID === null) {
this.id = address;
} else {
this.id = address + "." + paymentID;
}
this.fee = 0;
this.baseFee = global.support.decimalToCoin(global.config.payout.feeSlewAmount);
this.setFeeAmount = function () {
if (this.amount <= global.support.decimalToCoin(global.config.payout.walletMin)) {
this.fee = this.baseFee;
} else if (this.amount <= global.support.decimalToCoin(global.config.payout.feeSlewEnd)) {
let feeValue = this.baseFee / (global.support.decimalToCoin(global.config.payout.feeSlewEnd) - global.support.decimalToCoin(global.config.payout.walletMin));
this.fee = this.baseFee - ((this.amount - global.support.decimalToCoin(global.config.payout.walletMin)) * feeValue);
}
this.fee = Math.floor(this.fee);
};
this.makePaymentWithID = function () {
let paymentDetails = {
destinations: [
{
amount: this.amount - this.fee,
address: this.address
}
],
priority: global.config.payout.priority,
mixin: global.config.payout.mixIn,
payment_id: this.paymentID
};
let identifier = this.id;
let amount = this.amount;
let address = this.address;
let paymentID = this.paymentID;
let payee = this;
debug("Payment Details: " + JSON.stringify(paymentDetails));
paymentQueue.push(paymentDetails, function (body) {
if (body.fee && body.fee > 10) {
debug("Successful payment sent to: " + identifier);
global.mysql.query("INSERT INTO transactions (bitcoin, address, payment_id, xmr_amt, transaction_hash, mixin, fees, payees) VALUES (?, ?, ?, ?, ?, ?, ?, ?)",
[0, address, paymentID, amount, body.tx_hash.match(hexChars)[0], global.config.payout.mixIn, body.fee, 1]).then(function (result) {
payee.transactionID = result.insertId;
payee.trackPayment();
});
} else {
console.error("Unknown error from the wallet.");
}
});
};
this.makePaymentAsIntegrated = function () {
let paymentDetails = {
destinations: [
{
amount: this.amount - this.fee,
address: this.address
}
],
priority: global.config.payout.priority,
mixin: global.config.payout.mixIn
};
let identifier = this.id;
let amount = this.amount;
let address = this.address;
let payee = this;
debug("Payment Details: " + JSON.stringify(paymentDetails));
paymentQueue.push(paymentDetails, function (body) {
if (body.fee && body.fee > 10) {
debug("Successful payment sent to: " + identifier);
global.mysql.query("INSERT INTO transactions (bitcoin, address, xmr_amt, transaction_hash, mixin, fees, payees) VALUES (?, ?, ?, ?, ?, ?, ?)",
[0, address, amount, body.tx_hash.match(hexChars)[0], global.config.payout.mixIn, body.fee, 1]).then(function (result) {
payee.transactionID = result.insertId;
payee.trackPayment();
});
} else {
console.error("Unknown error from the wallet.");
}
});
};
this.trackPayment = function () {
global.mysql.query("UPDATE balance SET amount = amount - ? WHERE id = ?", [this.amount, this.sqlID]);
global.mysql.query("INSERT INTO payments (unlocked_time, paid_time, pool_type, payment_address, transaction_id, bitcoin, amount, payment_id, transfer_fee)" +
" VALUES (now(), now(), ?, ?, ?, ?, ?, ?, ?)", [this.poolType, this.address, this.transactionID, this.bitcoin, this.amount - this.fee, this.paymentID, this.fee]);
};
}
function makePayments() {
global.mysql.query("SELECT * FROM balance WHERE amount >= ?", [global.support.decimalToCoin(global.config.payout.walletMin)]).then(function (rows) {
console.log("Loaded all payees into the system for processing");
let paymentDestinations = [];
let totalAmount = 0;
let roundCount = 0;
let payeeList = [];
let payeeObjects = {};
rows.forEach(function (row) {
debug("Starting round for: " + JSON.stringify(row));
let payee = new Payee(row.amount, row.payment_address, row.payment_id, row.bitcoin);
payeeObjects[row.payment_address] = payee;
global.mysql.query("SELECT payout_threshold FROM users WHERE username = ?", [payee.id]).then(function (userRow) {
roundCount += 1;
let threshold = 0;
if (userRow.length !== 0) {
threshold = userRow[0].payout_threshold;
}
payee.poolType = row.pool_type;
payee.sqlID = row.id;
if (payee.poolType === "fees" && payee.address === global.config.payout.feeAddress && payee.amount >= ((global.support.decimalToCoin(global.config.payout.feesForTXN) + global.support.decimalToCoin(global.config.payout.exchangeMin)))) {
debug("This is the fee address internal check for value");
payee.amount -= global.support.decimalToCoin(global.config.payout.feesForTXN);
} else if (payee.address === global.config.payout.feeAddress && payee.poolType === "fees") {
debug("Unable to pay fee address.");
payee.amount = 0;
}
let remainder = payee.amount % (global.config.payout.denom * global.config.general.sigDivisor);
if (remainder !== 0) {
payee.amount -= remainder;
}
if (payee.amount > threshold) {
payee.setFeeAmount();
if (payee.bitcoin === 0 && payee.paymentID === null && payee.amount !== 0 && payee.amount > 0 && payee.address.length !== 106) {
debug("Adding " + payee.id + " to the list of people to pay (OG Address). Payee balance: " + global.support.coinToDecimal(payee.amount));
paymentDestinations.push({amount: payee.amount - payee.fee, address: payee.address});
totalAmount += payee.amount;
payeeList.push(payee);
} else if ((payee.amount >= global.support.decimalToCoin(global.config.payout.exchangeMin) || (payee.amount > threshold && threshold !== 0)) && payee.bitcoin === 0) {
debug("Adding " + payee.id + " to the list of people to pay (Payment ID Address). Payee balance: " + global.support.coinToDecimal(payee.amount));
payee.makePaymentWithID();
}
}
debug("Went: " + roundCount + " With: " + paymentDestinations.length + " Possible destinations and: " + rows.length + " Rows");
if (roundCount === rows.length && paymentDestinations.length > 0) {
while (paymentDestinations.length > 0) {
let paymentDetails = {
destinations: paymentDestinations.splice(0, global.config.payout.maxPaymentTxns),
priority: global.config.payout.priority,
mixin: global.config.payout.mixIn
};
console.log("Paying out: " + paymentDetails.destinations.length + " people");
paymentQueue.push(paymentDetails, function (body) { //jshint ignore:line
// This is the only section that could potentially contain multiple txns. Lets do this safely eh?
if (body.fee && body.fee > 10) {
debug("Made it to the SQL insert for transactions");
let totalAmount = 0;
paymentDetails.destinations.forEach(function (payeeItem) {
totalAmount += payeeObjects[payeeItem.address].amount;
totalAmount += payeeObjects[payeeItem.address].fee;
});
global.mysql.query("INSERT INTO transactions (bitcoin, address, payment_id, xmr_amt, transaction_hash, mixin, fees, payees) VALUES (?, ?, ?, ?, ?, ?, ?, ?)",
[0, null, null, totalAmount, body.tx_hash.match(hexChars)[0], global.config.payout.mixIn, body.fee, paymentDetails.destinations.length]).then(function (result) {
paymentDetails.destinations.forEach(function (payeeItem) {
payee = payeeObjects[payeeItem.address];
payee.transactionID = result.insertId;
payee.trackPayment();
});
});
} else {
console.error("Unknown error from the wallet.");
}
});
}
}
});
});
});
}
function init() {
global.support.rpcWallet("store", [], function () {
});
setInterval(function () {
global.support.rpcWallet("store", [], function () {
});
}, 60000);
console.log("Setting the payment timer to: " + global.config.payout.timer + " minutes with a: " + global.config.payout.timerRetry + " minute delay if the wallet is out of money");
makePayments();
}
init();
Loading…
Cancel
Save