-
Notifications
You must be signed in to change notification settings - Fork 0
/
Block.cs
131 lines (118 loc) · 4.28 KB
/
Block.cs
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
using System;
using System.IO;
using System.Linq;
using System.Collections;
namespace SimpleCrypto {
[Serializable]
public class Block {
public int height;
public byte[] hash;
public byte[] previousHash;
public int timestamp;
public BlockData data;
public int difficulty;
public int nonce;
public bool isComplete {get {
if (hash != null && data != null) {
return true;
} else {
return false;
}
}}
public Block(int index, byte[] previousHash, int timestamp, BlockData data, int difficulty) {
this.height = index;
this.previousHash = previousHash;
this.timestamp = timestamp;
this.data = data;
this.difficulty = difficulty;
}
public byte[] generateHash() {
byte[] toHash = BitConverter.GetBytes(height)
.Concat(previousHash)
.Concat(BitConverter.GetBytes(timestamp))
.Concat(data.getBytes())
.Concat(BitConverter.GetBytes(difficulty))
.Concat(BitConverter.GetBytes(nonce)).ToArray();
return Cryptography.SHA256HashBytes(toHash);
}
public void mineBlockHash() {
bool hashValid = false;
int nonce = 0;
byte[] hash = generateHash();
while (!hashValid) {
if (_isValidHash(hash, difficulty)) {
hashValid = true;
} else {
nonce++;
hash = generateHash();
}
}
this.hash = hash;
this.nonce = nonce;
}
private static bool _isValidHash(byte[] hash, int difficulty) {
BitArray hashBits = new BitArray(hash);
if (difficulty > hashBits.Length) {
throw new IndexOutOfRangeException("Difficulty exceeds total length of hash");
}
// Return false if any bits in first [difficulty] bits are 1
for (int i = 0; i < difficulty; i++) {
if (hashBits[i]) {
return false;
}
}
return true;
}
public bool isValidBlock(Block previousBlock, UTxOut[] uTxOuts) {
if (height != previousBlock.height - 1) {
Console.WriteLine("New block has invalid index");
return false;
}
if (previousHash != previousBlock.hash) {
Console.WriteLine("New block has invalid previous hash");
return false;
}
if (generateHash() != hash) {
Console.WriteLine("New block's hash does not match content hash");
return false;
}
if (!_isValidHash(hash, difficulty)) {
Console.WriteLine("New block hash does not meet difficulty requirements");
return false;
}
if (!data.isValidData(uTxOuts, height)) {
Console.WriteLine("Block data does not meet requirements");
return false;
}
return true;
}
}
[Serializable]
public class BlockData {
public CoinbaseTransaction coinbaseTransaction;
public Transaction[] transactions;
public BlockData(CoinbaseTransaction coinbaseTransaction, Transaction[] transactions, int blockHeight) {
this.coinbaseTransaction = coinbaseTransaction;
this.transactions = transactions;
}
public bool isValidData(UTxOut[] uTxOuts, int blockHeight) {
// Check that coinbase and all regular transactions are valid.
if (!coinbaseTransaction.isValidCoinbaseTransaction(blockHeight)) {
return false;
}
foreach (Transaction tx in transactions) {
if(!tx.isValidTransaction(uTxOuts)) {
return false;
}
}
return true;
}
public byte[] getBytes() {
byte[] bytes = coinbaseTransaction.getBytes();
foreach (Transaction tx in transactions) {
bytes.Concat(tx.getBytes());
}
return bytes;
}
}
}