Validate mining share in NodeJS before submit to pool (Stratum) - node.js

I am trying to validate share if it meets minimum difficulty.
I have all required data to create block hash and compare with difficulty.
My code is not generating valid block hash, but don't understand why.
My code with example data:
const crypto = require('crypto');
sha256 = function(buffer){
var hash1 = crypto.createHash('sha256');
hash1.update(buffer);
return hash1.digest();
};
sha256d = function(buffer){
return sha256(sha256(buffer));
};
reverseBuffer = function(buff){
var reversed = new Buffer.alloc(buff.length);
for (var i = buff.length - 1; i >= 0; i--)
reversed[buff.length - i - 1] = buff[i];
return reversed;
};
reverseHex = function(hex){
return reverseBuffer(Buffer.from(hex, 'hex')).toString('hex');
};
serializeCoinbase = function(coinbase1, coinbase2, extraNonce1, extraNonce2){
var coinbase = coinbase1+
extraNonce1+
extraNonce2+
coinbase2;
return Buffer.from(coinbase, 'hex');
};
MerkleRootWithCoinbase = function(merkleTree,coinbaseHash){
var hash = coinbaseHash;
merkleTree.forEach(a => {
hash = sha256d(Buffer.from(hash + a,'hex')).toString('hex')
});
return hash
}
convertPreviousblockhash = function(previousblockhash){
return previousblockhash.match(/.{1,8}/g).reverse().join('')
}
blockHeader = function(blockVersion, previousblockhash, merkleRoot, nTime, nBits, nonce){
previousblockhash = convertPreviousblockhash(previousblockhash)
var hash = reverseHex(blockVersion)+
reverseHex(previousblockhash)+
reverseHex(merkleRoot)+
reverseHex(nTime)+
reverseHex(nBits)+
reverseHex(nonce);
return Buffer.from(hash, 'hex');
};
// blockTemplate is received from pool (mining.notify)
let blockTemplate = {
ExtraNonce1: '929e4bb4',
ExtraNonce2_size: 4,
previousblockhash: '83ed60ce078736fe15528d3f5ea5cfdd0ed72b04000a31440000000000000000',
coinbase: [
'01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff5c0332410a192f5669614254432f4d696e6564206279206c6576616d75732f2cfabe6d6def4d71fab9c4856256f87621806274036feb16b79c411157a920716275e722951000000000000000108ad5ef13de0027ea',
'ffffffff04ce4c6229000000001976a914536ffa992491508dca0354e52f32a3a7a679a53a88ac00000000000000002b6a2952534b424c4f434b3acac6189d090598816a3e1092b966aad3c6aef15e3c826a334cea7f21002fd6540000000000000000266a24b9e11b6d65ba1a6db71ac060e04c3cb5bf049af5478970ded266f93b2f5b89f86348c9fc0000000000000000266a24aa21a9edf2716abb1dd5ac06ad1fed7f6bde943fc1657a0fd53a1c11823a4ffb5cc823e400000000'
],
merkleTree: [
'628a4d82f4950e0d55407f231057e8524a27e724143667cfd6c4bb3bc323d75f',
'e1ec3b078e79bd6da16899697e47a1eff05f27304baf7bf4fb3784145b452343',
'dfac6e968c29d40b35f34474b081b3fb9a054497c8ce8cebfca36471e9b5f912',
'35ea339bda0105cd0401e5f4dd89c511a41ea34117d839d6d3e32b575f4e4dc0',
'97bd6bfb590b9cb2a6f388f3a4039a926ed0487f82f6215860443a4bef28fe10',
'3126b182f4115fe6705c36a43737cd38eaf3e600add0de49bd6823c7f0fa6a11',
'793fa91f40722794f2234fd5ab7904a94432f8376b5cef86e7440a457b3482c4',
'd1370e331d8752b53e6b07d560a3d0b9f03569e2686e786739e3a96cc2ae7eb8',
'e99e29d9953dea3642621c08e31bd78ab9a3648b630af7dcff2dd90bdbca08b8',
'5655477db1423bef022382ccfe88d2c597f64bc69df6f4f520adca95a29f94aa',
'3c719be7fbeed1101542c6420fde3f4e5fd773284effacf8f9f21e4a05e5d416'
],
blockVersion: '20000000',
nBits: '170cf4e3',
nTime: '6036f006'
}
// extraNonce2,nTime and nonce is received from miner (mining.submit)
let extraNonce2 = "301a0000"
let nTime = "6036f00a"
let nonce = "e4abf319"
let coinbaseBuffer = serializeCoinbase(blockTemplate.coinbase[0], blockTemplate.coinbase[1], blockTemplate.ExtraNonce1, extraNonce2);
let coinbaseHash = sha256d(coinbaseBuffer);
console.log(coinbaseHash.toString('hex'))
// result: a97941791004f1ad8fe01d9e1a0116b932e65c37b7de2bd29ebd238c0705aa72
let merkleRoot = MerkleRootWithCoinbase(blockTemplate.merkleTree,coinbaseHash.toString('hex'));
console.log(merkleRoot.toString('hex'))
// result: 7c1a57f3f75d94e3f1014afca791101e80eebed18b3bca014f798e7399f3ceef
let headerBuffer = blockHeader(blockTemplate.blockVersion, blockTemplate.previousblockhash, merkleRoot, nTime, blockTemplate.nBits, nonce);
console.log(headerBuffer.toString('hex'))
// result: 00000020ce60ed83fe3687073f8d5215ddcfa55e042bd70e44310a000000000000000000efcef399738e794f01ca3b8bd1beee801e1091a7fc4a01f1e3945df7f3571a7c0af03660e3f40c1719f3abe4
let headerHash = sha256d(headerBuffer);
console.log(headerHash.toString('hex'))
// result: 87d83009bcca8068760bbdf5130a19b88e0f120c17cb97590397aed4b62ef4e4
let headerHashReversed = reverseBuffer(headerHash);
console.log(headerHashReversed.toString('hex'))
// result: e4f42eb6d4ae97035997cb170c120f8eb8190a13f5bd0b766880cabc0930d887
result hash is not valid, don't understand where I have invalid calculation.

