-
Notifications
You must be signed in to change notification settings - Fork 0
/
BlockChain.java
161 lines (139 loc) · 6.49 KB
/
BlockChain.java
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
150
151
152
153
154
155
156
157
158
159
160
161
import java.security.*;
import java.security.Security;
import java.util.*;
import org.bouncycastle.jce.provider.*;
public class BlockChain {
public static ArrayList<Block> blockList = new ArrayList<Block>();
public static int difficulty = 5;
public static Map<String, TransactionOutput> UTXOs = new HashMap<String, TransactionOutput>();
public static Wallet walletA;
public static Wallet walletB;
public static float minimumTransaction = 0.1f;
public static Transaction genesisTransaction;
public static void main(String[] args)
throws NoSuchAlgorithmException, InvalidAlgorithmParameterException, NoSuchProviderException {
// add our blocks to the blockchain ArrayList:
Security.addProvider(
new org.bouncycastle.jce.provider
.BouncyCastleProvider()); // Setup Bouncey castle as a Security Provider
// Create wallets:
walletA = new Wallet();
walletB = new Wallet();
Wallet coinbase = new Wallet();
// create genesis transaction, which sends 100 NoobCoin to walletA:
genesisTransaction = new Transaction(coinbase.publicKey, walletA.publicKey, 100f, null);
genesisTransaction.generateSignature(
coinbase.privateKey); // manually sign the genesis transaction
genesisTransaction.transactionId = "0"; // manually set the transaction id
genesisTransaction
.getOutputs()
.add(
new TransactionOutput(
genesisTransaction.getReciever(),
genesisTransaction.getAmount(),
genesisTransaction.getTransactionId())); // manually add the Transactions Output
UTXOs.put(
genesisTransaction.getOutputs().get(0).id,
genesisTransaction
.getOutputs()
.get(0)); // its important to store our first transaction in the UTXOs list.
System.out.println("Creating and Mining Genesis block... ");
Block genesis = new Block("0");
genesis.addTransaction(genesisTransaction);
addBlock(genesis);
// testing
Block block1 = new Block(genesis.getHash());
System.out.println("\nWalletA's balance is: " + walletA.getBalance());
System.out.println("\nWalletA is Attempting to send funds (40) to WalletB...");
block1.addTransaction(walletA.sendFunds(walletB.publicKey, 40f));
addBlock(block1);
System.out.println("\nWalletA's balance is: " + walletA.getBalance());
System.out.println("WalletB's balance is: " + walletB.getBalance());
Block block2 = new Block(block1.getHash());
System.out.println("\nWalletA Attempting to send more funds (1000) than it has...");
block2.addTransaction(walletA.sendFunds(walletB.publicKey, 1000f));
addBlock(block2);
System.out.println("\nWalletA's balance is: " + walletA.getBalance());
System.out.println("WalletB's balance is: " + walletB.getBalance());
Block block3 = new Block(block2.getHash());
System.out.println("\nWalletB is Attempting to send funds (20) to WalletA...");
block3.addTransaction(walletB.sendFunds(walletA.publicKey, 20));
System.out.println("\nWalletA's balance is: " + walletA.getBalance());
System.out.println("WalletB's balance is: " + walletB.getBalance());
isChainValid();
}
public static Boolean isChainValid() {
Block currentBlock;
Block previousBlock;
String hashTarget = new String(new char[difficulty]).replace('\0', '0');
HashMap<String, TransactionOutput> tempUTXOs =
new HashMap<
String,
TransactionOutput>(); // a temporary working list of unspent transactions at a given
// block state.
tempUTXOs.put(
genesisTransaction.getOutputs().get(0).id, genesisTransaction.getOutputs().get(0));
// loop through blockchain to check hashes:
for (int i = 1; i < blockList.size(); i++) {
currentBlock = blockList.get(i);
previousBlock = blockList.get(i - 1);
// compare registered hash and calculated hash:
if (!currentBlock.getHash().equals(currentBlock.calculateHash())) {
System.out.println("#Current Hashes not equal");
return false;
}
// compare previous hash and registered previous hash
if (!previousBlock.getHash().equals(currentBlock.getPreviousHash())) {
System.out.println("#Previous Hashes not equal");
return false;
}
// check if hash is solved
if (!currentBlock.getHash().substring(0, difficulty).equals(hashTarget)) {
System.out.println("#This block hasn't been mined");
return false;
}
// loop thru blockchains transactions:
TransactionOutput tempOutput;
for (int t = 0; t < currentBlock.transactions.size(); t++) {
Transaction currentTransaction = currentBlock.transactions.get(t);
if (!currentTransaction.verifiySignature()) {
System.out.println("#Signature on Transaction(" + t + ") is Invalid");
return false;
}
if (currentTransaction.getInputsValue() != currentTransaction.getOutputsValue()) {
System.out.println("#Inputs are note equal to outputs on Transaction(" + t + ")");
return false;
}
for (TransactionInput input : currentTransaction.getInputs()) {
tempOutput = tempUTXOs.get(input.transactionOutputId);
if (tempOutput == null) {
System.out.println("#Referenced input on Transaction(" + t + ") is Missing");
return false;
}
if (input.UTXO.value != tempOutput.value) {
System.out.println("#Referenced input Transaction(" + t + ") value is Invalid");
return false;
}
tempUTXOs.remove(input.transactionOutputId);
}
for (TransactionOutput output : currentTransaction.getOutputs()) {
tempUTXOs.put(output.id, output);
}
if (currentTransaction.getOutputs().get(0).reciepient != currentTransaction.getReciever()) {
System.out.println("#Transaction(" + t + ") output reciepient is not who it should be");
return false;
}
if (currentTransaction.getOutputs().get(1).reciepient != currentTransaction.getSender()) {
System.out.println("#Transaction(" + t + ") output 'change' is not sender.");
return false;
}
}
}
System.out.println("Blockchain is valid");
return true;
}
public static void addBlock(Block newBlock) {
newBlock.mineBlock(difficulty);
blockList.add(newBlock);
}
}