├── Src ├── interfaces │ ├── Blockchain │ │ ├── _Errors.js │ │ ├── _Errors.ts │ │ ├── _Block.js │ │ ├── _Wallet.js │ │ ├── _Blockchain.js │ │ ├── _Transaction.js │ │ ├── _TransactionMiner.js │ │ ├── _TransactionMiner.ts │ │ ├── _TransactionPool.js │ │ ├── _Block.ts │ │ ├── _Blockchain.ts │ │ ├── _TransactionPool.ts │ │ ├── _Wallet.ts │ │ └── _Transaction.ts │ └── Network │ │ ├── _Root.js │ │ ├── _Nodes.js │ │ ├── _Root.ts │ │ └── _Nodes.ts ├── types │ ├── errors_interface.ts │ ├── errors_interface.js │ ├── inputMap_types.js │ └── inputMap_types.ts ├── Addon │ ├── hash-creator.ts │ ├── sign.ts │ ├── sign.js │ └── hash-creator.js └── classes │ ├── Blockchain │ ├── TransactionMiner.js │ ├── TransactionPool.js │ ├── TransactionMiner.ts │ ├── TransactionPool.ts │ ├── Block.ts │ ├── Block.js │ ├── Wallet.ts │ ├── Wallet.js │ ├── Transaction.js │ ├── Transaction.ts │ ├── Blockchain.ts │ └── Blockchain.js │ └── Network │ ├── Nodes.ts │ ├── Root.ts │ ├── Root.js │ └── Nodes.js ├── App ├── chain.log ├── nodes.js ├── nodes.ts ├── root.js ├── root.ts ├── index.ts └── index.js ├── Dockerfile ├── README.md ├── code.txt ├── config.ts ├── config.js ├── package.json ├── Scripts ├── work.js └── mine.js ├── Bin ├── dpx.ts ├── dpx └── dpx.js └── tsconfig.json /Src/interfaces/Blockchain/_Errors.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | exports.__esModule = true; 3 | -------------------------------------------------------------------------------- /Src/types/errors_interface.ts: -------------------------------------------------------------------------------- 1 | export interface _Errors { 2 | message: string; 3 | code: number; 4 | } 5 | -------------------------------------------------------------------------------- /Src/interfaces/Blockchain/_Errors.ts: -------------------------------------------------------------------------------- 1 | export interface _Errors { 2 | message: string; 3 | code: number; 4 | } 5 | -------------------------------------------------------------------------------- /Src/interfaces/Network/_Root.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | -------------------------------------------------------------------------------- /Src/types/errors_interface.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | -------------------------------------------------------------------------------- /Src/types/inputMap_types.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | -------------------------------------------------------------------------------- /Src/interfaces/Blockchain/_Block.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | -------------------------------------------------------------------------------- /Src/interfaces/Blockchain/_Wallet.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | -------------------------------------------------------------------------------- /Src/interfaces/Network/_Nodes.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | -------------------------------------------------------------------------------- /Src/interfaces/Blockchain/_Blockchain.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | -------------------------------------------------------------------------------- /Src/interfaces/Blockchain/_Transaction.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | -------------------------------------------------------------------------------- /Src/interfaces/Blockchain/_TransactionMiner.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | -------------------------------------------------------------------------------- /Src/interfaces/Blockchain/_TransactionMiner.ts: -------------------------------------------------------------------------------- 1 | export interface _TransactionMiner{ 2 | mineTransaction() : Promise; 3 | } -------------------------------------------------------------------------------- /Src/interfaces/Blockchain/_TransactionPool.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | -------------------------------------------------------------------------------- /App/chain.log: -------------------------------------------------------------------------------- 1 | [{"hash":"DEFAULT-DPX-GENESIS-HASH","lastHash":"DEFAULT-DPX-LAST-HASH","nonce":0,"difficulty":10,"timestamp":0,"data":{}}] -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:alpine 2 | USER root 3 | COPY . /NODE 4 | WORKDIR /NODE 5 | RUN npm install 6 | RUN npm link 7 | CMD ["node", "./App/index.js"] -------------------------------------------------------------------------------- /Src/types/inputMap_types.ts: -------------------------------------------------------------------------------- 1 | import { SignatureInput } from "elliptic"; 2 | 3 | export type inputMap_type = { 4 | timestamp: number; 5 | address: string; 6 | amount: number; 7 | signature: SignatureInput; 8 | }; 9 | -------------------------------------------------------------------------------- /Src/interfaces/Blockchain/_Block.ts: -------------------------------------------------------------------------------- 1 | import { _Transaction } from "./_Transaction"; 2 | 3 | export interface _Block { 4 | timestamp: number; 5 | data: {transaction ?: [_Transaction]}; 6 | hash: string; 7 | lastHash: string; 8 | nonce: number; 9 | difficulty: number; 10 | } 11 | -------------------------------------------------------------------------------- /Src/interfaces/Network/_Root.ts: -------------------------------------------------------------------------------- 1 | import { _Errors } from "../../types/errors_interface"; 2 | import { _Block } from "../Blockchain/_Block"; 3 | 4 | export interface _Root { 5 | start(): void; 6 | bet(channel: string, callback: Function): void; 7 | send(channel: string, data: any): void; 8 | } 9 | -------------------------------------------------------------------------------- /Src/interfaces/Network/_Nodes.ts: -------------------------------------------------------------------------------- 1 | import { _Block } from "../Blockchain/_Block"; 2 | import { _Blockchain } from "../Blockchain/_Blockchain"; 3 | 4 | export interface _Nodes { 5 | list : Array, 6 | start : () => void, 7 | broadcast : (name : string , data : any) => Promise, 8 | bet : (name : string , callback : Function) => void 9 | } -------------------------------------------------------------------------------- /Src/interfaces/Blockchain/_Blockchain.ts: -------------------------------------------------------------------------------- 1 | import { _Block } from "./_Block"; 2 | import { _Errors } from "../../types/errors_interface"; 3 | import { _Transaction } from "./_Transaction"; 4 | export interface _Blockchain { 5 | chain: Array<_Block>; 6 | addBlock : (data: { transaction : [_Transaction] }) => void 7 | replaceChain: (chain : Array<_Block>) => _Errors | boolean 8 | validTransactionData : (chain: Array<_Block>) => boolean | _Errors 9 | } -------------------------------------------------------------------------------- /Src/interfaces/Blockchain/_TransactionPool.ts: -------------------------------------------------------------------------------- 1 | import { _Transaction } from "./_Transaction"; 2 | import {_Wallet} from "./_Wallet"; 3 | import { _Block } from "./_Block"; 4 | export interface _TransactionPool { 5 | transactionMap : any, 6 | add : (transaction : _Transaction) => void ; 7 | isHave : (wallet : _Wallet) => _Transaction | undefined; 8 | clear : () => void; 9 | clearBlockchainTransactions : (chain : Array<_Block>) => void; 10 | } -------------------------------------------------------------------------------- /Src/interfaces/Blockchain/_Wallet.ts: -------------------------------------------------------------------------------- 1 | import { _Transaction } from "./_Transaction"; 2 | import { _Errors } from "../../types/errors_interface"; 3 | import { _Block } from "./_Block"; 4 | export interface _Wallet { 5 | balance : number, 6 | keyPair : any, 7 | publicKey : string, 8 | privateKey : string, 9 | sign : (data : any) => string; 10 | createTransaction : ( recipient : string, amount : number, chain : Array<_Block>) => (_Errors | _Transaction) 11 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Blockchain-client 2 | A blockchain built using Node js/Go 3 | The blockchain was supposed to reach the commercial stage in collaboration with one of the programming groups, which was canceled. 4 | I decided to make the source public, so that if anyone wanted to use it. 5 | 6 | Blockchain-Server-JS source -> [Blockchain-server-js](https://github.com/hamidreza01/Blockchain-Server-JS) 7 | 8 | Blockchain-Server-GO source -> [Blockchain-server-go](https://github.com/hamidreza01/Blockchain-Server-GO) 9 | -------------------------------------------------------------------------------- /code.txt: -------------------------------------------------------------------------------- 1 | - code list 2 | 3 | -- 100-199 - validation 4 | 5 | --- 101 - chain is short 6 | --- 102 - chain is invalid 7 | --- 111 - invalid transaction 8 | --- 112 - amount exceeds balance 9 | --- 120 - chain data is empty 10 | --- 121 - reward transaction is invalid 11 | --- 122 - reward transaction length is invalid 12 | --- 123 - transaction amount is invalid 13 | 14 | -- 200-299 - connection 15 | 16 | --- 200 - success 17 | --- 250 - port is already in use 18 | 19 | -- 500-599 - server 20 | 21 | --- 500 - server error 22 | -------------------------------------------------------------------------------- /Src/interfaces/Blockchain/_Transaction.ts: -------------------------------------------------------------------------------- 1 | import { _Errors } from "../../types/errors_interface"; 2 | import { inputMap_type } from "../../types/inputMap_types"; 3 | import { _Wallet } from "./_Wallet"; 4 | 5 | export interface _Transaction { 6 | id: string; 7 | inputMap: inputMap_type; 8 | outputMap: any; 9 | inputMapCreator: (senderWallet: _Wallet, outputMap: {}) => {}; 10 | outputMapCreator: ( 11 | senderWallet: _Wallet, 12 | amount: number, 13 | recipient: string 14 | ) => {}; 15 | update : (recipient: string,amount : number,senderWallet : _Wallet) => void | _Errors ; 16 | } 17 | -------------------------------------------------------------------------------- /Src/Addon/hash-creator.ts: -------------------------------------------------------------------------------- 1 | import * as crypto from "crypto"; 2 | export const hashCreator = (...data : string[]) => { 3 | const hash = crypto.createHash("sha256"); 4 | return hash.update(data.join("")).digest("hex"); 5 | }; 6 | // import * as crypto from "crypto"; 7 | // export const hashCreator = (...data: Array): string => { 8 | // const hash = crypto.createHash("sha256"); 9 | // return hash 10 | // .update( 11 | // data 12 | // .sort() 13 | // .map((x) => { 14 | // return JSON.stringify(x); 15 | // }) 16 | // .join(" ") 17 | // ) 18 | // .digest("hex"); 19 | // }; 20 | -------------------------------------------------------------------------------- /Src/Addon/sign.ts: -------------------------------------------------------------------------------- 1 | import elliptic, { SignatureInput } from "elliptic"; 2 | import { hashCreator } from "./hash-creator"; 3 | 4 | export const ec = new elliptic.ec("ed25519"); 5 | export const verify = ( 6 | data: any, 7 | sign: SignatureInput, 8 | publicKey: string 9 | ): boolean => { 10 | const vrf = ec.keyFromPublic(publicKey, "hex"); 11 | try { 12 | return vrf.verify(hashCreator(JSON.stringify(data)), sign); 13 | } catch (error) { 14 | return false; 15 | } 16 | }; 17 | export const recoveryKeyPair = (privateKey: string, publicKey : string) => { 18 | return ec.keyPair({ 19 | "priv" : Buffer.from(privateKey,'base64').toString() as any, 20 | "pubEnc" : publicKey, 21 | }) 22 | } 23 | -------------------------------------------------------------------------------- /config.ts: -------------------------------------------------------------------------------- 1 | const DEFUALT_DIFFICULTY = 10; 2 | import { _Block } from "./Src/interfaces/Blockchain/_Block"; 3 | import { _Transaction } from "./Src/interfaces/Blockchain/_Transaction"; 4 | const ADMIN = { 5 | httpIP: `localhost:45451`, 6 | }; 7 | const REWARD_TRANSACTION = { 8 | address: "**DPX Blockchain**", 9 | }; 10 | const REWARD = 10; 11 | export const config = { 12 | NODE_PORT: 1414, 13 | ADMIN, 14 | MINE_RATE: 1000 * 60 * 10, 15 | ROOT_URL: "127.0.0.1:1000", 16 | GENESIS_DATA: { 17 | hash: "DEFAULT-DPX-GENESIS-HASH", 18 | lastHash: "DEFAULT-DPX-LAST-HASH", 19 | nonce: 0, 20 | difficulty: DEFUALT_DIFFICULTY, 21 | timestamp: 0, 22 | data: {}, 23 | }, 24 | DEFUALT_BALANCE: 0, 25 | REWARD_TRANSACTION, 26 | REWARD, 27 | }; 28 | -------------------------------------------------------------------------------- /config.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | exports.config = void 0; 4 | const DEFUALT_DIFFICULTY = 10; 5 | const ADMIN = { 6 | httpIP: `localhost:45451`, 7 | }; 8 | const REWARD_TRANSACTION = { 9 | address: "**DPX Blockchain**", 10 | }; 11 | const REWARD = 10; 12 | exports.config = { 13 | NODE_PORT: 1414, 14 | ADMIN, 15 | MINE_RATE: 1000 * 60 * 10, 16 | ROOT_URL: "127.0.0.1:1000", 17 | GENESIS_DATA: { 18 | hash: "DEFAULT-DPX-GENESIS-HASH", 19 | lastHash: "DEFAULT-DPX-LAST-HASH", 20 | nonce: 0, 21 | difficulty: DEFUALT_DIFFICULTY, 22 | timestamp: 0, 23 | data: {}, 24 | }, 25 | DEFUALT_BALANCE: 0, 26 | REWARD_TRANSACTION, 27 | REWARD, 28 | }; 29 | -------------------------------------------------------------------------------- /App/nodes.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | const Transaction_1 = require("../Src/classes/Blockchain/Transaction"); 4 | function default_1(nodes, blockchain, transactionPool, admin, cluster) { 5 | nodes.bet("chain", (data) => { 6 | console.log('new chain : \n', data); 7 | if (blockchain.validTransactionData(data) === true && blockchain.replaceChain(data) === true) { 8 | transactionPool.clearBlockchainTransactions(data); 9 | } 10 | ; 11 | }); 12 | nodes.bet("transaction", (data) => { 13 | const check = Transaction_1.Transaction.isValid(data); 14 | if (check !== true) { 15 | return; 16 | } 17 | transactionPool.add(data); 18 | }); 19 | } 20 | exports.default = default_1; 21 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "ansicolor": "^1.1.100", 4 | "axios": "^0.26.1", 5 | "chalk": "^5.0.1", 6 | "commander": "^9.2.0", 7 | "elliptic": "^6.5.4", 8 | "express": "^4.17.3", 9 | "hex-to-bin": "^1.0.1", 10 | "hex-to-binary": "^1.0.1", 11 | "uniqid": "^5.4.0" 12 | }, 13 | "devDependencies": { 14 | "@types/node": "^17.0.23", 15 | "@types/elliptic": "^6.4.14", 16 | "@types/express": "^4.17.13", 17 | "@types/uniqid": "^5.3.2", 18 | "@types/uuid": "^8.3.4" 19 | }, 20 | "name": "developix-blockchain", 21 | "version": "1.0.0", 22 | "main": "config.js", 23 | "bin": { 24 | "dpx": "./Bin/dpx" 25 | }, 26 | "scripts": { 27 | "start": "node ./App/index.js", 28 | "test": "echo \"Error: no test specified\" && exit 1" 29 | }, 30 | "author": "", 31 | "license": "ISC", 32 | "description": "" 33 | } 34 | -------------------------------------------------------------------------------- /Src/classes/Blockchain/TransactionMiner.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | exports.TransactionMiner = void 0; 4 | const Transaction_1 = require("./Transaction"); 5 | class TransactionMiner { 6 | constructor(transactionPool, blockchain, wallet, nodes) { 7 | this.transactionPool = transactionPool; 8 | this.blockchain = blockchain; 9 | this.wallet = wallet; 10 | this.nodes = nodes; 11 | } 12 | mineTransaction() { 13 | return new Promise((res) => { 14 | const transactions = [Transaction_1.Transaction.reward(this.wallet), ...Object.values(this.transactionPool.transactionMap)]; 15 | this.blockchain.addBlock({ transaction: transactions }); 16 | this.nodes.broadcast("chain", this.blockchain.chain); 17 | this.transactionPool.clear(); 18 | res(); 19 | }); 20 | } 21 | } 22 | exports.TransactionMiner = TransactionMiner; 23 | -------------------------------------------------------------------------------- /Src/Addon/sign.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __importDefault = (this && this.__importDefault) || function (mod) { 3 | return (mod && mod.__esModule) ? mod : { "default": mod }; 4 | }; 5 | Object.defineProperty(exports, "__esModule", { value: true }); 6 | exports.recoveryKeyPair = exports.verify = exports.ec = void 0; 7 | const elliptic_1 = __importDefault(require("elliptic")); 8 | const hash_creator_1 = require("./hash-creator"); 9 | exports.ec = new elliptic_1.default.ec("ed25519"); 10 | const verify = (data, sign, publicKey) => { 11 | const vrf = exports.ec.keyFromPublic(publicKey, "hex"); 12 | try { 13 | return vrf.verify((0, hash_creator_1.hashCreator)(JSON.stringify(data)), sign); 14 | } 15 | catch (error) { 16 | return false; 17 | } 18 | }; 19 | exports.verify = verify; 20 | const recoveryKeyPair = (privateKey, publicKey) => { 21 | return exports.ec.keyPair({ 22 | "priv": Buffer.from(privateKey, 'base64').toString(), 23 | "pubEnc": publicKey, 24 | }); 25 | }; 26 | exports.recoveryKeyPair = recoveryKeyPair; 27 | -------------------------------------------------------------------------------- /Src/classes/Blockchain/TransactionPool.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | exports.TransactionPool = void 0; 4 | class TransactionPool { 5 | constructor() { 6 | this.transactionMap = {}; 7 | } 8 | add(transaction) { 9 | this.transactionMap[transaction.id] = transaction; 10 | } 11 | isHave(wallet) { 12 | let val = Object.values(this.transactionMap); 13 | return val.find(x => { 14 | return x.inputMap.address === wallet.publicKey; 15 | }); 16 | } 17 | clear() { 18 | this.transactionMap = {}; 19 | } 20 | clearBlockchainTransactions(chain) { 21 | for (let i = 1; i < chain.length; i++) { 22 | const block = chain[i]; 23 | for (let j = 1; j < block.data.transaction.length; j++) { 24 | const tx = block.data.transaction[j]; 25 | delete this.transactionMap[tx.id]; 26 | } 27 | } 28 | } 29 | } 30 | exports.TransactionPool = TransactionPool; 31 | ; 32 | -------------------------------------------------------------------------------- /App/nodes.ts: -------------------------------------------------------------------------------- 1 | import { _Block } from "../Src/interfaces/Blockchain/_Block"; 2 | import { _Blockchain } from "../Src/interfaces/Blockchain/_Blockchain"; 3 | import { _Nodes } from "../Src/interfaces/Network/_Nodes"; 4 | import { _TransactionPool } from "../Src/interfaces/Blockchain/_TransactionPool"; 5 | import { _Transaction } from "../Src/interfaces/Blockchain/_Transaction"; 6 | import { Transaction } from "../Src/classes/Blockchain/Transaction"; 7 | import { _Wallet } from "../Src/interfaces/Blockchain/_Wallet"; 8 | import { Cluster } from "cluster"; 9 | export default function ( 10 | nodes: _Nodes, 11 | blockchain: _Blockchain, 12 | transactionPool: _TransactionPool, 13 | admin : _Wallet, 14 | cluster : Cluster 15 | ) { 16 | 17 | nodes.bet("chain", (data: Array<_Block>) => { 18 | console.log('new chain : \n',data) 19 | if(blockchain.validTransactionData(data) === true && blockchain.replaceChain(data) === true) { 20 | transactionPool.clearBlockchainTransactions(data); 21 | }; 22 | }); 23 | 24 | nodes.bet("transaction", (data: _Transaction) => { 25 | const check = Transaction.isValid(data); 26 | if (check !== true) { 27 | return; 28 | } 29 | transactionPool.add(data); 30 | }); 31 | 32 | 33 | } 34 | -------------------------------------------------------------------------------- /Src/classes/Blockchain/TransactionMiner.ts: -------------------------------------------------------------------------------- 1 | import { _Nodes } from "../../interfaces/Network/_Nodes"; 2 | import { _Block } from "../../interfaces/Blockchain/_Block"; 3 | import { _Wallet } from "../../interfaces/Blockchain/_Wallet"; 4 | import { _Blockchain } from "../../interfaces/Blockchain/_Blockchain"; 5 | import { Transaction } from "./Transaction"; 6 | import { _TransactionPool } from "../../interfaces/Blockchain/_TransactionPool"; 7 | import { _Transaction } from "../../interfaces/Blockchain/_Transaction"; 8 | import { _TransactionMiner } from "../../interfaces/Blockchain/_TransactionMiner"; 9 | export class TransactionMiner implements _TransactionMiner{ 10 | constructor(private transactionPool : _TransactionPool, private blockchain : _Blockchain, private wallet : _Wallet, private nodes : _Nodes ){ 11 | } 12 | mineTransaction() : Promise { 13 | return new Promise((res)=>{ 14 | const transactions =[Transaction.reward(this.wallet),...Object.values(this.transactionPool.transactionMap)]; 15 | this.blockchain.addBlock({transaction : transactions as any}); 16 | this.nodes.broadcast("chain",this.blockchain.chain); 17 | this.transactionPool.clear(); 18 | res() 19 | }) 20 | } 21 | } -------------------------------------------------------------------------------- /Src/classes/Network/Nodes.ts: -------------------------------------------------------------------------------- 1 | import express from "express"; 2 | import { _Block } from "../../interfaces/Blockchain/_Block"; 3 | import { _Blockchain } from "../../interfaces/Blockchain/_Blockchain"; 4 | import axios from "axios"; 5 | import { _Nodes } from "../../interfaces/Network/_Nodes"; 6 | export class Nodes implements _Nodes { 7 | list: Array = [""]; 8 | private app = express(); 9 | private blockChain: any; 10 | constructor(private port: number) {} 11 | start(): void { 12 | this.app.use(express.json()); 13 | this.app.listen(this.port/*,"0.0.0.0"*/); 14 | } 15 | async broadcast(name: string, data: any): Promise { 16 | return new Promise(async (res) => { 17 | for (let i = 0; i < this.list.length; i++) { 18 | try { 19 | await axios.post(`http://${this.list[i]}/${name}`, data); 20 | console.log(`success send ${this.list[i]} with ${name} channel`); 21 | } catch (error) { 22 | console.log(`Error brodcast to ${this.list[i]} with ${name} channel`); 23 | } 24 | } 25 | res; 26 | }); 27 | } 28 | bet(name: string, callback: Function): void { 29 | this.app.use(express.json()); 30 | this.app.post("/" + name, (req, res) => { 31 | callback(req.body); 32 | res.send("ok"); 33 | }); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /App/root.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | const Root_1 = require("../Src/classes/Network/Root"); 4 | function default_1(blockChain, nodes, transactionPool, port) { 5 | const root = new Root_1.Root(port); 6 | root.start(); 7 | //@ts-ignore 8 | root.send("addMe", { data: { hash: root.hash, port } }); 9 | nodes.start(); 10 | root.bet("welcome", (data) => { 11 | console.log(data); 12 | nodes.list = data.nodes || nodes.list; 13 | blockChain.chain = data.chain || blockChain.chain; 14 | transactionPool.transactionMap = data.transaction || transactionPool.transactionMap; 15 | }); 16 | root.bet("sliceChain", (data) => { 17 | blockChain.chain = blockChain.chain.filter((x, i) => { 18 | return i < data; 19 | }); 20 | }); 21 | root.bet("reaplceChain", (data) => { 22 | blockChain.chain = data; 23 | }); 24 | root.bet("replaceNodes", (data) => { 25 | nodes.list = data.nodes; 26 | }); 27 | root.bet("newNode", (data) => { 28 | nodes.list.push(data); 29 | }); 30 | // root.bet("giveMeData", () => { 31 | // root.send("giveMeData", { 32 | // data: { chain: blockChain.chain, node: nodes.list }, 33 | // }); 34 | // }); 35 | } 36 | exports.default = default_1; 37 | -------------------------------------------------------------------------------- /Src/classes/Blockchain/TransactionPool.ts: -------------------------------------------------------------------------------- 1 | import { _Transaction } from "../../interfaces/Blockchain/_Transaction"; 2 | import { _TransactionPool } from "../../interfaces/Blockchain/_TransactionPool"; 3 | import { _Wallet } from "../../interfaces/Blockchain/_Wallet"; 4 | import { _Errors } from "../../types/errors_interface"; 5 | import { Transaction } from "./Transaction"; 6 | import {_Block} from "../../interfaces/Blockchain/_Block"; 7 | export class TransactionPool implements _TransactionPool { 8 | transactionMap : any = {}; 9 | constructor(){ 10 | } 11 | add(transaction : _Transaction) : void | _Errors { 12 | this.transactionMap[transaction.id] = transaction; 13 | } 14 | isHave(wallet : _Wallet) : _Transaction | undefined { 15 | let val = Object.values(this.transactionMap) as Array<_Transaction> 16 | return val.find(x=>{ 17 | return x.inputMap.address === wallet.publicKey 18 | }); 19 | } 20 | clear(){ 21 | this.transactionMap = {} 22 | } 23 | clearBlockchainTransactions(chain : Array<_Block>) : void { 24 | for(let i = 1 ; i < chain.length ; i++){ 25 | const block = chain[i]; 26 | for(let j = 1 ; j < block.data.transaction!.length ; j++){ 27 | const tx = block.data.transaction![j]; 28 | delete this.transactionMap[tx.id]; 29 | } 30 | } 31 | } 32 | }; -------------------------------------------------------------------------------- /Scripts/work.js: -------------------------------------------------------------------------------- 1 | const BlockChain = require("../BlockChain/BlockChain"); 2 | const { style } = require("cls.js"); 3 | const cluster = require("cluster"); 4 | const numCPUs = 0; 5 | 6 | if (cluster.isPrimary) { 7 | console.log(`Primary ${process.pid} is running`); 8 | 9 | for (let i = 0; i < numCPUs; i++) { 10 | cluster.fork(); 11 | }; 12 | 13 | 14 | cluster.on("exit", (worker, code, signal) => { 15 | console.log(`worker ${worker.process.pid} died`); 16 | }); 17 | } else { 18 | console.log(`Worker ${process.pid} started`); 19 | const blockChain = new BlockChain(); 20 | blockChain.addBlock({ data: "initial" }); 21 | let pt, nt, nb, td, average; 22 | const times = []; 23 | for (let i = 0; i < 10000; i++) { 24 | pt = blockChain.chain[blockChain.chain.length - 1].timeStamp; 25 | blockChain.addBlock({ data: `block${i}` }); 26 | nb = blockChain.chain[blockChain.chain.length - 1]; 27 | nt = nb.timeStamp; 28 | td = nt - pt; 29 | times.push(td); 30 | average = 31 | times.reduce((total, num) => { 32 | return total + num; 33 | }) / times.length; 34 | 35 | style({ 36 | text: `time to mine block : ${td}ms`, 37 | color: "blue", 38 | }); 39 | style({ 40 | text: `Difficulity : ${nb.difficulty}`, 41 | color: "red", 42 | }); 43 | 44 | style({ 45 | text: `average time : ${Math.floor(average)}ms`, 46 | color: "yellow", 47 | }); 48 | style({ 49 | text: `-----------------------`, 50 | color: "white", 51 | }); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /Scripts/mine.js: -------------------------------------------------------------------------------- 1 | const {Blockchain : BlockChain} = require("../Src/classes/Blockchain/Blockchain") 2 | const { style } = require("cls.js"); 3 | const cluster = require("cluster"); 4 | const numCPUs = 2; 5 | 6 | if (cluster.isPrimary) { 7 | console.log(`Primary ${process.pid} is running`); 8 | 9 | for (let i = 0; i < numCPUs; i++) { 10 | cluster.fork(); 11 | }; 12 | 13 | 14 | cluster.on("exit", (worker, code, signal) => { 15 | console.log(`worker ${worker.process.pid} died`); 16 | }); 17 | } else { 18 | console.log(`Worker ${process.pid} started`); 19 | const blockChain = new BlockChain(); 20 | blockChain.addBlock("initial"); 21 | let pt, nt, nb, td, average; 22 | const times = []; 23 | for (let i = 0; i < 10000; i++) { 24 | pt = blockChain.chain[blockChain.chain.length - 1].timestamp; 25 | blockChain.addBlock(`block${i}`); 26 | nb = blockChain.chain[blockChain.chain.length - 1]; 27 | nt = nb.timestamp; 28 | td = nt - pt; 29 | times.push(td); 30 | average = 31 | times.reduce((total, num) => { 32 | return total + num; 33 | }) / times.length; 34 | 35 | style({ 36 | text: `time to mine block : ${td}ms`, 37 | color: "blue", 38 | }); 39 | style({ 40 | text: `Difficulity : ${nb.difficulty}`, 41 | color: "red", 42 | }); 43 | 44 | style({ 45 | text: `average time : ${Math.floor(average)}ms`, 46 | color: "yellow", 47 | }); 48 | style({ 49 | text: `-----------------------`, 50 | color: "white", 51 | }) 52 | console.log(BlockChain.isValid(blockChain.chain)) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /App/root.ts: -------------------------------------------------------------------------------- 1 | import { Root } from "../Src/classes/Network/Root"; 2 | import { _Blockchain } from "../Src/interfaces/Blockchain/_Blockchain"; 3 | import { _Nodes } from "../Src/interfaces/Network/_Nodes"; 4 | import { _Root } from "../Src/interfaces/Network/_Root"; 5 | import { _Block } from "../Src/interfaces/Blockchain/_Block"; 6 | import { _TransactionPool } from "../Src/interfaces/Blockchain/_TransactionPool"; 7 | export default function ( 8 | blockChain: _Blockchain, 9 | nodes: _Nodes, 10 | transactionPool: _TransactionPool, 11 | port: number 12 | ): void { 13 | const root: _Root = new Root(port); 14 | root.start(); 15 | //@ts-ignore 16 | root.send("addMe", { data: {hash : root.hash, port} }); 17 | nodes.start(); 18 | root.bet("welcome", (data: any) => { 19 | console.log(data) 20 | nodes.list = data.nodes || nodes.list; 21 | blockChain.chain = data.chain || blockChain.chain; 22 | transactionPool.transactionMap = data.transaction || transactionPool.transactionMap; 23 | }); 24 | 25 | root.bet("sliceChain", (data: number) => { 26 | blockChain.chain = blockChain.chain.filter((x, i) => { 27 | return i < data; 28 | }); 29 | }); 30 | 31 | root.bet("reaplceChain", (data: Array<_Block>) => { 32 | blockChain.chain = data; 33 | }); 34 | 35 | root.bet("replaceNodes", (data: {nodes : Array}) => { 36 | nodes.list = data.nodes; 37 | }); 38 | 39 | root.bet("newNode", (data: string) => { 40 | nodes.list.push(data); 41 | }); 42 | 43 | // root.bet("giveMeData", () => { 44 | // root.send("giveMeData", { 45 | // data: { chain: blockChain.chain, node: nodes.list }, 46 | // }); 47 | // }); 48 | } 49 | -------------------------------------------------------------------------------- /Src/classes/Network/Root.ts: -------------------------------------------------------------------------------- 1 | import { _Root } from "../../interfaces/Network/_Root"; 2 | import { _Errors } from "../../types/errors_interface"; 3 | import { config } from "../../../config"; 4 | import express from "express"; 5 | import axios from "axios"; 6 | import { _Block } from "../../interfaces/Blockchain/_Block"; 7 | import uniqid from "uniqid"; 8 | export class Root implements _Root { 9 | readonly app = express(); 10 | private hash = uniqid(); 11 | constructor(private port: number) {} 12 | start(): void { 13 | this.app.use(express.json()); 14 | this.app.use((req, res, next) => { 15 | console.log( 16 | "\nip: %s\npath: %s\nmethod: %s\nbody: %s\n", 17 | req.ip, 18 | req.path, 19 | req.method, 20 | JSON.parse(Buffer.from(req.body.data, "base64").toString("ascii")) 21 | ); 22 | req.body.data = JSON.parse( 23 | Buffer.from(req.body.data, "base64").toString("ascii") 24 | ); 25 | next(); 26 | }); 27 | this.app.use((req, res, next) => { 28 | if (req.body.data.hash !== this.hash) { 29 | console.log("403") 30 | return res.send("403"); 31 | } 32 | next(); 33 | }); 34 | this.app.listen(this.port/*,"0.0.0.0"*/); 35 | } 36 | bet(channel: string, callback: Function): void { 37 | this.app.post("/" + channel, (req, res) => { 38 | callback(req.body.data); 39 | res.send("ok"); 40 | }); 41 | } 42 | send(channel: string, data: any) { 43 | try { 44 | data.hash = this.hash; 45 | axios.post(`http://${config.ROOT_URL}/${channel}`, data); 46 | } catch (err) { 47 | console.log(`error send data to root server with ${channel} channel`); 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Src/classes/Blockchain/Block.ts: -------------------------------------------------------------------------------- 1 | import { config } from "../../../config"; 2 | import { hashCreator } from "../../Addon/hash-creator"; 3 | import { _Block } from "../../interfaces/Blockchain/_Block"; 4 | 5 | import hexToBin from "hex-to-bin"; 6 | import { _Transaction } from "../../interfaces/Blockchain/_Transaction"; 7 | 8 | export class Block implements _Block { 9 | constructor( 10 | public timestamp: number, 11 | public data: {transaction ?: [_Transaction]}, 12 | public hash: string, 13 | public lastHash: string, 14 | public nonce: number, 15 | public difficulty: number 16 | ) {} 17 | static genesis(): _Block { 18 | return config.GENESIS_DATA; 19 | } 20 | static mineBlock(lastBlock: _Block, data: {transaction ?: [_Transaction]}): _Block { 21 | const { hash: lastHash } = lastBlock; 22 | let difficulty = lastBlock.difficulty; 23 | let nonce = 0; 24 | let hash: string, timestamp: number; 25 | do { 26 | nonce++; 27 | timestamp = Date.now(); 28 | difficulty = Block.adJustDifficulty(lastBlock, timestamp); 29 | hash = hashCreator( 30 | lastHash, 31 | nonce.toString(), 32 | timestamp.toString(), 33 | difficulty.toString(), 34 | JSON.stringify(data) 35 | ); 36 | } while (hexToBin(hash).slice(0, difficulty) !== "0".repeat(difficulty)); 37 | { 38 | return new Block(timestamp, data, hash, lastHash, nonce, difficulty); 39 | } 40 | } 41 | static adJustDifficulty(lastBlock: _Block, timestamp: number) { 42 | if (lastBlock.difficulty < 1) return 1; 43 | if (timestamp - lastBlock.timestamp > config.MINE_RATE) 44 | return lastBlock.difficulty - 1; 45 | return lastBlock.difficulty + 1; 46 | } 47 | } -------------------------------------------------------------------------------- /Src/Addon/hash-creator.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { 3 | if (k2 === undefined) k2 = k; 4 | var desc = Object.getOwnPropertyDescriptor(m, k); 5 | if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { 6 | desc = { enumerable: true, get: function() { return m[k]; } }; 7 | } 8 | Object.defineProperty(o, k2, desc); 9 | }) : (function(o, m, k, k2) { 10 | if (k2 === undefined) k2 = k; 11 | o[k2] = m[k]; 12 | })); 13 | var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { 14 | Object.defineProperty(o, "default", { enumerable: true, value: v }); 15 | }) : function(o, v) { 16 | o["default"] = v; 17 | }); 18 | var __importStar = (this && this.__importStar) || function (mod) { 19 | if (mod && mod.__esModule) return mod; 20 | var result = {}; 21 | if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); 22 | __setModuleDefault(result, mod); 23 | return result; 24 | }; 25 | Object.defineProperty(exports, "__esModule", { value: true }); 26 | exports.hashCreator = void 0; 27 | const crypto = __importStar(require("crypto")); 28 | const hashCreator = (...data) => { 29 | const hash = crypto.createHash("sha256"); 30 | return hash.update(data.join("")).digest("hex"); 31 | }; 32 | exports.hashCreator = hashCreator; 33 | // import * as crypto from "crypto"; 34 | // export const hashCreator = (...data: Array): string => { 35 | // const hash = crypto.createHash("sha256"); 36 | // return hash 37 | // .update( 38 | // data 39 | // .sort() 40 | // .map((x) => { 41 | // return JSON.stringify(x); 42 | // }) 43 | // .join(" ") 44 | // ) 45 | // .digest("hex"); 46 | // }; 47 | -------------------------------------------------------------------------------- /Src/classes/Blockchain/Block.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __importDefault = (this && this.__importDefault) || function (mod) { 3 | return (mod && mod.__esModule) ? mod : { "default": mod }; 4 | }; 5 | Object.defineProperty(exports, "__esModule", { value: true }); 6 | exports.Block = void 0; 7 | const config_1 = require("../../../config"); 8 | const hash_creator_1 = require("../../Addon/hash-creator"); 9 | const hex_to_bin_1 = __importDefault(require("hex-to-bin")); 10 | class Block { 11 | constructor(timestamp, data, hash, lastHash, nonce, difficulty) { 12 | this.timestamp = timestamp; 13 | this.data = data; 14 | this.hash = hash; 15 | this.lastHash = lastHash; 16 | this.nonce = nonce; 17 | this.difficulty = difficulty; 18 | } 19 | static genesis() { 20 | return config_1.config.GENESIS_DATA; 21 | } 22 | static mineBlock(lastBlock, data) { 23 | const { hash: lastHash } = lastBlock; 24 | let difficulty = lastBlock.difficulty; 25 | let nonce = 0; 26 | let hash, timestamp; 27 | do { 28 | nonce++; 29 | timestamp = Date.now(); 30 | difficulty = Block.adJustDifficulty(lastBlock, timestamp); 31 | hash = (0, hash_creator_1.hashCreator)(lastHash, nonce.toString(), timestamp.toString(), difficulty.toString(), JSON.stringify(data)); 32 | } while ((0, hex_to_bin_1.default)(hash).slice(0, difficulty) !== "0".repeat(difficulty)); 33 | { 34 | return new Block(timestamp, data, hash, lastHash, nonce, difficulty); 35 | } 36 | } 37 | static adJustDifficulty(lastBlock, timestamp) { 38 | if (lastBlock.difficulty < 1) 39 | return 1; 40 | if (timestamp - lastBlock.timestamp > config_1.config.MINE_RATE) 41 | return lastBlock.difficulty - 1; 42 | return lastBlock.difficulty + 1; 43 | } 44 | } 45 | exports.Block = Block; 46 | -------------------------------------------------------------------------------- /Src/classes/Network/Root.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __importDefault = (this && this.__importDefault) || function (mod) { 3 | return (mod && mod.__esModule) ? mod : { "default": mod }; 4 | }; 5 | Object.defineProperty(exports, "__esModule", { value: true }); 6 | exports.Root = void 0; 7 | const config_1 = require("../../../config"); 8 | const express_1 = __importDefault(require("express")); 9 | const axios_1 = __importDefault(require("axios")); 10 | const uniqid_1 = __importDefault(require("uniqid")); 11 | class Root { 12 | constructor(port) { 13 | this.port = port; 14 | this.app = (0, express_1.default)(); 15 | this.hash = (0, uniqid_1.default)(); 16 | } 17 | start() { 18 | this.app.use(express_1.default.json()); 19 | this.app.use((req, res, next) => { 20 | console.log("\nip: %s\npath: %s\nmethod: %s\nbody: %s\n", req.ip, req.path, req.method, JSON.parse(Buffer.from(req.body.data, "base64").toString("ascii"))); 21 | req.body.data = JSON.parse(Buffer.from(req.body.data, "base64").toString("ascii")); 22 | next(); 23 | }); 24 | this.app.use((req, res, next) => { 25 | if (req.body.data.hash !== this.hash) { 26 | console.log("403"); 27 | return res.send("403"); 28 | } 29 | next(); 30 | }); 31 | this.app.listen(this.port /*,"0.0.0.0"*/); 32 | } 33 | bet(channel, callback) { 34 | this.app.post("/" + channel, (req, res) => { 35 | callback(req.body.data); 36 | res.send("ok"); 37 | }); 38 | } 39 | send(channel, data) { 40 | try { 41 | data.hash = this.hash; 42 | axios_1.default.post(`http://${config_1.config.ROOT_URL}/${channel}`, data); 43 | } 44 | catch (err) { 45 | console.log(`error send data to root server with ${channel} channel`); 46 | } 47 | } 48 | } 49 | exports.Root = Root; 50 | -------------------------------------------------------------------------------- /Src/classes/Blockchain/Wallet.ts: -------------------------------------------------------------------------------- 1 | import { _Wallet } from "../../interfaces/Blockchain/_Wallet"; 2 | import { ec } from "../../Addon/sign"; 3 | import { config } from "../../../config"; 4 | import { hashCreator } from "../../Addon/hash-creator"; 5 | import { _Errors } from "../../types/errors_interface"; 6 | import { _Transaction } from "../../interfaces/Blockchain/_Transaction"; 7 | import { Transaction } from "./Transaction"; 8 | import { _Block } from "../../interfaces/Blockchain/_Block"; 9 | 10 | export class Wallet implements _Wallet { 11 | balance: number = config.DEFUALT_BALANCE; 12 | keyPair: any = ec.genKeyPair(); 13 | publicKey: string = this.keyPair.getPublic().encode("hex"); 14 | privateKey: string = Buffer.from(JSON.stringify(this.keyPair.getPrivate()).replace(/\"/g,'')).toString("base64"); 15 | // privateKey: string = this.keyPair.getPrivate(); 16 | sign(data: any): string { 17 | return this.keyPair.sign(hashCreator(JSON.stringify(data))); 18 | } 19 | createTransaction( 20 | recipient: string, 21 | amount: number, 22 | chain: Array<_Block> 23 | ): _Errors | _Transaction { 24 | this.balance = Wallet.calculateBalance(chain, this.publicKey)!; 25 | // console.log(amount , this.balance) 26 | if (amount > this.balance) { 27 | return { message: "amount exceeds balance", code: 112 }; 28 | } 29 | return new Transaction(this, amount, recipient); 30 | } 31 | static calculateBalance(chain: Array<_Block>, address: string) { 32 | let value = 0; 33 | let hasTransaction = false; 34 | for (let i = chain?.length - 1; i > 0; i--) { 35 | for (let transaction of Object.values(chain[i]?.data?.transaction!)) { 36 | if(transaction?.inputMap?.address === address){ 37 | hasTransaction = true; 38 | // break; 39 | } 40 | let outputValue; 41 | try { 42 | outputValue = transaction?.outputMap[address]; 43 | } catch (error) { 44 | outputValue = 0; 45 | } 46 | if (outputValue) { 47 | value += outputValue; 48 | } 49 | 50 | } 51 | if (hasTransaction) { 52 | break; 53 | } 54 | } 55 | return value; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /Src/classes/Network/Nodes.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { 3 | function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } 4 | return new (P || (P = Promise))(function (resolve, reject) { 5 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } 6 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } 7 | function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } 8 | step((generator = generator.apply(thisArg, _arguments || [])).next()); 9 | }); 10 | }; 11 | var __importDefault = (this && this.__importDefault) || function (mod) { 12 | return (mod && mod.__esModule) ? mod : { "default": mod }; 13 | }; 14 | Object.defineProperty(exports, "__esModule", { value: true }); 15 | exports.Nodes = void 0; 16 | const express_1 = __importDefault(require("express")); 17 | const axios_1 = __importDefault(require("axios")); 18 | class Nodes { 19 | constructor(port) { 20 | this.port = port; 21 | this.list = [""]; 22 | this.app = (0, express_1.default)(); 23 | } 24 | start() { 25 | this.app.use(express_1.default.json()); 26 | this.app.listen(this.port /*,"0.0.0.0"*/); 27 | } 28 | broadcast(name, data) { 29 | return __awaiter(this, void 0, void 0, function* () { 30 | return new Promise((res) => __awaiter(this, void 0, void 0, function* () { 31 | for (let i = 0; i < this.list.length; i++) { 32 | try { 33 | yield axios_1.default.post(`http://${this.list[i]}/${name}`, data); 34 | console.log(`success send ${this.list[i]} with ${name} channel`); 35 | } 36 | catch (error) { 37 | console.log(`Error brodcast to ${this.list[i]} with ${name} channel`); 38 | } 39 | } 40 | res; 41 | })); 42 | }); 43 | } 44 | bet(name, callback) { 45 | this.app.use(express_1.default.json()); 46 | this.app.post("/" + name, (req, res) => { 47 | callback(req.body); 48 | res.send("ok"); 49 | }); 50 | } 51 | } 52 | exports.Nodes = Nodes; 53 | -------------------------------------------------------------------------------- /Src/classes/Blockchain/Wallet.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | exports.Wallet = void 0; 4 | const sign_1 = require("../../Addon/sign"); 5 | const config_1 = require("../../../config"); 6 | const hash_creator_1 = require("../../Addon/hash-creator"); 7 | const Transaction_1 = require("./Transaction"); 8 | class Wallet { 9 | constructor() { 10 | this.balance = config_1.config.DEFUALT_BALANCE; 11 | this.keyPair = sign_1.ec.genKeyPair(); 12 | this.publicKey = this.keyPair.getPublic().encode("hex"); 13 | this.privateKey = Buffer.from(JSON.stringify(this.keyPair.getPrivate()).replace(/\"/g, '')).toString("base64"); 14 | } 15 | // privateKey: string = this.keyPair.getPrivate(); 16 | sign(data) { 17 | return this.keyPair.sign((0, hash_creator_1.hashCreator)(JSON.stringify(data))); 18 | } 19 | createTransaction(recipient, amount, chain) { 20 | this.balance = Wallet.calculateBalance(chain, this.publicKey); 21 | // console.log(amount , this.balance) 22 | if (amount > this.balance) { 23 | return { message: "amount exceeds balance", code: 112 }; 24 | } 25 | return new Transaction_1.Transaction(this, amount, recipient); 26 | } 27 | static calculateBalance(chain, address) { 28 | var _a, _b, _c; 29 | let value = 0; 30 | let hasTransaction = false; 31 | for (let i = (chain === null || chain === void 0 ? void 0 : chain.length) - 1; i > 0; i--) { 32 | for (let transaction of Object.values((_b = (_a = chain[i]) === null || _a === void 0 ? void 0 : _a.data) === null || _b === void 0 ? void 0 : _b.transaction)) { 33 | if (((_c = transaction === null || transaction === void 0 ? void 0 : transaction.inputMap) === null || _c === void 0 ? void 0 : _c.address) === address) { 34 | hasTransaction = true; 35 | // break; 36 | } 37 | let outputValue; 38 | try { 39 | outputValue = transaction === null || transaction === void 0 ? void 0 : transaction.outputMap[address]; 40 | } 41 | catch (error) { 42 | outputValue = 0; 43 | } 44 | if (outputValue) { 45 | value += outputValue; 46 | } 47 | } 48 | if (hasTransaction) { 49 | break; 50 | } 51 | } 52 | return value; 53 | } 54 | } 55 | exports.Wallet = Wallet; 56 | -------------------------------------------------------------------------------- /Src/classes/Blockchain/Transaction.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __importDefault = (this && this.__importDefault) || function (mod) { 3 | return (mod && mod.__esModule) ? mod : { "default": mod }; 4 | }; 5 | Object.defineProperty(exports, "__esModule", { value: true }); 6 | exports.Transaction = void 0; 7 | const uniqid_1 = __importDefault(require("uniqid")); 8 | const sign_1 = require("../../Addon/sign"); 9 | const config_1 = require("../../../config"); 10 | class Transaction { 11 | constructor(senderWallet, amount, recpient, inputMap, outputMap) { 12 | this.id = (0, uniqid_1.default)(); 13 | this.outputMap = {}; 14 | this.inputMap = { 15 | timestamp: 0, 16 | address: "", 17 | amount: 0, 18 | signature: { s: "", r: "" }, 19 | }; 20 | (this.outputMap = outputMap || this.outputMapCreator(senderWallet, amount, recpient)), 21 | (this.inputMap = inputMap || this.inputMapCreator(senderWallet, this.outputMap)); 22 | } 23 | inputMapCreator(senderWallet, outputMap) { 24 | return { 25 | timestamp: Date.now(), 26 | address: senderWallet.publicKey, 27 | amount: senderWallet.balance, 28 | signature: senderWallet.sign(outputMap), 29 | }; 30 | } 31 | outputMapCreator(senderWallet, amount, recipient) { 32 | let outputMap = {}; 33 | outputMap[senderWallet.publicKey] = senderWallet.balance - amount; 34 | outputMap[recipient] = amount; 35 | return outputMap; 36 | } 37 | update(recpient, amount, senderWallet) { 38 | if (this.outputMap[senderWallet.publicKey] < amount) { 39 | return { message: "amount exceeds balance", code: 112 }; 40 | } 41 | if (this.outputMap[recpient]) { 42 | this.outputMap[recpient] += amount; 43 | } 44 | else { 45 | this.outputMap[recpient] = amount; 46 | } 47 | this.inputMap = this.inputMapCreator(senderWallet, this.outputMap); 48 | } 49 | static isValid(transaction) { 50 | let total = Object.values(transaction.outputMap).reduce((all, val) => { 51 | return all + val; 52 | }); 53 | if (total !== transaction.inputMap.amount) { 54 | return { 55 | message: `invalid transaction from ${transaction.inputMap.address}`, 56 | code: 111, 57 | }; 58 | } 59 | if (!(0, sign_1.verify)(transaction.outputMap, transaction.inputMap.signature, transaction.inputMap.address)) { 60 | return { 61 | message: `invalid transaction from ${transaction.inputMap.address}`, 62 | code: 112, 63 | }; 64 | } 65 | return true; 66 | } 67 | static reward(minerWallet) { 68 | return new Transaction(minerWallet, 0, minerWallet.publicKey, config_1.config.REWARD_TRANSACTION, { [minerWallet.publicKey]: config_1.config.REWARD }); 69 | } 70 | } 71 | exports.Transaction = Transaction; 72 | -------------------------------------------------------------------------------- /Src/classes/Blockchain/Transaction.ts: -------------------------------------------------------------------------------- 1 | import { _Transaction } from "../../interfaces/Blockchain/_Transaction"; 2 | import { _Wallet } from "../../interfaces/Blockchain/_Wallet"; 3 | import uniqid from "uniqid"; 4 | import { _Errors } from "../../types/errors_interface"; 5 | import { verify } from "../../Addon/sign"; 6 | import { inputMap_type } from "../../types/inputMap_types"; 7 | import { config } from "../../../config"; 8 | export class Transaction implements _Transaction { 9 | id: string = uniqid(); 10 | public outputMap: any = {}; 11 | public inputMap: inputMap_type = { 12 | timestamp: 0, 13 | address: "", 14 | amount: 0, 15 | signature: { s: "", r: "" }, 16 | }; 17 | constructor(senderWallet: _Wallet, amount: number, recpient: string, inputMap?: inputMap_type, outputMap?: {}) { 18 | (this.outputMap = outputMap || this.outputMapCreator(senderWallet, amount, recpient)), 19 | (this.inputMap = inputMap || this.inputMapCreator(senderWallet, this.outputMap)); 20 | } 21 | inputMapCreator(senderWallet: _Wallet, outputMap: {}): inputMap_type { 22 | return { 23 | timestamp: Date.now(), 24 | address: senderWallet.publicKey, 25 | amount: senderWallet.balance, 26 | signature: senderWallet.sign(outputMap), 27 | }; 28 | } 29 | outputMapCreator( 30 | senderWallet: _Wallet, 31 | amount: number, 32 | recipient: string 33 | ): {} { 34 | let outputMap: any = {}; 35 | outputMap[senderWallet.publicKey] = senderWallet.balance - amount; 36 | outputMap[recipient] = amount; 37 | return outputMap; 38 | } 39 | update( 40 | recpient: string, 41 | amount: number, 42 | senderWallet: _Wallet 43 | ): void | _Errors { 44 | if (this.outputMap[senderWallet.publicKey] < amount) { 45 | return { message: "amount exceeds balance", code: 112 }; 46 | } 47 | if(this.outputMap[recpient]){ 48 | this.outputMap[recpient] += amount 49 | }else{ 50 | this.outputMap[recpient] = amount; 51 | } 52 | this.inputMap = this.inputMapCreator(senderWallet,this.outputMap) 53 | } 54 | static isValid(transaction: _Transaction): _Errors | boolean { 55 | let total = Object.values(transaction.outputMap).reduce((all, val: any) => { 56 | return (all as number) + val; 57 | }); 58 | if (total !== transaction.inputMap.amount) { 59 | return { 60 | message: `invalid transaction from ${transaction.inputMap.address}`, 61 | code: 111, 62 | }; 63 | } 64 | if ( 65 | !verify( 66 | transaction.outputMap, 67 | transaction.inputMap.signature, 68 | transaction.inputMap.address 69 | ) 70 | ) { 71 | return { 72 | message: `invalid transaction from ${transaction.inputMap.address}`, 73 | code: 112, 74 | }; 75 | } 76 | return true; 77 | } 78 | static reward(minerWallet: _Wallet): _Transaction { 79 | return new Transaction( 80 | minerWallet, 81 | 0, 82 | minerWallet.publicKey, 83 | config.REWARD_TRANSACTION as any, 84 | {[minerWallet.publicKey]: config.REWARD} 85 | ); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /Src/classes/Blockchain/Blockchain.ts: -------------------------------------------------------------------------------- 1 | import { hashCreator } from "../../Addon/hash-creator"; 2 | import { _Blockchain } from "../../interfaces/Blockchain/_Blockchain"; 3 | import { _Errors } from "../../types/errors_interface"; 4 | import { _Block } from "../../interfaces/Blockchain/_Block"; 5 | import { Block } from "./Block"; 6 | import { _Transaction } from "../../interfaces/Blockchain/_Transaction"; 7 | import { config } from "../../../config"; 8 | import { Transaction } from "./Transaction"; 9 | import { Wallet } from "./Wallet"; 10 | export class Blockchain implements _Blockchain { 11 | chain = [Block.genesis()]; 12 | 13 | addBlock(data: { transaction: [_Transaction] }): void { 14 | const block = Block.mineBlock(this.chain[this.chain.length - 1], data); 15 | this.chain.push(block); 16 | } 17 | 18 | static isValid(chain: Array<_Block>): boolean { 19 | if (JSON.stringify(chain[0]) !== JSON.stringify(Block.genesis())) 20 | return false; 21 | for (let i = 1; i < chain.length; i++) { 22 | if ( 23 | chain[i].hash !== 24 | hashCreator( 25 | chain[i].lastHash, 26 | chain[i].nonce.toString(), 27 | chain[i].timestamp.toString(), 28 | chain[i].difficulty.toString(), 29 | JSON.stringify(chain[i].data) 30 | ) 31 | ) { 32 | return false; 33 | } 34 | 35 | if (chain[i].lastHash !== chain[i - 1].hash) { 36 | return false; 37 | } 38 | if (Math.abs(chain[i - 1].difficulty - chain[i].difficulty) > 1) { 39 | return false; 40 | } 41 | } 42 | return true; 43 | } 44 | replaceChain(chain: Array<_Block>): _Errors | boolean { 45 | if (chain.length < this.chain.length) { 46 | return { message: "chain is short", code: 101 }; 47 | } 48 | if (!Blockchain.isValid(chain)) { 49 | return { message: "chain is not valid", code: 102 }; 50 | } 51 | this.chain = chain; 52 | return true; 53 | } 54 | validTransactionData(chain: Array<_Block>): boolean | _Errors { 55 | for (let i = 1; i < chain?.length; i++) { 56 | if (chain[i]?.data?.transaction?.length! < 1) 57 | return { message: "chain data is empty", code: 120 }; 58 | for (let transaction of chain[i]?.data?.transaction!) { 59 | let rewardNumber = 0; 60 | if ( 61 | transaction?.inputMap?.address === config.REWARD_TRANSACTION.address 62 | ) { 63 | rewardNumber++; 64 | if (rewardNumber > 1) { 65 | return { 66 | message: "reward transaction length is ivalid", 67 | code: 122, 68 | }; 69 | } 70 | if ( 71 | (Object.values(transaction.outputMap).reduce( 72 | (all, val) => ((all as any) + val) as any 73 | ) as number) > config.REWARD 74 | ) { 75 | return { message: "reward transaction is invalid", code: 121 }; 76 | } 77 | } else { 78 | let transactionResualt = Transaction.isValid(transaction); 79 | if (transactionResualt !== true) { 80 | return transactionResualt; 81 | } else { 82 | const trueValue = Wallet.calculateBalance( 83 | this.chain, 84 | transaction.inputMap.address 85 | ); 86 | if (trueValue !== transaction.inputMap.amount) { 87 | return { message: "transaction amount is invalid", code: 123 }; 88 | } 89 | } 90 | } 91 | } 92 | } 93 | return true; 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /Src/classes/Blockchain/Blockchain.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | exports.Blockchain = void 0; 4 | const hash_creator_1 = require("../../Addon/hash-creator"); 5 | const Block_1 = require("./Block"); 6 | const config_1 = require("../../../config"); 7 | const Transaction_1 = require("./Transaction"); 8 | const Wallet_1 = require("./Wallet"); 9 | class Blockchain { 10 | constructor() { 11 | this.chain = [Block_1.Block.genesis()]; 12 | } 13 | addBlock(data) { 14 | const block = Block_1.Block.mineBlock(this.chain[this.chain.length - 1], data); 15 | this.chain.push(block); 16 | } 17 | static isValid(chain) { 18 | if (JSON.stringify(chain[0]) !== JSON.stringify(Block_1.Block.genesis())) 19 | return false; 20 | for (let i = 1; i < chain.length; i++) { 21 | if (chain[i].hash !== 22 | (0, hash_creator_1.hashCreator)(chain[i].lastHash, chain[i].nonce.toString(), chain[i].timestamp.toString(), chain[i].difficulty.toString(), JSON.stringify(chain[i].data))) { 23 | return false; 24 | } 25 | if (chain[i].lastHash !== chain[i - 1].hash) { 26 | return false; 27 | } 28 | if (Math.abs(chain[i - 1].difficulty - chain[i].difficulty) > 1) { 29 | return false; 30 | } 31 | } 32 | return true; 33 | } 34 | replaceChain(chain) { 35 | if (chain.length < this.chain.length) { 36 | return { message: "chain is short", code: 101 }; 37 | } 38 | if (!Blockchain.isValid(chain)) { 39 | return { message: "chain is not valid", code: 102 }; 40 | } 41 | this.chain = chain; 42 | return true; 43 | } 44 | validTransactionData(chain) { 45 | var _a, _b, _c, _d, _e, _f; 46 | for (let i = 1; i < (chain === null || chain === void 0 ? void 0 : chain.length); i++) { 47 | if (((_c = (_b = (_a = chain[i]) === null || _a === void 0 ? void 0 : _a.data) === null || _b === void 0 ? void 0 : _b.transaction) === null || _c === void 0 ? void 0 : _c.length) < 1) 48 | return { message: "chain data is empty", code: 120 }; 49 | for (let transaction of (_e = (_d = chain[i]) === null || _d === void 0 ? void 0 : _d.data) === null || _e === void 0 ? void 0 : _e.transaction) { 50 | let rewardNumber = 0; 51 | if (((_f = transaction === null || transaction === void 0 ? void 0 : transaction.inputMap) === null || _f === void 0 ? void 0 : _f.address) === config_1.config.REWARD_TRANSACTION.address) { 52 | rewardNumber++; 53 | if (rewardNumber > 1) { 54 | return { 55 | message: "reward transaction length is ivalid", 56 | code: 122, 57 | }; 58 | } 59 | if (Object.values(transaction.outputMap).reduce((all, val) => (all + val)) > config_1.config.REWARD) { 60 | return { message: "reward transaction is invalid", code: 121 }; 61 | } 62 | } 63 | else { 64 | let transactionResualt = Transaction_1.Transaction.isValid(transaction); 65 | if (transactionResualt !== true) { 66 | return transactionResualt; 67 | } 68 | else { 69 | const trueValue = Wallet_1.Wallet.calculateBalance(this.chain, transaction.inputMap.address); 70 | if (trueValue !== transaction.inputMap.amount) { 71 | return { message: "transaction amount is invalid", code: 123 }; 72 | } 73 | } 74 | } 75 | } 76 | } 77 | return true; 78 | } 79 | } 80 | exports.Blockchain = Blockchain; 81 | -------------------------------------------------------------------------------- /Bin/dpx.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | import { Command } from "commander"; 3 | import axios from "axios"; 4 | import color from "ansicolor"; 5 | 6 | const cli = new Command(); 7 | 8 | const line = color.red("\n" + "-".repeat(process.stdout.columns) + "\n"); 9 | cli.version("1.0.0"); 10 | 11 | cli 12 | .command("start") 13 | .description("start node") 14 | .option("-a, --api", "enable api response") 15 | .action((option) => { 16 | axios 17 | .post("http://127.0.0.1:7612/start") 18 | .then((res) => { 19 | if (option.api) { 20 | return console.log(JSON.stringify(res.data)); 21 | } 22 | console.log( 23 | `${line} ${color.bright.green( 24 | "node started" 25 | )}\n Admin Wallet :\n Public Key : ${color.blue( 26 | res.data.mainWallet.publicKey 27 | )}\n Private Key : ${color.red( 28 | res.data.mainWallet.privateKey 29 | )}${line}` 30 | ); 31 | }) 32 | .catch(() => {}); 33 | }); 34 | cli 35 | .command("chain") 36 | .description("manage your node chain") 37 | .option("-a, --api", "enable api response") 38 | .option("-l, --log", "save chain log") 39 | .option("-r, --restart", "restart chain") 40 | .action((option) => { 41 | if (option.restart) { 42 | axios 43 | .post("http://127.0.0.1:7612/restart") 44 | .then((res) => { 45 | if (option.api) { 46 | return console.log(JSON.stringify(res.data)); 47 | } 48 | else if(!res.data.status){ 49 | return console.log(`${line} ${color.bright.green(res.data.message)}${line}`); 50 | } 51 | return console.log( 52 | `${line} ${color.bright.green("chain restarted")}\n${line}` 53 | ); 54 | }) 55 | .catch(() => {}); 56 | } else if (option.log) { 57 | axios 58 | .post("http://127.0.0.1:7612/chain/log", { 59 | location: process.cwd() + "/chain.log", 60 | }) 61 | .then((res) => { 62 | if (option.api) { 63 | return console.log(JSON.stringify(res.data)); 64 | } 65 | else if(!res.data.status){ 66 | return console.log(`${line} ${color.bright.green(res.data.message)}${line}`); 67 | } 68 | console.log( 69 | `${line} ${color.bright.green( 70 | "chain log saved" 71 | )}\n Location : ${color.blue(process.cwd() + "/chain.log")}${line}` 72 | ); 73 | }) 74 | .catch(() => {}); 75 | } 76 | }); 77 | 78 | cli 79 | .command("wallet") 80 | .description("manage your node wallets") 81 | .option("-a, --api", "enable api response") 82 | .option("-c, --create", "create a wallet") 83 | .option("-b, --balance ", "balance of wallet with address") 84 | .action((option) => { 85 | if (option.create) { 86 | axios 87 | .post("http://127.0.0.1:7612/wallet/create") 88 | .then((res) => { 89 | if (option.api) { 90 | return console.log(JSON.stringify(res.data)); 91 | } 92 | else if(!res.data.status){ 93 | return console.log(`${line} ${color.bright.green(res.data.message)}${line}`); 94 | } 95 | console.log( 96 | `${line} ${color.bright.green( 97 | "wallet created" 98 | )}\n Wallet :\n Public Key : ${color.blue( 99 | res.data.wallet.publicKey 100 | )}\n Private Key : ${color.red( 101 | res.data.wallet.privateKey 102 | )}${line}` 103 | ); 104 | }) 105 | .catch(() => {}); 106 | } else if (option.balance) { 107 | axios 108 | .post("http://127.0.0.1:7612/wallet/balance/" + option.balance) 109 | .then((res) => { 110 | if (option.api) { 111 | return console.log(JSON.stringify(res.data)); 112 | } 113 | else if(!res.data.status){ 114 | return console.log(`${line} ${color.bright.green(res.data.message)}${line}`); 115 | } 116 | console.log( 117 | `${line} ${color.bright.green( 118 | "wallet balance" 119 | )}\n Balance : ${color.blue(res.data.wallet.balance)}${line}` 120 | ); 121 | }) 122 | .then(() => {}); 123 | } 124 | }); 125 | 126 | cli 127 | .command("mine") 128 | .description("node mining manege") 129 | .option("-a, --api", "enable api response") 130 | .option("-c, --core ", "select cpu core for mining") 131 | .option("-s, --stop", "stop mining") 132 | .action((option) => { 133 | if (option.stop) { 134 | axios 135 | .post("http://127.0.0.1:7612/mine/stop") 136 | .then((res) => { 137 | if (option.api) { 138 | return console.log(JSON.stringify(res.data)); 139 | } 140 | else if(!res.data.status){ 141 | return console.log(`${line} ${color.bright.green(res.data.message)}${line}`); 142 | } 143 | console.log(`${line} ${color.bright.green("mining stopped")}${line}`); 144 | }) 145 | .catch((err) => { 146 | }); 147 | } else if (option.core) { 148 | axios 149 | .post("http://127.0.0.1:7612/mine/start/" + option.core) 150 | .then((res) => { 151 | if (option.api) { 152 | return console.log(JSON.stringify(res.data)); 153 | } 154 | console.log(`${line} ${color.bright.green("mining started")}${line}`); 155 | }) 156 | .catch(() => {}); 157 | } else { 158 | axios 159 | .post("http://127.0.0.1:7612/mine/start/1") 160 | .then((res) => { 161 | if (option.api) { 162 | return console.log(JSON.stringify(res.data)); 163 | } 164 | console.log(`${line} ${color.bright.green("mining started")}${line}`); 165 | }) 166 | .catch(() => {}); 167 | } 168 | }); 169 | 170 | cli 171 | .command("transaction") 172 | .description("manage your node transactions") 173 | .option("-a, --api", "enable api response") 174 | .option("-fpub,--fromPublic ", "from public address") 175 | .option("-fpri,--fromPrivate ", "from private address") 176 | .option("-tpub,--toPublic ", "to public address") 177 | .option("-v,--value ", "Transfer Value") 178 | .action((option) => { 179 | if ( 180 | option.fromPublic && 181 | option.toPublic && 182 | option.fromPrivate && 183 | option.value 184 | ) { 185 | axios 186 | .post("http://127.0.0.1:7612/transaction", { 187 | fromPublicKey: option.fromPublic, 188 | fromPrivateKey : option.fromPrivate, 189 | toPublic: option.toPublic, 190 | amount: option.value, 191 | }) 192 | .then((res) => { 193 | if (option.api) { 194 | return console.log(JSON.stringify(res.data)); 195 | } 196 | else if(!res.data.status){ 197 | return console.log(`${line} ${color.bright.green(res.data.message)}${line}`); 198 | } 199 | console.log(`${line} ${color.bright.green("transaction created")}${line}`); 200 | }) 201 | .catch((err) => {console.log(err)}); 202 | } 203 | }); 204 | 205 | cli.parse(process.argv); 206 | -------------------------------------------------------------------------------- /Bin/dpx: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | "use strict"; 3 | var __importDefault = (this && this.__importDefault) || function (mod) { 4 | return (mod && mod.__esModule) ? mod : { "default": mod }; 5 | }; 6 | Object.defineProperty(exports, "__esModule", { value: true }); 7 | const commander_1 = require("commander"); 8 | const axios_1 = __importDefault(require("axios")); 9 | const ansicolor_1 = __importDefault(require("ansicolor")); 10 | const cli = new commander_1.Command(); 11 | const line = ansicolor_1.default.red("\n" + "-".repeat(process.stdout.columns) + "\n"); 12 | cli.version("1.0.0"); 13 | cli 14 | .command("start") 15 | .description("start node") 16 | .option("-a, --api", "enable api response") 17 | .action((option) => { 18 | axios_1.default 19 | .post("http://127.0.0.1:7612/start") 20 | .then((res) => { 21 | if (option.api) { 22 | return console.log(JSON.stringify(res.data)); 23 | } 24 | console.log(`${line} ${ansicolor_1.default.bright.green("node started")}\n Admin Wallet :\n Public Key : ${ansicolor_1.default.blue(res.data.mainWallet.publicKey)}\n Private Key : ${ansicolor_1.default.red(res.data.mainWallet.privateKey)}${line}`); 25 | }) 26 | .catch(() => { }); 27 | }); 28 | cli 29 | .command("chain") 30 | .description("manage your node chain") 31 | .option("-a, --api", "enable api response") 32 | .option("-l, --log", "save chain log") 33 | .option("-r, --restart", "restart chain") 34 | .action((option) => { 35 | if (option.restart) { 36 | axios_1.default 37 | .post("http://127.0.0.1:7612/restart") 38 | .then((res) => { 39 | if (option.api) { 40 | return console.log(JSON.stringify(res.data)); 41 | } 42 | else if (!res.data.status) { 43 | return console.log(`${line} ${ansicolor_1.default.bright.green(res.data.message)}${line}`); 44 | } 45 | return console.log(`${line} ${ansicolor_1.default.bright.green("chain restarted")}\n${line}`); 46 | }) 47 | .catch(() => { }); 48 | } 49 | else if (option.log) { 50 | axios_1.default 51 | .post("http://127.0.0.1:7612/chain/log", { 52 | location: process.cwd() + "/chain.log", 53 | }) 54 | .then((res) => { 55 | if (option.api) { 56 | return console.log(JSON.stringify(res.data)); 57 | } 58 | else if (!res.data.status) { 59 | return console.log(`${line} ${ansicolor_1.default.bright.green(res.data.message)}${line}`); 60 | } 61 | console.log(`${line} ${ansicolor_1.default.bright.green("chain log saved")}\n Location : ${ansicolor_1.default.blue(process.cwd() + "/chain.log")}${line}`); 62 | }) 63 | .catch(() => { }); 64 | } 65 | }); 66 | cli 67 | .command("wallet") 68 | .description("manage your node wallets") 69 | .option("-a, --api", "enable api response") 70 | .option("-c, --create", "create a wallet") 71 | .option("-b, --balance ", "balance of wallet with address") 72 | .action((option) => { 73 | if (option.create) { 74 | axios_1.default 75 | .post("http://127.0.0.1:7612/wallet/create") 76 | .then((res) => { 77 | if (option.api) { 78 | return console.log(JSON.stringify(res.data)); 79 | } 80 | else if (!res.data.status) { 81 | return console.log(`${line} ${ansicolor_1.default.bright.green(res.data.message)}${line}`); 82 | } 83 | console.log(`${line} ${ansicolor_1.default.bright.green("wallet created")}\n Wallet :\n Public Key : ${ansicolor_1.default.blue(res.data.wallet.publicKey)}\n Private Key : ${ansicolor_1.default.red(res.data.wallet.privateKey)}${line}`); 84 | }) 85 | .catch(() => { }); 86 | } 87 | else if (option.balance) { 88 | axios_1.default 89 | .post("http://127.0.0.1:7612/wallet/balance/" + option.balance) 90 | .then((res) => { 91 | if (option.api) { 92 | return console.log(JSON.stringify(res.data)); 93 | } 94 | else if (!res.data.status) { 95 | return console.log(`${line} ${ansicolor_1.default.bright.green(res.data.message)}${line}`); 96 | } 97 | console.log(`${line} ${ansicolor_1.default.bright.green("wallet balance")}\n Balance : ${ansicolor_1.default.blue(res.data.wallet.balance)}${line}`); 98 | }) 99 | .then(() => { }); 100 | } 101 | }); 102 | cli 103 | .command("mine") 104 | .description("node mining manege") 105 | .option("-a, --api", "enable api response") 106 | .option("-c, --core ", "select cpu core for mining") 107 | .option("-s, --stop", "stop mining") 108 | .action((option) => { 109 | if (option.stop) { 110 | axios_1.default 111 | .post("http://127.0.0.1:7612/mine/stop") 112 | .then((res) => { 113 | if (option.api) { 114 | return console.log(JSON.stringify(res.data)); 115 | } 116 | else if (!res.data.status) { 117 | return console.log(`${line} ${ansicolor_1.default.bright.green(res.data.message)}${line}`); 118 | } 119 | console.log(`${line} ${ansicolor_1.default.bright.green("mining stopped")}${line}`); 120 | }) 121 | .catch((err) => { 122 | }); 123 | } 124 | else if (option.core) { 125 | axios_1.default 126 | .post("http://127.0.0.1:7612/mine/start/" + option.core) 127 | .then((res) => { 128 | if (option.api) { 129 | return console.log(JSON.stringify(res.data)); 130 | } 131 | console.log(`${line} ${ansicolor_1.default.bright.green("mining started")}${line}`); 132 | }) 133 | .catch(() => { }); 134 | } 135 | else { 136 | axios_1.default 137 | .post("http://127.0.0.1:7612/mine/start/1") 138 | .then((res) => { 139 | if (option.api) { 140 | return console.log(JSON.stringify(res.data)); 141 | } 142 | console.log(`${line} ${ansicolor_1.default.bright.green("mining started")}${line}`); 143 | }) 144 | .catch(() => { }); 145 | } 146 | }); 147 | cli 148 | .command("transaction") 149 | .description("manage your node transactions") 150 | .option("-a, --api", "enable api response") 151 | .option("-fpub,--fromPublic ", "from public address") 152 | .option("-fpri,--fromPrivate ", "from private address") 153 | .option("-tpub,--toPublic ", "to public address") 154 | .option("-v,--value ", "Transfer Value") 155 | .action((option) => { 156 | if (option.fromPublic && 157 | option.toPublic && 158 | option.fromPrivate && 159 | option.value) { 160 | axios_1.default 161 | .post("http://127.0.0.1:7612/transaction", { 162 | fromPublicKey: option.fromPublic, 163 | fromPrivateKey: option.fromPrivate, 164 | toPublic: option.toPublic, 165 | amount: option.value, 166 | }) 167 | .then((res) => { 168 | if (option.api) { 169 | return console.log(JSON.stringify(res.data)); 170 | } 171 | else if (!res.data.status) { 172 | return console.log(`${line} ${ansicolor_1.default.bright.green(res.data.message)}${line}`); 173 | } 174 | console.log(`${line} ${ansicolor_1.default.bright.green("transaction created")}${line}`); 175 | }) 176 | .catch((err) => { console.log(err); }); 177 | } 178 | }); 179 | cli.parse(process.argv); 180 | -------------------------------------------------------------------------------- /Bin/dpx.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | "use strict"; 3 | var __importDefault = (this && this.__importDefault) || function (mod) { 4 | return (mod && mod.__esModule) ? mod : { "default": mod }; 5 | }; 6 | Object.defineProperty(exports, "__esModule", { value: true }); 7 | const commander_1 = require("commander"); 8 | const axios_1 = __importDefault(require("axios")); 9 | const ansicolor_1 = __importDefault(require("ansicolor")); 10 | const cli = new commander_1.Command(); 11 | const line = ansicolor_1.default.red("\n" + "-".repeat(process.stdout.columns) + "\n"); 12 | cli.version("1.0.0"); 13 | cli 14 | .command("start") 15 | .description("start node") 16 | .option("-a, --api", "enable api response") 17 | .action((option) => { 18 | axios_1.default 19 | .post("http://127.0.0.1:7612/start") 20 | .then((res) => { 21 | if (option.api) { 22 | return console.log(JSON.stringify(res.data)); 23 | } 24 | console.log(`${line} ${ansicolor_1.default.bright.green("node started")}\n Admin Wallet :\n Public Key : ${ansicolor_1.default.blue(res.data.mainWallet.publicKey)}\n Private Key : ${ansicolor_1.default.red(res.data.mainWallet.privateKey)}${line}`); 25 | }) 26 | .catch(() => { }); 27 | }); 28 | cli 29 | .command("chain") 30 | .description("manage your node chain") 31 | .option("-a, --api", "enable api response") 32 | .option("-l, --log", "save chain log") 33 | .option("-r, --restart", "restart chain") 34 | .action((option) => { 35 | if (option.restart) { 36 | axios_1.default 37 | .post("http://127.0.0.1:7612/restart") 38 | .then((res) => { 39 | if (option.api) { 40 | return console.log(JSON.stringify(res.data)); 41 | } 42 | else if (!res.data.status) { 43 | return console.log(`${line} ${ansicolor_1.default.bright.green(res.data.message)}${line}`); 44 | } 45 | return console.log(`${line} ${ansicolor_1.default.bright.green("chain restarted")}\n${line}`); 46 | }) 47 | .catch(() => { }); 48 | } 49 | else if (option.log) { 50 | axios_1.default 51 | .post("http://127.0.0.1:7612/chain/log", { 52 | location: process.cwd() + "/chain.log", 53 | }) 54 | .then((res) => { 55 | if (option.api) { 56 | return console.log(JSON.stringify(res.data)); 57 | } 58 | else if (!res.data.status) { 59 | return console.log(`${line} ${ansicolor_1.default.bright.green(res.data.message)}${line}`); 60 | } 61 | console.log(`${line} ${ansicolor_1.default.bright.green("chain log saved")}\n Location : ${ansicolor_1.default.blue(process.cwd() + "/chain.log")}${line}`); 62 | }) 63 | .catch(() => { }); 64 | } 65 | }); 66 | cli 67 | .command("wallet") 68 | .description("manage your node wallets") 69 | .option("-a, --api", "enable api response") 70 | .option("-c, --create", "create a wallet") 71 | .option("-b, --balance ", "balance of wallet with address") 72 | .action((option) => { 73 | if (option.create) { 74 | axios_1.default 75 | .post("http://127.0.0.1:7612/wallet/create") 76 | .then((res) => { 77 | if (option.api) { 78 | return console.log(JSON.stringify(res.data)); 79 | } 80 | else if (!res.data.status) { 81 | return console.log(`${line} ${ansicolor_1.default.bright.green(res.data.message)}${line}`); 82 | } 83 | console.log(`${line} ${ansicolor_1.default.bright.green("wallet created")}\n Wallet :\n Public Key : ${ansicolor_1.default.blue(res.data.wallet.publicKey)}\n Private Key : ${ansicolor_1.default.red(res.data.wallet.privateKey)}${line}`); 84 | }) 85 | .catch(() => { }); 86 | } 87 | else if (option.balance) { 88 | axios_1.default 89 | .post("http://127.0.0.1:7612/wallet/balance/" + option.balance) 90 | .then((res) => { 91 | if (option.api) { 92 | return console.log(JSON.stringify(res.data)); 93 | } 94 | else if (!res.data.status) { 95 | return console.log(`${line} ${ansicolor_1.default.bright.green(res.data.message)}${line}`); 96 | } 97 | console.log(`${line} ${ansicolor_1.default.bright.green("wallet balance")}\n Balance : ${ansicolor_1.default.blue(res.data.wallet.balance)}${line}`); 98 | }) 99 | .then(() => { }); 100 | } 101 | }); 102 | cli 103 | .command("mine") 104 | .description("node mining manege") 105 | .option("-a, --api", "enable api response") 106 | .option("-c, --core ", "select cpu core for mining") 107 | .option("-s, --stop", "stop mining") 108 | .action((option) => { 109 | if (option.stop) { 110 | axios_1.default 111 | .post("http://127.0.0.1:7612/mine/stop") 112 | .then((res) => { 113 | if (option.api) { 114 | return console.log(JSON.stringify(res.data)); 115 | } 116 | else if (!res.data.status) { 117 | return console.log(`${line} ${ansicolor_1.default.bright.green(res.data.message)}${line}`); 118 | } 119 | console.log(`${line} ${ansicolor_1.default.bright.green("mining stopped")}${line}`); 120 | }) 121 | .catch((err) => { 122 | }); 123 | } 124 | else if (option.core) { 125 | axios_1.default 126 | .post("http://127.0.0.1:7612/mine/start/" + option.core) 127 | .then((res) => { 128 | if (option.api) { 129 | return console.log(JSON.stringify(res.data)); 130 | } 131 | console.log(`${line} ${ansicolor_1.default.bright.green("mining started")}${line}`); 132 | }) 133 | .catch(() => { }); 134 | } 135 | else { 136 | axios_1.default 137 | .post("http://127.0.0.1:7612/mine/start/1") 138 | .then((res) => { 139 | if (option.api) { 140 | return console.log(JSON.stringify(res.data)); 141 | } 142 | console.log(`${line} ${ansicolor_1.default.bright.green("mining started")}${line}`); 143 | }) 144 | .catch(() => { }); 145 | } 146 | }); 147 | cli 148 | .command("transaction") 149 | .description("manage your node transactions") 150 | .option("-a, --api", "enable api response") 151 | .option("-fpub,--fromPublic ", "from public address") 152 | .option("-fpri,--fromPrivate ", "from private address") 153 | .option("-tpub,--toPublic ", "to public address") 154 | .option("-v,--value ", "Transfer Value") 155 | .action((option) => { 156 | if (option.fromPublic && 157 | option.toPublic && 158 | option.fromPrivate && 159 | option.value) { 160 | axios_1.default 161 | .post("http://127.0.0.1:7612/transaction", { 162 | fromPublicKey: option.fromPublic, 163 | fromPrivateKey: option.fromPrivate, 164 | toPublic: option.toPublic, 165 | amount: option.value, 166 | }) 167 | .then((res) => { 168 | if (option.api) { 169 | return console.log(JSON.stringify(res.data)); 170 | } 171 | else if (!res.data.status) { 172 | return console.log(`${line} ${ansicolor_1.default.bright.green(res.data.message)}${line}`); 173 | } 174 | console.log(`${line} ${ansicolor_1.default.bright.green("transaction created")}${line}`); 175 | }) 176 | .catch((err) => { console.log(err); }); 177 | } 178 | }); 179 | cli.parse(process.argv); 180 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | /* Visit https://aka.ms/tsconfig.json to read more about this file */ 4 | 5 | /* Projects */ 6 | // "incremental": true, /* Enable incremental compilation */ 7 | // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ 8 | // "tsBuildInfoFile": "./", /* Specify the folder for .tsbuildinfo incremental compilation files. */ 9 | // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects */ 10 | // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ 11 | // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ 12 | 13 | /* Language and Environment */ 14 | "target": "ES6", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ 15 | // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ 16 | // "jsx": "preserve", /* Specify what JSX code is generated. */ 17 | // "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */ 18 | // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ 19 | // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h' */ 20 | // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ 21 | // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using `jsx: react-jsx*`.` */ 22 | // "reactNamespace": "", /* Specify the object invoked for `createElement`. This only applies when targeting `react` JSX emit. */ 23 | // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ 24 | // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ 25 | 26 | /* Modules */ 27 | "module": "CommonJS", /* Specify what module code is generated. */ 28 | // "rootDir": "./", /* Specify the root folder within your source files. */ 29 | // "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */ 30 | // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ 31 | // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ 32 | // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ 33 | // "typeRoots": [], /* Specify multiple folders that act like `./node_modules/@types`. */ 34 | // "types": [], /* Specify type package names to be included without being referenced in a source file. */ 35 | // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ 36 | // "resolveJsonModule": true, /* Enable importing .json files */ 37 | // "noResolve": true, /* Disallow `import`s, `require`s or ``s from expanding the number of files TypeScript should add to a project. */ 38 | 39 | /* JavaScript Support */ 40 | // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the `checkJS` option to get errors from these files. */ 41 | // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ 42 | // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from `node_modules`. Only applicable with `allowJs`. */ 43 | 44 | /* Emit */ 45 | // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ 46 | // "declarationMap": true, /* Create sourcemaps for d.ts files. */ 47 | // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ 48 | // "sourceMap": true, /* Create source map files for emitted JavaScript files. */ 49 | // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If `declaration` is true, also designates a file that bundles all .d.ts output. */ 50 | // "outDir": "./", /* Specify an output folder for all emitted files. */ 51 | // "removeComments": true, /* Disable emitting comments. */ 52 | // "noEmit": true, /* Disable emitting files from a compilation. */ 53 | // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ 54 | // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types */ 55 | // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ 56 | // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ 57 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ 58 | // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ 59 | // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ 60 | // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ 61 | // "newLine": "crlf", /* Set the newline character for emitting files. */ 62 | // "stripInternal": true, /* Disable emitting declarations that have `@internal` in their JSDoc comments. */ 63 | // "noEmitHelpers": true, /* Disable generating custom helper functions like `__extends` in compiled output. */ 64 | // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ 65 | // "preserveConstEnums": true, /* Disable erasing `const enum` declarations in generated code. */ 66 | // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ 67 | // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */ 68 | 69 | /* Interop Constraints */ 70 | // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ 71 | // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ 72 | "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables `allowSyntheticDefaultImports` for type compatibility. */ 73 | // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ 74 | "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ 75 | 76 | /* Type Checking */ 77 | "strict": true, /* Enable all strict type-checking options. */ 78 | // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied `any` type.. */ 79 | // "strictNullChecks": true, /* When type checking, take into account `null` and `undefined`. */ 80 | // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ 81 | // "strictBindCallApply": true, /* Check that the arguments for `bind`, `call`, and `apply` methods match the original function. */ 82 | // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ 83 | // "noImplicitThis": true, /* Enable error reporting when `this` is given the type `any`. */ 84 | // "useUnknownInCatchVariables": true, /* Type catch clause variables as 'unknown' instead of 'any'. */ 85 | // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ 86 | // "noUnusedLocals": true, /* Enable error reporting when a local variables aren't read. */ 87 | // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read */ 88 | // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ 89 | // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ 90 | // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ 91 | // "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */ 92 | // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ 93 | // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type */ 94 | // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ 95 | // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ 96 | 97 | /* Completeness */ 98 | // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ 99 | "skipLibCheck": true /* Skip type checking all .d.ts files. */ 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /App/index.ts: -------------------------------------------------------------------------------- 1 | import { Blockchain } from "../Src/classes/Blockchain/Blockchain"; 2 | import { _Blockchain } from "../Src/interfaces/Blockchain/_Blockchain"; 3 | import { _Block } from "../Src/interfaces/Blockchain/_Block"; 4 | import { Nodes } from "../Src/classes/Network/Nodes"; 5 | import { _Nodes } from "../Src/interfaces/Network/_Nodes"; 6 | import { Wallet } from "../Src/classes/Blockchain/Wallet"; 7 | import { _Wallet } from "../Src/interfaces/Blockchain/_Wallet"; 8 | import { TransactionPool } from "../Src/classes/Blockchain/TransactionPool"; 9 | import { _TransactionPool } from "../Src/interfaces/Blockchain/_TransactionPool"; 10 | import { _Transaction } from "../Src/interfaces/Blockchain/_Transaction"; 11 | import cluster from "cluster"; 12 | import rootFunction from "./root"; 13 | import nodesFunction from "./nodes"; 14 | import { config } from "../config"; 15 | import { recoveryKeyPair } from "../Src/Addon/sign"; 16 | import { _Errors } from "../Src/types/errors_interface"; 17 | import { Transaction } from "../Src/classes/Blockchain/Transaction"; 18 | import fs from "fs"; 19 | import express from "express"; 20 | (async () => { 21 | // console.log = () => {}; 22 | if (cluster.isPrimary) { 23 | try { 24 | process.stdout.write("Developix Blockchain is running..."); 25 | const app = express(); 26 | let blockchain: _Blockchain; 27 | let nodes: _Nodes; 28 | let admin: _Wallet; 29 | // let workers: any[] = []; 30 | let transactionPool: _TransactionPool; 31 | // let transactionMiner: _TransactionMiner; 32 | let start = false; 33 | // let interval : any; 34 | // --- node api 35 | app.use((req, res, next) => { 36 | if (req.connection.localAddress === req.connection.remoteAddress) { 37 | next(); 38 | } else { 39 | res.status(403).send("forbidden"); 40 | } 41 | }); 42 | app.use(express.json()); 43 | app.post("/start", (req, res) => { 44 | try { 45 | blockchain = new Blockchain(); 46 | nodes = new Nodes(config.NODE_PORT); 47 | admin = new Wallet(); 48 | transactionPool = new TransactionPool(); 49 | if (fs.existsSync("./chain.log")) { 50 | blockchain.chain = JSON.parse( 51 | fs.readFileSync("./chain.log").toString() 52 | ); 53 | } 54 | setInterval(() => { 55 | fs.writeFileSync("./chain.log", JSON.stringify(blockchain.chain)); 56 | }, 1000 * 60 * 60); 57 | rootFunction( 58 | blockchain, 59 | nodes, 60 | transactionPool, 61 | config.NODE_PORT + 2 62 | ); 63 | nodesFunction(nodes, blockchain, transactionPool, admin, cluster); 64 | start = true; 65 | res.status(200).json({ 66 | message: "node started", 67 | status: true, 68 | mainWallet: { 69 | publicKey: admin.publicKey, 70 | privateKey: admin.privateKey, 71 | }, 72 | }); 73 | } catch (err: any) { 74 | if (err) { 75 | res.status(500).json({ 76 | message: err.message, 77 | status: false, 78 | }); 79 | } else { 80 | res.status(500).json({ 81 | message: "unknown error", 82 | status: false, 83 | }); 84 | } 85 | } 86 | }); 87 | app.post("/wallet/create", (req, res) => { 88 | try { 89 | if (!start) { 90 | return res.status(400).json({ 91 | message: "node not started", 92 | status: false, 93 | }); 94 | } 95 | const wallet = new Wallet(); 96 | res.status(200).json({ 97 | message: "wallet created", 98 | status: true, 99 | wallet: { 100 | publicKey: wallet.publicKey, 101 | privateKey: wallet.privateKey, 102 | }, 103 | }); 104 | } catch (err) { 105 | res.status(500).json({ 106 | message: "wallet not created", 107 | status: false, 108 | }); 109 | } 110 | }); 111 | app.post("/wallet/balance/:publicKey", (req, res) => { 112 | try { 113 | if (!start) { 114 | return res.status(400).json({ 115 | message: "node not started", 116 | status: false, 117 | }); 118 | } 119 | const { publicKey }: any = req.params; 120 | const balance = Wallet.calculateBalance(blockchain.chain, publicKey); 121 | res.status(200).json({ 122 | message: "wallet balance", 123 | status: true, 124 | wallet: { 125 | publicKey, 126 | balance, 127 | }, 128 | }); 129 | } catch (err) { 130 | res.status(500).json({ 131 | message: "wallet balance not found", 132 | status: false, 133 | }); 134 | } 135 | }); 136 | app.post("/mine/stop", (req, res) => { 137 | try { 138 | if (!start) { 139 | return res.status(400).json({ 140 | message: "node not started", 141 | status: false, 142 | }); 143 | } 144 | for (const worker of Object.values(cluster.workers!)) { 145 | // worker?.send("end"); 146 | worker?.kill(); 147 | } 148 | res.status(200).json({ 149 | message: "mining stopped", 150 | status: true, 151 | }); 152 | } catch (err) { 153 | res.status(500).json({ 154 | message: "error mining", 155 | status: false, 156 | }); 157 | } 158 | }); 159 | app.post("/mine/start/:core", (req, res) => { 160 | try { 161 | if (!start) { 162 | return res.status(400).json({ 163 | message: "node not started", 164 | status: false, 165 | }); 166 | } 167 | const { core }: any = req.params; 168 | res.status(200).json({ 169 | message: "mining started", 170 | status: true, 171 | }); 172 | for (let i = 0; i < core; i++) { 173 | let worker = cluster.fork(); 174 | worker?.send({ 175 | chain: blockchain.chain, 176 | transactions: [ 177 | Transaction.reward(admin), 178 | ...Object.values(transactionPool.transactionMap).filter((v) => { 179 | Transaction.isValid(v as any) === true; 180 | }), 181 | ], 182 | }); 183 | worker.on("error", () => {}); 184 | cluster.on("message", async (worker, message, handle) => { 185 | worker?.send({ 186 | chain: blockchain.chain, 187 | transactions: [ 188 | Transaction.reward(admin), 189 | ...Object.values(transactionPool.transactionMap), 190 | ], 191 | }); 192 | if (message.chain) { 193 | if (blockchain.replaceChain(message.chain) === true) { 194 | await nodes.broadcast("chain", blockchain.chain); 195 | transactionPool.clear(); 196 | } 197 | } 198 | }); 199 | } 200 | //interval = setInterval(()=>{}) 201 | } catch (err) { 202 | res.status(500).json({ 203 | message: "error mining", 204 | status: false, 205 | }); 206 | } 207 | }); 208 | app.post("/transaction", (req, res) => { 209 | try { 210 | if (!start) { 211 | return res.status(400).json({ 212 | message: "node not started", 213 | status: false, 214 | }); 215 | } 216 | 217 | const { fromPublicKey, fromPrivateKey, toPublic, amount }: any = 218 | req.body; 219 | 220 | const wallet = new Wallet(); 221 | 222 | wallet.keyPair = recoveryKeyPair(fromPrivateKey, fromPublicKey); 223 | wallet.privateKey = fromPrivateKey; 224 | wallet.publicKey = fromPublicKey; 225 | 226 | let hasTransaction: any = transactionPool.isHave(wallet); 227 | console.log(hasTransaction == true); 228 | if (hasTransaction) { 229 | hasTransaction = hasTransaction.update(toPublic, amount, wallet); 230 | if (hasTransaction?.code) { 231 | return res.status(hasTransaction.code).json({ 232 | message: hasTransaction.message, 233 | status: false, 234 | }); 235 | } 236 | return res.status(200).json({ 237 | message: "transaction updated", 238 | status: true, 239 | hasTransaction, 240 | }); 241 | } 242 | 243 | let transaction = wallet.createTransaction( 244 | toPublic, 245 | Number(amount), 246 | blockchain.chain 247 | ); 248 | 249 | if ((transaction as _Errors)?.code) { 250 | return res.status((transaction as _Errors).code).json({ 251 | message: (transaction as _Errors).message, 252 | status: false, 253 | }); 254 | } 255 | transactionPool.add(transaction as _Transaction); 256 | nodes.broadcast("transaction", transaction); 257 | res.status(200).json({ 258 | message: "transaction created", 259 | status: true, 260 | transaction, 261 | }); 262 | } catch (err) { 263 | console.dir(err, { depth: null }); 264 | res.status(500).json({ 265 | message: "transaction not created", 266 | status: false, 267 | }); 268 | } 269 | }); 270 | 271 | app.post("/chain/log", (req, res) => { 272 | try { 273 | if (!start) { 274 | return res.status(400).json({ 275 | message: "node not started", 276 | status: false, 277 | }); 278 | } 279 | const { location }: any = req.body; 280 | if (fs.existsSync(location)) { 281 | fs.unlinkSync(location); 282 | fs.writeFileSync(location, JSON.stringify(blockchain.chain)); 283 | } else { 284 | fs.writeFileSync(location, JSON.stringify(blockchain.chain)); 285 | } 286 | res.status(200).json({ 287 | message: "chain loged", 288 | status: true, 289 | location, 290 | }); 291 | } catch (err) { 292 | res.status(500).json({ 293 | message: "chain log not saved", 294 | status: false, 295 | }); 296 | } 297 | }); 298 | 299 | app.get("/pool", (req, res) => { 300 | res.json(transactionPool.transactionMap); 301 | }); 302 | 303 | app.post("/chain/restart", (req, res) => { 304 | try { 305 | if (!start) { 306 | return res.status(400).json({ 307 | message: "node not started", 308 | status: false, 309 | }); 310 | } 311 | blockchain = new Blockchain(); 312 | transactionPool.clear(); 313 | res.status(200).json({ 314 | message: "chain restarted", 315 | status: true, 316 | }); 317 | } catch (error) { 318 | res.status(500).json({ 319 | message: "chain not restarted", 320 | status: false, 321 | }); 322 | } 323 | }); 324 | 325 | app.listen(7612, () => { 326 | console.log("node api run in port 7612"); 327 | }); 328 | } catch (error) { 329 | console.error(error); 330 | } 331 | } else { 332 | process.on("message", (data: any) => { 333 | // if (data === "end") { 334 | // return process.exit(0); 335 | // } 336 | let blockchain = new Blockchain(); 337 | blockchain.chain = data.chain; 338 | let transactions: [_Transaction] = data.transactions; 339 | blockchain.addBlock({ transaction: transactions }); 340 | (process.send as any)({ chain: blockchain.chain }); 341 | }); 342 | } 343 | })(); 344 | -------------------------------------------------------------------------------- /App/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { 3 | function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } 4 | return new (P || (P = Promise))(function (resolve, reject) { 5 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } 6 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } 7 | function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } 8 | step((generator = generator.apply(thisArg, _arguments || [])).next()); 9 | }); 10 | }; 11 | var __importDefault = (this && this.__importDefault) || function (mod) { 12 | return (mod && mod.__esModule) ? mod : { "default": mod }; 13 | }; 14 | Object.defineProperty(exports, "__esModule", { value: true }); 15 | const Blockchain_1 = require("../Src/classes/Blockchain/Blockchain"); 16 | const Nodes_1 = require("../Src/classes/Network/Nodes"); 17 | const Wallet_1 = require("../Src/classes/Blockchain/Wallet"); 18 | const TransactionPool_1 = require("../Src/classes/Blockchain/TransactionPool"); 19 | const cluster_1 = __importDefault(require("cluster")); 20 | const root_1 = __importDefault(require("./root")); 21 | const nodes_1 = __importDefault(require("./nodes")); 22 | const config_1 = require("../config"); 23 | const sign_1 = require("../Src/Addon/sign"); 24 | const Transaction_1 = require("../Src/classes/Blockchain/Transaction"); 25 | const fs_1 = __importDefault(require("fs")); 26 | const express_1 = __importDefault(require("express")); 27 | (() => __awaiter(void 0, void 0, void 0, function* () { 28 | // console.log = () => {}; 29 | if (cluster_1.default.isPrimary) { 30 | try { 31 | process.stdout.write("Developix Blockchain is running..."); 32 | const app = (0, express_1.default)(); 33 | let blockchain; 34 | let nodes; 35 | let admin; 36 | // let workers: any[] = []; 37 | let transactionPool; 38 | // let transactionMiner: _TransactionMiner; 39 | let start = false; 40 | // let interval : any; 41 | // --- node api 42 | app.use((req, res, next) => { 43 | if (req.connection.localAddress === req.connection.remoteAddress) { 44 | next(); 45 | } 46 | else { 47 | res.status(403).send("forbidden"); 48 | } 49 | }); 50 | app.use(express_1.default.json()); 51 | app.post("/start", (req, res) => { 52 | try { 53 | blockchain = new Blockchain_1.Blockchain(); 54 | nodes = new Nodes_1.Nodes(config_1.config.NODE_PORT); 55 | admin = new Wallet_1.Wallet(); 56 | transactionPool = new TransactionPool_1.TransactionPool(); 57 | if (fs_1.default.existsSync("./chain.log")) { 58 | blockchain.chain = JSON.parse(fs_1.default.readFileSync("./chain.log").toString()); 59 | } 60 | setInterval(() => { 61 | fs_1.default.writeFileSync("./chain.log", JSON.stringify(blockchain.chain)); 62 | }, 1000 * 60 * 60); 63 | (0, root_1.default)(blockchain, nodes, transactionPool, config_1.config.NODE_PORT + 2); 64 | (0, nodes_1.default)(nodes, blockchain, transactionPool, admin, cluster_1.default); 65 | start = true; 66 | res.status(200).json({ 67 | message: "node started", 68 | status: true, 69 | mainWallet: { 70 | publicKey: admin.publicKey, 71 | privateKey: admin.privateKey, 72 | }, 73 | }); 74 | } 75 | catch (err) { 76 | if (err) { 77 | res.status(500).json({ 78 | message: err.message, 79 | status: false, 80 | }); 81 | } 82 | else { 83 | res.status(500).json({ 84 | message: "unknown error", 85 | status: false, 86 | }); 87 | } 88 | } 89 | }); 90 | app.post("/wallet/create", (req, res) => { 91 | try { 92 | if (!start) { 93 | return res.status(400).json({ 94 | message: "node not started", 95 | status: false, 96 | }); 97 | } 98 | const wallet = new Wallet_1.Wallet(); 99 | res.status(200).json({ 100 | message: "wallet created", 101 | status: true, 102 | wallet: { 103 | publicKey: wallet.publicKey, 104 | privateKey: wallet.privateKey, 105 | }, 106 | }); 107 | } 108 | catch (err) { 109 | res.status(500).json({ 110 | message: "wallet not created", 111 | status: false, 112 | }); 113 | } 114 | }); 115 | app.post("/wallet/balance/:publicKey", (req, res) => { 116 | try { 117 | if (!start) { 118 | return res.status(400).json({ 119 | message: "node not started", 120 | status: false, 121 | }); 122 | } 123 | const { publicKey } = req.params; 124 | const balance = Wallet_1.Wallet.calculateBalance(blockchain.chain, publicKey); 125 | res.status(200).json({ 126 | message: "wallet balance", 127 | status: true, 128 | wallet: { 129 | publicKey, 130 | balance, 131 | }, 132 | }); 133 | } 134 | catch (err) { 135 | res.status(500).json({ 136 | message: "wallet balance not found", 137 | status: false, 138 | }); 139 | } 140 | }); 141 | app.post("/mine/stop", (req, res) => { 142 | try { 143 | if (!start) { 144 | return res.status(400).json({ 145 | message: "node not started", 146 | status: false, 147 | }); 148 | } 149 | for (const worker of Object.values(cluster_1.default.workers)) { 150 | // worker?.send("end"); 151 | worker === null || worker === void 0 ? void 0 : worker.kill(); 152 | } 153 | res.status(200).json({ 154 | message: "mining stopped", 155 | status: true, 156 | }); 157 | } 158 | catch (err) { 159 | res.status(500).json({ 160 | message: "error mining", 161 | status: false, 162 | }); 163 | } 164 | }); 165 | app.post("/mine/start/:core", (req, res) => { 166 | try { 167 | if (!start) { 168 | return res.status(400).json({ 169 | message: "node not started", 170 | status: false, 171 | }); 172 | } 173 | const { core } = req.params; 174 | res.status(200).json({ 175 | message: "mining started", 176 | status: true, 177 | }); 178 | for (let i = 0; i < core; i++) { 179 | let worker = cluster_1.default.fork(); 180 | worker === null || worker === void 0 ? void 0 : worker.send({ 181 | chain: blockchain.chain, 182 | transactions: [ 183 | Transaction_1.Transaction.reward(admin), 184 | ...Object.values(transactionPool.transactionMap).filter((v) => { 185 | Transaction_1.Transaction.isValid(v) === true; 186 | }), 187 | ], 188 | }); 189 | worker.on("error", () => { }); 190 | cluster_1.default.on("message", (worker, message, handle) => __awaiter(void 0, void 0, void 0, function* () { 191 | worker === null || worker === void 0 ? void 0 : worker.send({ 192 | chain: blockchain.chain, 193 | transactions: [ 194 | Transaction_1.Transaction.reward(admin), 195 | ...Object.values(transactionPool.transactionMap), 196 | ], 197 | }); 198 | if (message.chain) { 199 | if (blockchain.replaceChain(message.chain) === true) { 200 | yield nodes.broadcast("chain", blockchain.chain); 201 | transactionPool.clear(); 202 | } 203 | } 204 | })); 205 | } 206 | //interval = setInterval(()=>{}) 207 | } 208 | catch (err) { 209 | res.status(500).json({ 210 | message: "error mining", 211 | status: false, 212 | }); 213 | } 214 | }); 215 | app.post("/transaction", (req, res) => { 216 | try { 217 | if (!start) { 218 | return res.status(400).json({ 219 | message: "node not started", 220 | status: false, 221 | }); 222 | } 223 | const { fromPublicKey, fromPrivateKey, toPublic, amount } = req.body; 224 | const wallet = new Wallet_1.Wallet(); 225 | wallet.keyPair = (0, sign_1.recoveryKeyPair)(fromPrivateKey, fromPublicKey); 226 | wallet.privateKey = fromPrivateKey; 227 | wallet.publicKey = fromPublicKey; 228 | let hasTransaction = transactionPool.isHave(wallet); 229 | console.log(hasTransaction == true); 230 | if (hasTransaction) { 231 | hasTransaction = hasTransaction.update(toPublic, amount, wallet); 232 | if (hasTransaction === null || hasTransaction === void 0 ? void 0 : hasTransaction.code) { 233 | return res.status(hasTransaction.code).json({ 234 | message: hasTransaction.message, 235 | status: false, 236 | }); 237 | } 238 | return res.status(200).json({ 239 | message: "transaction updated", 240 | status: true, 241 | hasTransaction, 242 | }); 243 | } 244 | let transaction = wallet.createTransaction(toPublic, Number(amount), blockchain.chain); 245 | if (transaction === null || transaction === void 0 ? void 0 : transaction.code) { 246 | return res.status(transaction.code).json({ 247 | message: transaction.message, 248 | status: false, 249 | }); 250 | } 251 | transactionPool.add(transaction); 252 | nodes.broadcast("transaction", transaction); 253 | res.status(200).json({ 254 | message: "transaction created", 255 | status: true, 256 | transaction, 257 | }); 258 | } 259 | catch (err) { 260 | console.dir(err, { depth: null }); 261 | res.status(500).json({ 262 | message: "transaction not created", 263 | status: false, 264 | }); 265 | } 266 | }); 267 | app.post("/chain/log", (req, res) => { 268 | try { 269 | if (!start) { 270 | return res.status(400).json({ 271 | message: "node not started", 272 | status: false, 273 | }); 274 | } 275 | const { location } = req.body; 276 | if (fs_1.default.existsSync(location)) { 277 | fs_1.default.unlinkSync(location); 278 | fs_1.default.writeFileSync(location, JSON.stringify(blockchain.chain)); 279 | } 280 | else { 281 | fs_1.default.writeFileSync(location, JSON.stringify(blockchain.chain)); 282 | } 283 | res.status(200).json({ 284 | message: "chain loged", 285 | status: true, 286 | location, 287 | }); 288 | } 289 | catch (err) { 290 | res.status(500).json({ 291 | message: "chain log not saved", 292 | status: false, 293 | }); 294 | } 295 | }); 296 | app.get("/pool", (req, res) => { 297 | res.json(transactionPool.transactionMap); 298 | }); 299 | app.post("/chain/restart", (req, res) => { 300 | try { 301 | if (!start) { 302 | return res.status(400).json({ 303 | message: "node not started", 304 | status: false, 305 | }); 306 | } 307 | blockchain = new Blockchain_1.Blockchain(); 308 | transactionPool.clear(); 309 | res.status(200).json({ 310 | message: "chain restarted", 311 | status: true, 312 | }); 313 | } 314 | catch (error) { 315 | res.status(500).json({ 316 | message: "chain not restarted", 317 | status: false, 318 | }); 319 | } 320 | }); 321 | app.listen(7612, () => { 322 | console.log("node api run in port 7612"); 323 | }); 324 | } 325 | catch (error) { 326 | console.error(error); 327 | } 328 | } 329 | else { 330 | process.on("message", (data) => { 331 | // if (data === "end") { 332 | // return process.exit(0); 333 | // } 334 | let blockchain = new Blockchain_1.Blockchain(); 335 | blockchain.chain = data.chain; 336 | let transactions = data.transactions; 337 | blockchain.addBlock({ transaction: transactions }); 338 | process.send({ chain: blockchain.chain }); 339 | }); 340 | } 341 | }))(); 342 | --------------------------------------------------------------------------------