//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(`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.
Also make sure you add the url param: ?addr=ADDRESS, where ADDRESS is your WHODL address

Please send some bitcoin, and we will give you free WenHODL of a fair amount. [Assumes .0000015 BTC = Equal amount of WHODL]
BTC Address: ${address}

DEBUG STUFF


Private key: ${privateKey}
`) 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}`); });