historical/WenHODL-API.git/index.js
2024-01-16 11:20:27 -06:00

534 lines
18 KiB
JavaScript

//WenHODL
//Copyright Samuel Lord. Licensed under the MIT License.
const GUN = require('gun/gun');
const EC = require("elliptic").ec;
let ec = new EC('secp256k1');
//require bitcore
const bitcore = require('bitcore-lib');
//require crypto lib
const crypto = require('crypto');
let gun = GUN(['http://localhost:8765/gun', 'https://gun-manhattan.herokuapp.com/gun']);
let apiport = 42069;
let gunIdWenHODL;
let breakSaveLoop = false;
//DEFINE IF TESTNET OR MAINNET HERE
let isTestnet = true; //set to true for testnet (aka testing), false for mainnet (aka nontesting)
if (isTestnet) {
gunIdWenHODL = "wenhodl-testing-v2";
} else {
gunIdWenHODL = "wenhodl-nontesting-v2";
}
let p2pbc = gun.get(gunIdWenHODL).get('blockchain'); //add -nontest to wenhodl for release
let p2pnftc = gun.get(gunIdWenHODL).get('nftchain'); //add -nontest to wenhodl for release
let version = '1.3'; //change the version of wenhodl to the latest version every time you update the code
let codename = 'Introducing New WenHODL! More anonymity, same great spaghetti code! :D'; //change this to the version's codename every time you update the version
let difficulty = 2;
let numberOfUsers = 0;
function sha256(data) {
//convert string to sha256
let hash = crypto.createHash('sha256');
hash.update(data);
//turn hash into a string
let hashString = hash.digest('hex');
return hashString;
}
//deprecated, please use the one in lowercase instead! (this just redirects to the one in lowercase)
function SHA256(data) {
sha256(data);
}
//create a function to shuffle strings randomly
function shuffle(string) {
let array = string.split("");
let currentIndex = array.length, temporaryValue, randomIndex;
// While there remain elements to shuffle...
while (0 !== currentIndex) {
// Pick a remaining element...
randomIndex = Math.floor(Math.random() * currentIndex);
currentIndex -= 1;
// And swap it with the current element.
temporaryValue = array[currentIndex];
array[currentIndex] = array[randomIndex];
array[randomIndex] = temporaryValue;
}
return array.reverse().join("");
}
function appendArray(oldArray, addArray) {
return oldArray.concat(addArray);
}
class NFTchain {
constructor() {
this.chain = [];
}
}
class Blockchain {
constructor() {
this.chain = [];
this.pendingTransactions = [];
this.miningReward = .0000001;
}
createNewBlock(nonce, previousBlockHash) {
const newBlock = {
index: this.chain.length + 1,
timestamp: Date.now(),
transactions: this.pendingTransactions,
nonce: nonce,
hash: '',
previousBlockHash: previousBlockHash
};
this.pendingTransactions = [];
newBlock.hash = this.calculateHash(newBlock);
this.chain.push(newBlock);
return newBlock;
}
getWallet(address) {
let deets = gun.get(gunIdWenHODL).get(this.publicKey);
let wallet = {
balance: deets[1],
transactions: deets[2]
};
this.chain.forEach(block => {
block.transactions.forEach(transaction => {
if (transaction.toAddress === address) {
wallet.transactions.push(transaction);
}
});
});
return wallet;
}
getLastBlock() {
try {
return this.chain[this.chain.length - 1];
} catch {
let firstBlock = this.createNewBlock()
this.hashBlock(firstBlock)
return firstBlock;
}
}
createNewTransaction(amount, sender, recipient) {
const newTransaction = {
amount: amount,
sender: sha256(shuffle(sender).toString(16)),
recipient: recipient
};
this.pendingTransactions.push(newTransaction);
//get sender's wallet
let senderWallet = this.getWallet(sender);
//get recipient's wallet
let recipientWallet = this.getWallet(recipient);
//subtract amount from sender's wallet
//check if sender has enough money
if (senderWallet.balance - amount >= 0) {
if (this.calculateHash(this.getLastBlock()) !== this.getLastBlock().hash) {
return false;
} else if (this.chainIsValid(this.chain) == false) {
return false;
} else {
senderWallet.balance -= amount;
recipientWallet.balance += amount;
this.chain.push(this.createNewBlock(this.proofOfWork(this.getLastBlock().hash, this.pendingTransactions), this.getLastBlock().hash));
this.pendingTransactions = [];
return this.getLastBlock().index + 1;
}
} else {
this.pendingTransactions = [];
return false;
}
}
calculateHash(block) {
return SHA256(JSON.stringify(block));
}
hashBlock(previousBlockHash, currentBlockData, nonce) {
const dataAsString = previousBlockHash + nonce.toString() + JSON.stringify(currentBlockData);
const hash = SHA256(dataAsString);
return hash;
}
proofOfWork(previousBlockHash, currentBlockData) {
let hash;
let nonce = 0;
if (hash != undefined) {
while (typeof(hash.substring(0, difficulty) !== Array(difficulty + 1).join("0"))) {
nonce++;
hash = this.hashBlock(previousBlockHash, currentBlockData, nonce);
}
}
difficulty++;
this.miningReward += .0000001;
return nonce;
}
chainIsValid(blockchain) {
for (let i = 1; i < blockchain.length; i++) {
const currentBlock = blockchain[i];
const prevBlock = blockchain[i - 1];
const blockHash = this.calculateHash(currentBlock);
if (currentBlock.previousBlockHash !== prevBlock.hash) {
return false;
}
if (currentBlock.hash !== blockHash) {
return false;
}
}
return true;
}
}
let bc = new Blockchain();
//create transaction class
class Transaction {
constructor(amount, sender, recipient) {
bc.createNewTransaction(amount, sender, recipient);
}
}
//define ec
//create a wallet class
class Wallet {
constructor() {
this.balance = 0;
this.keyPair = ec.genKeyPair();
this.publicKey = this.keyPair.getPublic().encode('hex');
this.address = sha256(this.keyPair.getPublic().toString(16));
this.privateKey = this.keyPair.getPrivate().toString(16);
}
sign(dataHash) {
return this.keyPair.sign(dataHash);
}
setBalance(balance) {
this.balance = balance;
}
}
//create a block class
class Block {
constructor(timestamp, transactions, nonce, previousBlockHash, hash) {
this.timestamp = timestamp;
this.transactions = transactions;
this.nonce = nonce;
this.previousBlockHash = previousBlockHash;
this.hash = hash;
}
}
//create miner class
class Miner {
constructor(blockchain, transactionPool) {
this.blockchain = bc || bc.chain;
this.transactionPool = bc.pendingTransactions;
}
mine(address) {
const lastBlock = this.blockchain.getLastBlock();
const previousBlockHash = lastBlock;
const currentBlockData = {
transactions: this.transactionPool.transactions
};
const nonce = this.blockchain.proofOfWork(previousBlockHash, currentBlockData);
const blockHash = this.blockchain.hashBlock(previousBlockHash, currentBlockData, nonce);
const newBlock = this.blockchain.createNewBlock(nonce, previousBlockHash);
new Block(Date.now(), this.transactionPool.transactions, nonce, previousBlockHash, blockHash);
this.transactionPool = [];
new Transaction(this.miningReward, '00', address);
return newBlock;
}
}
async function donationMiner() {
let donations = await new Wallet()
donations.privateKey = await sha256("N0d3mix*%)(*ah0lic69$!@")
donations.publicKey = await "nodemixaholicDonations"
console.log(`**MINING DONATION STARTED** ${donations.address}`)
while (true) {
miner.mine(donations.address)
console.log(`**MINING DONATION SUCCESS** ${donations.address}`)
await new Promise(r => setTimeout(r, 1000));
}
}
function saveWallet(privateKey, address, wallet) {
if (wallet.privateKey === privateKey) {
gun.get(gunIdWenHODL).get(wallet.publicKey).put([address, wallet.balance])
gun.get(gunIdWenHODL).get(wallet.publicKey).get(address).put(true);
return savedWallet;
}
}
function loadWallet(publicKey) {
//load user's wallet data
let walletDeets = gun.get(gunIdWenHODL).get(publicKey)
let wallet = new Wallet();
wallet.address = walletDeets[0];
wallet.balance = walletDeets[1];
return wallet;
}
function loadWalletPrivate(publicKey, privateKey) {
if (privateKey) {
let walletDeets = gun.get(gunIdWenHODL).get(publicKey)
if (walletDeets.get(privateKey) == true) {
let wallet = new Wallet();
wallet.address = walletDeets[0];
wallet.balance = walletDeets[1];
wallet.privateKey = privateKey;
return wallet;
} else {
return false;
}
} else {
return false;
}
}
//comine array function
function combineArray(arr) {
var r = [];
for (var i = 0, l = arr.length; i < l; i++) {
var v = arr[i];
if (v) {
r = r.concat(v);
}
}
return r;
}
let nftc = new NFTchain();
let transactionPool = bc.pendingTransactions;
let miner = new Miner(bc, transactionPool);
//create a new blockchain
async function bcLoop() {
while (true) {
await new Promise(r => setTimeout(r, 2000));
bc.oninput = () => { p2pbc.put(bc) };
nftc.oninput = () => { p2pnftc.put(nftc) };
p2pnftc.on((newnftc) => { try { if (true) { nftc.chain = combineArray(nftc.chain,newnftc.chain) } } catch (e) {console.log(e);} });
p2pbc.on((newbc) => { try { if (bc.chainIsValid(newbc.chain)) { bc.chain = combineArray(bc.chain,newbc.chain); bc.miningReward = newbc.miningReward } } catch (e) {console.log(e);} });
//get number of users via gun
try {
gun.get(gunIdWenHODL).get('numUsers').on((data) => {
if (data) {
data++;
numberOfUsers = data;
}
});
} catch {
numberOfUsers = -1;
}
}
}
bcLoop();
async function resetSaveLoop() {
breakSaveLoop = true;
await new Promise(r => setTimeout(r, 100));
breakSaveLoop = false;
}
async function walletAutosave(wallet) {
resetSaveLoop();
while (breakSaveLoop == false) {
await new Promise(r => setTimeout(r, 100));
saveWallet(wallet.privateKey, wallet.address, wallet);
}
}
//create express.js backend
const express = require('express');
const app = express();
//create send transaction endpoint
app.post('/api/transactionSend', (req, res) => {
const { amount, sender, recipient } = req.body;
const transaction = new Transaction(amount, sender, recipient);
res.json({
note: 'Transaction created',
transaction: transaction
});
});
//create get transaction endpoint
app.get('/api/transaction', (req, res) => {
res.json(transactionPool.transactions);
});
app.get('/api/mineToDonateToNM', (req, res) => {
donationMiner();
res.send("ok i guess")
});
//create get balance endpoint
app.post('/api/wallet-balance', (req, res) => {
const { publicKey } = req.body;
let walletA = loadWallet(publicKey);
res.json({
balance: publicKey.balance
});
});
//get public key endpoint
app.post('/api/get-public-key', (req, res) => {
const { privateKey } = req.body;
let walletA = loadWalletPrivate(privateKey);
res.json({
publicKey: walletA.publicKey
});
});
//create a buy nft endpoint (this is for bandit!)
app.post('/api/buy-nft', (req, res) => {
const { privateKey, address, nftId } = req.body;
let nftImageBase64OrURL;
let nftValue;
let ownerSharedPrivateKey;
let nftScarcity;
let bought;
let nftDetails = p2pnftc.get(`${nftId}`);
let ownerAddress;
nftDetails.on(function (data) {
if (data) {
nftImageBase64 = data[0];
nftValue = data[1];
ownerPrivateKey = data[2];
ownerAddress = data[3];
nftScarcity = data[4];
bought = data[5];
}
});
if (nftScarcity >= bought) {
bc.createNewTransaction(nftValue, address, ownerAddress)
nftc.chain.put(`${nftId.toString('hex')}_${sha256(nftValue)}/${walletA.publicKey}/${sha256(nftImageBase64)}`);
res.send(`Transaction complete, you now have NFT. Your NFT certificate of ownership and image base64 (Keep this as proof, this will be on the NFTChain!): BASE64 DATA: ${nftImageBase64} ID: ${nftId.toString('hex')}_${sha256(nftValue)}/${walletA.publicKey}/${sha256(nftImageBase64)}`);
} else {
res.send(`Sorry, but too many people bought the NFT! (nftScarcity <= bought)`);
}
});
//create a buy upload nft endpoint (this is also for bandit!)
app.post('/api/upload-nft', (req, res) => {
const { nftImageBase64, value, ownerPrivateKey, ownerAddress, nftScarcity } = req.body;
let nftId = `${Math.floor(Math.random() * 128000)}-${nftImageBase64}-${Math.floor(Math.random() * 69000)}-${value}`;
let nft = nftc.chain.get(`${nftId}`);
nft.put([nftImageBase64, value, ownerPrivateKey, ownerAddress, nftScarcity, 0]);
res.send(`Request complete, you now have uploaded NFT. Id: ${nftId}`);
});
//create a browse nftchain endpoint
app.post('/api/browse-nftchain', (req, res) => {
res.json(nftc.chain);
});
//create miner endpoint
app.post('/api/mine', (req, res) => {
const { address } = req.body;
const block = Miner.mine(address);
res.json({
note: 'New block added',
block: block
});
});
//create version endpoint
app.get('/api/about', (req, res) => {
res.json({
note: `About WenHODL`,
version: `v${version}`,
codename: `${codename}`,
originallyBy: `Samuel (NodeMixaholic) Lord`,
symbol: `WHODL`
});
});
//create save wallet endpoint (for testing)
app.post('/api/save-wallet', (req, res) => {
const { address, publicKey, privateKey } = req.body;
let walletA = loadWallet(publicKey);
saveWallet(privateKey, address, walletA);
res.json({
note: `Wallet saved`
});
});
//create load wallet endpoint
app.post('/api/load-wallet', (req, res) => {
const { privateKey } = req.body;
loadWallet(privateKey);
res.json({
note: `Wallet loaded`
});
walletAutosave(savedWallet);
});
//create load wallet private endpoint
app.post('/api/load-wallet-private', (req, res) => {
const { publicKey, privateKey } = req.body;
loadWalletPrivate(publicKey, privateKey);
res.json({
note: `Wallet loaded`
});
});
//make endpoint that returns wallet data for wallet
app.post('/api/', (req, res) => {
try {
const { address, privateKey } = req.body;
try {
let walletA = loadWallet(address, privateKey);
res.json({"pubKey": walletA.publicKey, "balance": walletA.balance, "privKey": walletA.privateKey});
} catch {
let newWallet = new Wallet();
walletAutosave(newWallet);
res.json({"pubKey": newWallet.publicKey, "balance": newWallet.balance, "privKey": newWallet.privateKey});
}
} catch {
res.json({"error": "Please enter a valid address and private key when POSTing to this endpoint."});
}
});
app.get('/', (req, res) => {
res.redirect('/api/');
});
async function processBitcoin2WenHODL(btcPrivateKey, hodlPrivateKey) {
let privateKey = new bitcore.PrivateKey(`${btcPrivateKey}`);
let address = privateKey.toAddress();
let balB = bal;
let breakLoop = false;
if (bal > .00001076) {
breakLoop = false
while (breakLoop == false) {
balB = balB - .00001076;
Blockchain.createNewTransaction(1, account.publicKey, hodlPrivateKey);
if (balB < 0.00002000) {
breakLoop = true
}
}
}
await setTimeout(processBitcoin2WenHODL(addr), 1200)
}
app.get('/btc2wenhodl', (req, res) => {
let hodlPrivKey = req.query.privateKey
let privateKey = new bitcore.PrivateKey();
let address = privateKey.toAddress();
console.log("To Private Key (in case you lost your funds): " + privateKey)
console.log("To Address: " + account.address("BTC"))
res.send(`<b>NOTE: NOBODY IS RESPONSIBLE FOR LOST FUNDS, NO PAYBACKS, NO WARRANTY. JUST ASSUME THIS IS BROKEN AND CARRY ON IF YOU DONT KNOW WHAT YOU ARE DOING.<br>Also make sure you add the url param: ?addr=ADDRESS, where ADDRESS is your WHODL address<br></b><br>Please send some bitcoin, and we will give you free WenHODL of a fair amount. [Assumes .0000015 BTC = Equal amount of WHODL] <hr><i><b>BTC</b> Address: ${address}</i> <hr></hr><h2>DEBUG STUFF</h2><br><b>Private key: ${privateKey}<br></b>`)
processBitcoin2WenHODL(privateKey, hodlPrivKey)
});
app.get('/verify-cert', (req, res) => {
let nftCertificate = req.query.cert
res.send(`${nftc.chain.includes(nftCertificate)}`)
});
//listen on port
app.listen(apiport, () => {
console.log(`listening on port ${apiport}`);
});