-
Notifications
You must be signed in to change notification settings - Fork 0
/
Blockchain.java
138 lines (115 loc) · 6.11 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
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SignatureException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
public class Blockchain {
public Peer peer; //Referring to the maintainer of this copy of the blockchain
public ArrayList<Block> blocks; //Blocks in the blockchain
public ArrayList<Transaction> mempool; //All the txs arrived. https://www.btcturk.com/bilgi-platformu/bitcoin-mempool-nedir/
public HashMap<String, TransactionOutput> UTXOs; //Unspent tx outputs
public int difficulty = 5; //Current difficulty of the blockchain (later can be changed as in btc)
public int MININGREWARD = 2 ^ 10; // Mining reward will be 1024 at the beginning
public final int HALVINGPERIOD = 10; // After every 10 blocks added, mining reward will be halved
//Question: How many coins are we going to get eventually then?
public Blockchain(Peer p) throws InvalidKeyException, NoSuchAlgorithmException, SignatureException {
peer = p;
blocks = new ArrayList<Block>();
mempool = new ArrayList<>();
UTXOs = new HashMap<String, TransactionOutput>();
createGenesisBlock();
}
// Add transactions to the blockchain
public void addBlock(Block newBlock) {
newBlock.mineBlock();
blocks.add(newBlock);
if (blocks.size() % HALVINGPERIOD == 0) MININGREWARD /= 2;
}
public Block getLastBlock() {
return blocks.get(blocks.size() - 1);
}
public void createGenesisBlock() throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
// Create a coinbase transaction for the block reward. For simplicity, we'll
// pretend this peer's wallet address is the recipient.
// Since this is a simulation, we can use this peer's public key.
// In reality, this would be a more complex transaction.
Transaction coinbase = new Transaction(this.peer.wallet.publicKey, this.MININGREWARD, this.peer);
// Manually set a transaction ID
coinbase.transactionId = "coinbase";
// Create a list of transactions and add the coinbase transaction
List<Transaction> transactions = new ArrayList<>();
transactions.add(coinbase);
//this.blockchain = new Blockchain(this);
// Create the genesis block. We'll set previousHash as "0" because there's no previous block.
Block genesisBlock = new Block(this.peer.wallet.publicKey, this);
// Add it to the blockchain
blocks.add(genesisBlock);
// Add the output transaction to UTXOs
TransactionOutput output = new TransactionOutput(coinbase.recipient, coinbase.value, coinbase.transactionId);
this.UTXOs.put(output.id, output);
System.out.println("Peer" + this.peer.id + "Genesis block created.");
}
// Check if the blockchain is valid
public Boolean isChainValid() throws InvalidKeyException, NoSuchAlgorithmException, SignatureException {
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.putAll(UTXOs);
// Loop through blockchain to check hashes:
for (int i = 1; i < blocks.size(); i++) {
currentBlock = blocks.get(i);
previousBlock = blocks.get(i - 1);
// Compare registered hash and calculated hash:
if (!currentBlock.hash.equals(currentBlock.calculateHash())) {
System.out.println("#Current Hashes not equal");
return false;
}
// Compare previous hash and registered previous hash
if (!previousBlock.hash.equals(currentBlock.previousHash)) {
System.out.println("#Previous Hashes not equal");
return false;
}
// Check if hash is solved
if (!currentBlock.hash.substring(0, difficulty).equals(hashTarget)) {
System.out.println("#This block hasn't been mined");
return false;
}
// Loop through blockchains transactions:
TransactionOutput tempOutput;
for (int t = 0; t < currentBlock.transactions.size(); t++) {
Transaction currentTransaction = currentBlock.transactions.get(t);
if (currentTransaction.getInputsValue() != currentTransaction.getOutputsValue()) {
System.out.println("#Inputs are note equal to outputs on transaction(" + t + ")");
return false;
}
for (TransactionInput input : currentTransaction.inputs) {
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.outputs) {
tempUTXOs.put(output.id, output);
}
if (currentTransaction.outputs.get(0).recipient != currentTransaction.recipient) {
System.out.println("#Transaction(" + t + ") output recipient is not who it should be");
return false;
}
if (currentTransaction.outputs.get(1).recipient != currentTransaction.sender) {
System.out.println("#Transaction(" + t + ") output 'change' is not sender.");
return false;
}
}
}
System.out.println("Blockchain is valid");
return true;
}
}