-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathWallet.java
138 lines (111 loc) · 5.75 KB
/
Wallet.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.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.*;
import java.util.Base64;
public class Wallet {
private PrivateKey privateKey; // Private key associated with the wallet
public PublicKey publicKey; // Public key associated with the wallet
public Peer peer; // The id of the peer that has this wallet
public HashMap<String, TransactionOutput> UTXOs; // Unspent txs available for this wallet to spend
public Wallet(Peer p) {
peer = p;
generateKeyPair();
UTXOs = new HashMap<String, TransactionOutput>(); // Only UTXOs owned by this wallet
}
// This method calculates the balance of this wallet while
// also adding incoming UTXOs of this wallet to UTXO list
public int getBalance() {
int total = 0;
for (Map.Entry<String, TransactionOutput> item : peer.blockchain.UTXOs.entrySet()) {
TransactionOutput UTXO = item.getValue();
if (UTXO.belongsTo(publicKey)) { // If output belongs to me (if coins belong to me)
UTXOs.put(UTXO.id, UTXO); // Add it to our list of unspent transactions
total += UTXO.value;
}
}
return total;
}
// method to sign a message using the private key
public byte[] sign(String message) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
Signature signature = Signature.getInstance("SHA256withRSA");
signature.initSign(this.privateKey);
signature.update(message.getBytes());
return signature.sign();
}
// method to verify a message using the signature and the public key
public boolean verify(PublicKey publicKey, String data, byte[] signature) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
Signature s = Signature.getInstance("SHA256withRSA");
s.initVerify(publicKey);
s.update(data.getBytes());
return s.verify(signature);
}
// This method arranges Tx inputs and outputs in the Tx so that the amount will
// be sent to the receiver and the change will be sent back to the sender
public void arrangeFunds(PublicKey recipient, int value, List<TransactionInput> inputs, List<TransactionOutput> outputs, String txid) throws NoSuchAlgorithmException {
// Find enough transaction inputs to afford the value
int total = 0;
for (Map.Entry<String, TransactionOutput> item : UTXOs.entrySet()) {
TransactionOutput UTXO = item.getValue();
total += UTXO.value;
inputs.add(new TransactionInput(UTXO.id));
if (total >= value) break; // When we have enough of the UTXOs to afford value, stop
}
int creatorOver = total + value; // because we use negative value with the transcaction so no need any extra extraction
outputs.add(new TransactionOutput(recipient, value, txid)); //send value to recipient
outputs.add(new TransactionOutput(publicKey, creatorOver, txid)); //send the left over 'change' back to sender
// If a UTXO is spent with this tx, remove it from UTXOs list
for (TransactionInput input : inputs) {
UTXOs.remove(input.transactionOutputId);
}
}
// This method arranges Tx outputs for a coinbase transaction
// Just put a tx output and that's it.
public void arrangeFunds(PublicKey recipient, int value, List<TransactionOutput> outputs, String txid) throws NoSuchAlgorithmException {
outputs.add(new TransactionOutput(recipient, value, txid)); //send value to recipient
}
///////////////////////////////// CRYPTOGRAPHIC OPERATIONS /////////////////////////////////
// Generates a public-private RSA key pair for this wallet
public void generateKeyPair() {
try {
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
keyGen.initialize(2048);
KeyPair pair = keyGen.generateKeyPair();
this.privateKey = pair.getPrivate();
this.publicKey = pair.getPublic();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
// Return the private key of this wallet
public String getPrivateKeyString() {
return Base64.getEncoder().encodeToString(this.privateKey.getEncoded());
}
// Return the public key of this wallet
public String getPublicKeyString() {
return Base64.getEncoder().encodeToString(this.publicKey.getEncoded());
}
// STATIC UTILITY FUNCTIONS REGARDING KEY-STRING TRANSFORMATIONS
// A utility function to turn a private key into a string
public static String getStringFromPrivateKey(PrivateKey pk) {
return Base64.getEncoder().encodeToString(pk.getEncoded());
}
// A utility function to turn a public key into a string
public static String getStringFromPublicKey(PublicKey pk) {
return Base64.getEncoder().encodeToString(pk.getEncoded());
}
// A utility function to turn a string back into a private key
public static PrivateKey getPrivateKeyFromString(String key) throws Exception {
byte[] byteKey = Base64.getDecoder().decode(key);
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(byteKey);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
return keyFactory.generatePrivate(keySpec);
}
// A utility function to turn a string back into a public key
public static PublicKey getPublicKeyFromString(String key) throws Exception {
byte[] byteKey = Base64.getDecoder().decode(key);
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(byteKey);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
return keyFactory.generatePublic(keySpec);
}
}