-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathblockchain.ts
More file actions
149 lines (120 loc) · 4.17 KB
/
Copy pathblockchain.ts
File metadata and controls
149 lines (120 loc) · 4.17 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
import * as crypto from "crypto";
/**
* A transaction is a record of an exchange of legos between a payer who sends
* the money and a payee who receives the money. These two parties are defined
* by their respective public keys.
*/
class Transaction {
constructor(
public amount: number,
public payer: string, // payer public key
public payee: string // payee public key
) {}
toString() {
return JSON.stringify( this );
}
}
/**
* A block is a group of transaction which will be an element in the blocks
* linked list or "chain".
*/
class Block {
// a random number used in problem for proof of work
public nonce = Math.round( Math.random() * 999999999 );
constructor(
public prevHash: string,
public transaction: Transaction,
public ts = Date.now()
) {}
get hash() {
const str = JSON.stringify( this );
const hash = crypto.createHash( 'SHA256' ); // setup hashing function
// add string to hash
hash.update( str ).end();
return hash.digest( 'hex' );
}
}
/**
* The chain is a singleton linked list of blocks in the blockchain.
*/
class Chain {
public static instance = new Chain(); // force singleton
chain: Block[];
constructor() {
this.chain = [ new Block(
'',
new Transaction( 100, 'genesis', 'thudsonbu' ) // genisis block of blockchain
)];
}
get lastBlock() {
return this.chain[ this.chain.length - 1 ];
}
/**
* Mining is the process of finding a random solution to a very difficult
* problem. This is used to avoid a double spending issue in a blockchain.
* If two blocks are created, the first to be verified by solving the mining
* problem and confirmed across all nodes will be added to the blockchain. If
* the two are confirmed simultaneously, the transaction with the most
* confirmations will be added to the chain.
* @param nonce
*/
mine( nonce: number ) {
let solution = 1;
console.log( 'mining...' );
while( true ) {
const hash = crypto.createHash( 'MD5' );
hash.update( ( nonce + solution ).toString() ).end();
const attempt = hash.digest( 'hex' );
if ( attempt.substr( 0, 4 ) === '0000' ) {
console.log( `Solved: ${ solution } ` );
return solution;
}
solution += 1;
}
}
addBlock( transaction: Transaction, senderPublicKey: string, signature: Buffer ) {
// create a verifier for the transaction
const verifier = crypto.createVerify( 'SHA256' );
verifier.update( transaction.toString() );
// verify created hash with signature
const isValid = verifier.verify( senderPublicKey, signature );
if ( isValid ) { // if valid add to chain
const newBlock = new Block( this.lastBlock.hash, transaction );
this.chain.push( newBlock );
}
}
}
/**
* The wallet functions as the key manager for a user. When a transaction is
* created, a hash of the transaction data is first created using sha256. In
* order to make sure that the transaction was made by the payer, the hash is
* signed with the payers private key. This makes sure that the transaction
* cannot be changed as the signed hash can be verified by comparing it with
* a hash of plain text transaction data after decrypting it with the public
* key.
*/
class Wallet {
public publicKey: string;
public privateKey: string;
constructor() {
// create new public and private key pair for wallet
const keypair = crypto.generateKeyPairSync( 'rsa', {
modulusLength: 2048,
publicKeyEncoding: { type: 'spki', format: 'pem' },
privateKeyEncoding: { type: 'pkcs8', format: 'pem' }
});
this.privateKey = keypair.privateKey;
this.publicKey = keypair.publicKey;
}
sendMoney( amount: number, payeePublicKey: string ) {
// create a new transaction
const transaction = new Transaction( amount, this.publicKey, payeePublicKey );
// create a hash for the transaction
const sign = crypto.createSign( 'SHA256' );
sign.update( transaction.toString() ).end();
// sign the hash with the private key
const signature = sign.sign( this.privateKey );
// add the new block to the chian
Chain.instance.addBlock( transaction, this.publicKey, signature );
}
}