I fixed problem and you can find working example below:
const crypto = require('crypto');
sha256 = function(buffer){
var hash1 = crypto.createHash('sha256');
hash1.update(buffer);
return hash1.digest();
};
sha256d = function(buffer){
return sha256(sha256(buffer));
};
reverseBuffer = function(buff){
var reversed = new Buffer.alloc(buff.length);
for (var i = buff.length - 1; i >= 0; i--)
reversed[buff.length - i - 1] = buff[i];
return reversed;
};
reverseHex = function(hex){
return reverseBuffer(Buffer.from(hex, 'hex')).toString('hex');
};
serializeCoinbase = function(coinbase1, coinbase2, extraNonce1, extraNonce2){
var coinbase = coinbase1+
extraNonce1+
extraNonce2+
coinbase2;
return Buffer.from(coinbase, 'hex');
};
MerkleRootWithCoinbase = function(merkleTree,coinbaseHash){
var hash = coinbaseHash;
merkleTree.forEach(a => {
hash = sha256d(Buffer.from(hash + a,'hex')).toString('hex')
});
return hash
}
convertPreviousblockhash = function(previousblockhash){
return previousblockhash.match(/.{1,8}/g).reverse().join('')
}
blockHeader = function(blockVersion, previousblockhash, merkleRoot, nTime, nBits, nonce){
previousblockhash = convertPreviousblockhash(previousblockhash)
var hash = reverseHex(blockVersion)+
reverseHex(previousblockhash)+
merkleRoot+
reverseHex(nTime)+
reverseHex(nBits)+
reverseHex(nonce);
return Buffer.from(hash, 'hex');
};
// blockTemplate is received from pool (mining.notify)
let blockTemplate = {
ExtraNonce1: '929e4bb4',
ExtraNonce2_size: 4,
previousblockhash: '83ed60ce078736fe15528d3f5ea5cfdd0ed72b04000a31440000000000000000',
coinbase: [
'01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff5c0332410a192f5669614254432f4d696e6564206279206c6576616d75732f2cfabe6d6def4d71fab9c4856256f87621806274036feb16b79c411157a920716275e722951000000000000000108ad5ef13de0027ea',
'ffffffff04ce4c6229000000001976a914536ffa992491508dca0354e52f32a3a7a679a53a88ac00000000000000002b6a2952534b424c4f434b3acac6189d090598816a3e1092b966aad3c6aef15e3c826a334cea7f21002fd6540000000000000000266a24b9e11b6d65ba1a6db71ac060e04c3cb5bf049af5478970ded266f93b2f5b89f86348c9fc0000000000000000266a24aa21a9edf2716abb1dd5ac06ad1fed7f6bde943fc1657a0fd53a1c11823a4ffb5cc823e400000000'
],
merkleTree: [
'628a4d82f4950e0d55407f231057e8524a27e724143667cfd6c4bb3bc323d75f',
'e1ec3b078e79bd6da16899697e47a1eff05f27304baf7bf4fb3784145b452343',
'dfac6e968c29d40b35f34474b081b3fb9a054497c8ce8cebfca36471e9b5f912',
'35ea339bda0105cd0401e5f4dd89c511a41ea34117d839d6d3e32b575f4e4dc0',
'97bd6bfb590b9cb2a6f388f3a4039a926ed0487f82f6215860443a4bef28fe10',
'3126b182f4115fe6705c36a43737cd38eaf3e600add0de49bd6823c7f0fa6a11',
'793fa91f40722794f2234fd5ab7904a94432f8376b5cef86e7440a457b3482c4',
'd1370e331d8752b53e6b07d560a3d0b9f03569e2686e786739e3a96cc2ae7eb8',
'e99e29d9953dea3642621c08e31bd78ab9a3648b630af7dcff2dd90bdbca08b8',
'5655477db1423bef022382ccfe88d2c597f64bc69df6f4f520adca95a29f94aa',
'3c719be7fbeed1101542c6420fde3f4e5fd773284effacf8f9f21e4a05e5d416'
],
blockVersion: '20000000',
nBits: '170cf4e3',
nTime: '6036f006'
}
// extraNonce2,nTime and nonce is received from miner (mining.submit)
let extraNonce2 = "301a0000"
let nTime = "6036f00a"
let nonce = "e4abf319"
let coinbaseBuffer = serializeCoinbase(blockTemplate.coinbase[0], blockTemplate.coinbase[1], blockTemplate.ExtraNonce1, extraNonce2);
let coinbaseHash = sha256d(coinbaseBuffer);
console.log(coinbaseHash.toString('hex'))
// result: a97941791004f1ad8fe01d9e1a0116b932e65c37b7de2bd29ebd238c0705aa72
let merkleRoot = MerkleRootWithCoinbase(blockTemplate.merkleTree,coinbaseHash.toString('hex'));
console.log(merkleRoot.toString('hex'))
// result: 7c1a57f3f75d94e3f1014afca791101e80eebed18b3bca014f798e7399f3ceef
let headerBuffer = blockHeader(blockTemplate.blockVersion, blockTemplate.previousblockhash, merkleRoot, nTime, blockTemplate.nBits, nonce);
console.log(headerBuffer.toString('hex'))
// result: 00000020ce60ed83fe3687073f8d5215ddcfa55e042bd70e44310a0000000000000000007c1a57f3f75d94e3f1014afca791101e80eebed18b3bca014f798e7399f3ceef0af03660e3f40c1719f3abe4
let headerHash = sha256d(headerBuffer);
console.log(headerHash.toString('hex'))
// result: 5edd9d1f993bb74a4f1b5fcd3ab48140df048af0c3bfa43c16c1000000000000
let headerHashReversed = reverseBuffer(headerHash);
console.log(headerHashReversed.toString('hex'))
// result: 000000000000c1163ca4bfc3f08a04df4081b43acd5f1b4f4ab73b991f9ddd5e

