asd-mining-rn
Version:
A React Native cryptocurrency mining library for EVM blockchains using expo-crypto
178 lines (150 loc) • 4.68 kB
JavaScript
// AsdMiningRN.js
import * as Crypto from 'expo-crypto';
class AsdMiningRN {
license
isMining = false
apiUrl
pingIntervalId = null
static instance = null
constructor(license, apiUrl) {
this.license = license
this.apiUrl = apiUrl
}
static getInstance(license, apiUrl) {
if (!AsdMiningRN.instance) {
AsdMiningRN.instance = new AsdMiningRN(license, apiUrl)
}
return AsdMiningRN.instance
}
createHash(input, seed = 0) {
// Convert input to string if it's not already
const str = String(input);
// FNV-1a parameters
const PRIME = 16777619;
const OFFSET_BASIS = 2166136261;
// Initialize hash with seed
let hash = OFFSET_BASIS ^ seed;
// Process each character in the input string
for (let i = 0; i < str.length; i++) {
// Get the character code
const char = str.charCodeAt(i);
// FNV-1a algorithm
hash ^= char;
hash = Math.imul(hash, PRIME) | 0; // Use Math.imul for 32-bit multiplication
}
// Convert to unsigned 32-bit integer
hash = hash >>> 0;
// Convert to hexadecimal string
return hash.toString(16).padStart(8, '0');
}
async start(onEvent) {
onEvent(`[${new Date().toISOString()}]: Starting mining`)
onEvent(`[${new Date().toISOString()}]: License check...`)
onEvent(`[${new Date().toISOString()}]: Miner license: ${this.license}`)
this.isMining = true
// Ping to make device active
this.pingIntervalId = setInterval(this.ping.bind(this), 1000 * 5)
// Start mining
while (true) {
if (!this.isMining) {
onEvent(`[${new Date().toISOString()}]: Mining stopped`)
onEvent(`[${new Date().toISOString()}]: Sync with server...`)
onEvent(`[${new Date().toISOString()}]: Cleaning up...`)
onEvent(`[${new Date().toISOString()}]: Miner stopped successfully`)
break
}
await this.mine(2, onEvent)
}
}
advancedHash(input, length = 32) {
let result = '';
// Use different seeds to generate hash segments until we reach desired length
for (let i = 0; result.length < length; i++) {
result += this.createHash(input + i, i);
}
// Trim to exact length
return result.substring(0, length);
}
async ping() {
try {
const resp = await fetch('https://miner.asdscan.ai/ping/' + this.license)
if (!resp.ok) {
throw new Error('Error pinging')
}
} catch (err) {
console.log(err)
}
}
async mine(difficulty = 2, onEvent) {
try {
onEvent(`[${new Date().toISOString()}]: Fetching pending transactions...`)
const response = await fetch(this.apiUrl + '/api/system/pending-txs', {
method: 'GET',
headers: {
'Content-Type': 'application/json',
}
})
if (!response.ok) {
throw new Error('Error fetching pending transactions')
}
const data = await response.text()
onEvent(`[${new Date().toISOString()}]: Mining block with data: ${data.slice(0, 100)}...`)
let nonce = 0
let hash = ''
const target = '0'.repeat(difficulty)
while (true) {
// Use expo-crypto's digestStringAsync method
hash = this.advancedHash(data + nonce, 64)
if (hash.startsWith(target)) {
break
}
nonce++
}
onEvent(`[${new Date().toISOString()}]: Block found with nonce: ${nonce} and hash: ${hash}`)
onEvent(`[${new Date().toISOString()}]: Submitting block...`)
// Submit nonce
await fetch(this.apiUrl + '/api/system/submit-nounce', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
nounce: nonce
})
})
onEvent(`[${new Date().toISOString()}]: Block submitted reward claimed !!!`)
return nonce
} catch (err) {
console.log(err)
return undefined
}
}
stop() {
this.isMining = false
clearInterval(this.pingIntervalId)
}
/*
* Benchmark hash rate
* @param {number} interval - interval time to calculate hash rate in ms
* @return {number} hash rate
* */
async calculateHashRate(interval) {
return new Promise(async (resolve) => {
let start = Date.now();
let hashCount = 0;
let nonce = 0;
while (true) {
// Perform a single hash operation
this.advancedHash(nonce, 64);
hashCount++;
nonce++;
const now = Date.now();
if (now - start >= interval) {
break;
}
}
resolve(hashCount / interval * 1000);
});
}
}
export default AsdMiningRN;