import { sha256 } from "js-sha256" ; import { serialize } from "serializer.ts/Serializer" ; import BigNumber from "bignumber.js" ; export type Address = string ; export class Transaction { public senderAddress : Address ; public recipientAddress : Address ; public value : number ; constructor ( senderAddress , recipientAddress , value ) { this . senderAddress = senderAddress ; this . recipientAddress = recipientAddress ; this . value = value ; } } export class Block { public blockNumber : number ; public transactions : Array < Transaction > ; public timestamp : number ; public nonce : number ; public prevBlock : string ; constructor ( blockNumber , transactions , timestamp , nonce , prevBlock ) { this . blockNumber = blockNumber ; this . transactions = transactions ; this . timestamp = timestamp ; this . nonce = nonce ; this . prevBlock = prevBlock ; } public sha256 ( ) { return sha256 ( JSON . stringify ( serialize < Block > ( this ) ) ) ; } } export class Blockchain { public static readonly GENESIS_BLOCK = new Block ( 0 , [ ] , Blockchain . now ( ) , 0 , "" ) ; public static readonly DIFFICULTY = 4 ; public static readonly TARGET = 2 ** ( 256 - Blockchain . DIFFICULTY ) ; public nodeId : string ; public blocks : Array < Block > ; public transactionPool : Array < Transaction > ; constructor ( nodeId ) { this . nodeId = nodeId ; this . blocks = [ Blockchain . GENESIS_BLOCK ] ; this . transactionPool = [ ] ; } public static isPoWValid ( pow ) { try { if ( ! pow . startsWith ( "0x" ) ) { pow = `0x${ pow }` ; } return new BigNumber ( pow ) . lessThanOrEqualTo ( Blockchain . TARGET . toString ( ) ) ; } catch { return false ; } } public mineBlock ( transactions ) { const lastBlock = this . getLastBlock ( ) ; const newBlock = new Block ( lastBlock . blockNumber + 1 , transactions , Blockchain . now ( ) , 0 , lastBlock . sha256 ( ) ) ; while ( true ) { const pow = newBlock . sha256 ( ) ; console . log ( `Mining #${ newBlock . blockNumber }: nonce: ${ newBlock . nonce }, pow: ${ pow }` ) ; if ( Blockchain . isPoWValid ( pow ) ) { console . log ( `Found valid POW: ${ pow }!` ) ; break ; } newBlock . nonce ++ ; } return newBlock ; } public submitTransaction ( senderAddress , recipientAddress , value ) { this . transactionPool . push ( new Transaction ( senderAddress , recipientAddress , value ) ) ; } public createBlock ( ) { const newBlock = this . mineBlock ( this . transactionPool ) ; this . blocks . push ( newBlock ) ; this . transactionPool = [ ] ; return newBlock ; } public getLastBlock ( ) { return this . blocks [ this . blocks . length - 1 ] ; } public static now ( ) { return Math . round ( new Date ( ) . getTime ( ) / 1000 ) ; } } const blockchain = new Blockchain ( "node123" ) ; blockchain . submitTransaction ( "Alice" , "Bob" , 1000 ) ; blockchain . submitTransaction ( "Alice" , "Eve" , 12345 ) ; const block = blockchain . createBlock ( ) ; console . log ( `Mined block: ${ JSON . stringify ( serialize ( block ) ) }` ) ; console . log ( `Mined block with: ${ block . sha256 ( ) }` ) ;