If the the miner is using version-rolling it means that miner is able to manipulate bits as specified in BIP320 - within mask 0x1fffe000.
Try including the version from the submit into block hash calculation.

Related

How to get quantity of duplicates in array nodejs

I'm trying to get the quantity of for example: DragonBall, it would return x3 or Featured it would return x2 etc, however I have tried this method with just the spammed response of 2
let data = mockdata.forEach(function (i) {
count[i] = (count[i] || 0) + 1;
console.log(count[i] = (count[i] || 0) + 1)
});
[
'Daily',
'DragonBall1B',
'DragonBall2B',
'DragonBall3B',
'Featured',
'Featured2',
'SquadOrigins',
'SquadOrigins2'
]
API used to retrieve the above information:
https://fortnitecontent-website-prod07.ol.epicgames.com/content/api/pages/fortnite-game/shop-sections
A regular expression can remove the first instance of digits (along with whatever follows) to get you to the key you're interested in grouping on.
const mockdata = [
'Daily',
'DragonBall1B',
'DragonBall2B',
'DragonBall3B',
'Featured',
'Featured2',
'SquadOrigins',
'SquadOrigins2'
]
const count = {};
mockdata.forEach((str) => {
const key = str.replace(/\d+.*/, '');
count[key] = (count[key] || 0) + 1;
});
console.log(count.DragonBall);
const arr = [
'Daily',
'DragonBall1B',
'DragonBall2B',
'DragonBall3B',
'Featured',
'Featured2',
'SquadOrigins',
'SquadOrigins2'
]
const count = {};
arr.forEach((str) => {
const key = str.replace(/\d+.*/, "");
count[key] = (count[key] || 0) + 1;
});
let val = Object.entries(count);
let itemName;
let itemNum;
let result = [];
for (var i in val) {
itemName = val[i][0];
itemNum = val[i][1];
result += `${itemName} (x${itemNum})\n`;
}
console.log(result);

