-
Notifications
You must be signed in to change notification settings - Fork 0
/
Transaction.java
104 lines (86 loc) · 3.99 KB
/
Transaction.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
import java.lang.reflect.Array;
import java.security.*;
import java.util.ArrayList;
public class Transaction {
public String transactionId; // also the hash of the transaction
public PublicKey sender, recipient; // addresses/public keys
public float value;
public byte[] signature; // to prevent anyone from spending funds in our wallet or tampering with submitted transactions
public ArrayList<TransactionInput> inputs = new ArrayList<TransactionInput>();
public ArrayList<TransactionOutput> outputs = new ArrayList<TransactionOutput>();
private static int sequence = 0; // rough count of how many transactions have been generated
public Transaction(PublicKey from, PublicKey to, float value, ArrayList<TransactionInput> inputs) {
this.sender = from;
this.recipient = to;
this.value = value;
this.inputs = inputs;
}
// This calculates the transaction hash (which will be used as its Id)
private String calculateHash() {
sequence ++; // increase the sequence to avoid 2 identical transactions having the same hash
return StringUtil.applySha256(
StringUtil.getStringFromKey(sender) +
StringUtil.getStringFromKey(recipient) +
Float.toString(value) +
sequence
);
}
// Signs all the data we don't wish to be tampered with.
public void generateSignature(PrivateKey privateKey) {
String data = StringUtil.getStringFromKey(sender) + StringUtil.getStringFromKey(recipient) + Float.toString(value);
signature = StringUtil.applyECDSASig(privateKey, data);
}
// Verifies the data we signed hasn't been tampered with.
public boolean verifySignature() {
String data = StringUtil.getStringFromKey(sender) + StringUtil.getStringFromKey(recipient) + Float.toString(value);
return StringUtil.verifyECDSASig(sender, data, signature);
}
// Returns true if new transaction could be created.
public boolean processTransaction() {
if (verifySignature() == false) {
System.out.println("#Transaction Signature failed to verify");
return false;
}
// gather transaction inputs (Make sure they are unspent):
for (TransactionInput i : inputs) {
i.UTXO = ChaChain.UTXOs.get(i.transactionOutputId);
}
// check if transaction is valid:
if (getInputsValue() < ChaChain.minimumTransaction) {
System.out.println("#Transaction Inputs too small: " + getInputsValue());
return false;
}
// generate transaction outputs:
float leftOver = getInputsValue() - value; // get value of inputs then the left over change
transactionId = calculateHash();
outputs.add(new TransactionOutput(this.recipient, value, transactionId)); // send value to recipient
outputs.add(new TransactionOutput(this.sender, leftOver, transactionId)); // send left-over "change" back to sender
// add outputs to Unspent list
for (TransactionOutput o : outputs) {
ChaChain.UTXOs.put(o.id, o);
}
// remove transaction inputs from UTXO lists as spent:
for (TransactionInput i : inputs) {
if (i.UTXO == null) continue; // if Transaction can't be found skip it
ChaChain.UTXOs.remove(i.UTXO.id);
}
return true;
}
// Returns sum of inputs(UTXOs) values
public float getInputsValue() {
float total = 0;
for (TransactionInput i : inputs) {
if (i.UTXO == null) continue; // if Transaction can't be found skip it
total += i.UTXO.value;
}
return total;
}
// Returns sum of outputs:
public float getOutputsValue() {
float total = 0;
for (TransactionOutput o : outputs) {
total += o.value;
}
return total;
}
}