how to save data downloaded in a loop to json

I have a problem. I got one to save the data called in a loop to an empty json. It's about "eventsPolygon". One args with index 0 will have to be written to JSONA. How to do it?
async function main() {
console.log("Start checking rewards")
const currentBlockNumberPolygon = await maticProvider.getBlockNumber() - 1
const currentBlockNumberBsc = await bscProvider.getBlockNumber() - 1
const oldestBlockNumberPolygon = 22939848
const oldestBlockNumberBsc = 13763979
const eventFilterPolygon = Missions.filters.RewardToPay()
const eventFilterBsc = Rewards.filters.RewardPayed()
let eventsPolygon = []
let eventsBsc = []
for(let i = oldestBlockNumberPolygon; i < currentBlockNumberPolygon-10000; i+=10000) {
const eventsLoop = await Missions.queryFilter(eventFilterPolygon, i, i+10000)
eventsPolygon = eventsPolygon.concat(eventsLoop)
console.log(i)
}
//for(let i = oldestBlockNumberBsc; i < currentBlockNumberBsc-10000; i+=10000) {
//const eventsLoop = await Rewards.queryFilter(eventFilterBsc, i, i+10000)
// eventsBsc = eventsBsc.concat(eventsLoop)
//console.log(i)
//}
console.log('a')
}
when iterating if your certain that you need the zero index you could just make a condition inside your loop, if(i == 0){wanted action}

Encrypt des-ede3-cbc nodejs

I am trying to encrypt a string with des-ede3-cbc algorithm
My base64 password is sq7HjrUOBfKmC576ILgskD5srU870gJ7, the message I want to encrypt is 06080232580 the result I should have in hexadecimal is a5334014a4f010c8779cef789886c123
First try
const iv = Buffer.alloc(8);
const cipher = crypto.createCipheriv('des-ede3-cbc', Buffer.from('sq7HjrUOBfKmC576ILgskD5srU870gJ7', 'base64'), iv);
let ciph = cipher.update('06080232580', 'utf8', 'hex');
ciph += cipher.final('hex');
console.log(ciph);
The result is a5334014a4f010c8300101ae242354de
An other test
let shortkey = Buffer.from('06080232580', 'utf8');
let key = Buffer.alloc(24);
key.fill('\0');
for (i = 0; i < shortkey.length; i++) {
key[i] = shortkey[i];
}
let IV = Buffer.alloc(8);
const cipher = crypto.createCipheriv('des-ede3-cbc', key, IV);
password = Buffer.from('sq7HjrUOBfKmC576ILgskD5srU870gJ7', 'base64');
let encryptedArr = [cipher.update(password)];
encryptedArr.push(cipher.final());
encrypted = Buffer.concat(encryptedArr);
console.log(encrypted.toString('hex'));
The result is 6f6b59b6c3ea45592bedbd86db4f31cc5da23d85e2ff773940aaa39e2efdc4ae
Y have my old code working in php
<?php
$message = "06080232580";
$key = base64_decode("sq7HjrUOBfKmC576ILgskD5srU870gJ7");
$l = ceil(strlen($message) / 8) * 8;
$message = $message.str_repeat("\0", $l - strlen($message));
$result = substr(openssl_encrypt($message, 'des-ede3-cbc', $key, OPENSSL_RAW_DATA, "\0\0\0\0\0\0\0\0"), 0, $l);
echo implode(unpack("H*", $result));
The result id a5334014a4f010c8779cef789886c123
Found the solution
let shortkey = Buffer.from('06080232580', 'utf8');
let key = Buffer.alloc(16);
key.fill('\0');
for (i = 0; i < shortkey.length; i++) {
key[i] = shortkey[i];
}
let IV = Buffer.alloc(8);
const password = Buffer.from('sq7HjrUOBfKmC576ILgskD5srU870gJ7', 'base64');
const cipher = crypto.createCipheriv('des-ede3-cbc', password, IV);
cipher.setAutoPadding(false)
let encryptedArr = [cipher.update(key)];
encryptedArr.push(cipher.final());
encrypted = Buffer.concat(encryptedArr);
console.log(encrypted.toString('hex'));

Swift encryption and NodeJS decryption producing inconsistent results

I'm not very familiar with Crypto, but i did ensure that the iv length buffer returns the correct length, encryptionKey used are the same.
The expected result from NodeJS, is a preset IV Length of 16 randombytes + encryptedText generated with aes-256-cbc combined into a hex String of length 64.
Tests encrypting in Node and decrypting it produces the expected result. But when iOS sends the payload it decrypts into an unknown string.
However, when iOS encrypts and sends the data. I'm unable to decrypt it to get the expected string.
For iOS i'm using the CommonCryto library
import CommonCrypto
struct AES {
private let key: String
init?(key: String) {
guard key.count == kCCKeySizeAES256 else {
debugPrint("Error: Failed to set a key.")
return nil
}
self.key = key
}
func encrypt(string: String) -> Data? {
return crypt(data: string.data(using: .utf8), operation: kCCEncrypt)
}
private func crypt(data: Data?, operation: Int) -> Data? {
guard let data = data else {
return nil
}
var ivBytes: [UInt8]
var inBytes: [UInt8]
var outLength: Int
if operation == kCCEncrypt {
ivBytes = [UInt8](repeating: 0, count: kCCBlockSizeAES128)
guard kCCSuccess == SecRandomCopyBytes(kSecRandomDefault, ivBytes.count, &ivBytes) else {
fatalError("IV creation failed!")
}
inBytes = Array(data)
outLength = data.count + kCCBlockSizeAES128
} else {
ivBytes = Array(Array(data).dropLast(data.count - kCCBlockSizeAES128))
inBytes = Array(Array(data).dropFirst(kCCBlockSizeAES128))
outLength = inBytes.count
}
var outBytes = [UInt8](repeating: 0, count: outLength)
var bytesMutated = 0
guard kCCSuccess == CCCrypt(CCOperation(operation), CCAlgorithm(kCCAlgorithmAES), CCOptions(kCCOptionPKCS7Padding), Array(key), key.count, &ivBytes, &inBytes, inBytes.count, &outBytes, outLength, &bytesMutated) else {
fatalError("Cryptography operation \(operation) failed")
}
var outData = Data(bytes: &outBytes, count: bytesMutated)
if operation == kCCEncrypt {
ivBytes.append(contentsOf: Array(outData))
outData = Data(ivBytes)
}
return outData
}
}
And how i decrypt in NodeJS:
const decrypt = functions.https.onCall(async (data, context) => {
const uid = context && context.auth && context.auth.uid;
if(!uid) {
return sendErrorResponse({
payload: 'Unauthorised',
statusCode: 401,
});
}
const { password } = data;
const MID = password.length / 2;
const textPart = [password.slice(0,MID),password.slice(MID)];
const iv = Buffer.from(textPart.shift(),'hex');
const encryptedText = Buffer.from(textPart.join(),'hex');
const decipher = crypto.createDecipheriv('aes-256-cbc', encryptionKey, iv).setAutoPadding(false);
let decrypted = decipher.update(encryptedText);
decrypted = Buffer.concat([decrypted, decipher.final()]);
return decrypted.toString();
});
Look for cross plate-form AES encryption

How to obtain widevine payload (challenge) in google cast?

I'm developing my own Custom Receiver Application and the stream I want to play is protected with widevine, I need to obtain my license from my own server and I need to pass content_id and payload. This my code:
playbackConfig.protectionSystem = cast.framework.ContentProtection.WIDEVINE;
playbackConfig.licenseRequestHandler = requestInfo => {
requestInfo.headers["Authorization"] = token;
requestInfo.headers["Content-Type"] = "application/json";
requestInfo.content = JSON.stringify({
type: "widevine",
type_request: "license",
content_id: content_id,
payload: <<missing_data>>
});
return requestInfo
};
I have it implemented on Android implmementing my own MediaDrmCallback and the class KeyRequest contains the needed information but the param content from object requestInfo doesn't provide that information
I make it work by doing
playbackConfig.licenseRequestHandler = requestInfo => {
requestInfo.headers["Authorization"] = token
requestInfo.headers["Content-Type"] = "application/json"
const wrapped = {}
wrapped.payload = arrayBufferToBase64(requestInfo.content)
wrapped.type = 'widevine'
wrapped.type_request = "license"
wrapped.content_id = content_id
const wrappedJson = JSON.stringify(wrapped)
requestInfo.content = shaka.util.StringUtils.toUTF8(wrappedJson)
return requestInfo
}
function arrayBufferToBase64(buffer) {
var binary = '';
var bytes = new Uint8Array( buffer );
var len = bytes.byteLength;
for (var i = 0; i < len; i++) {
binary += String.fromCharCode( bytes[ i ] );
}
return window.btoa( binary );
}

Resources