-
Notifications
You must be signed in to change notification settings - Fork 6
Expand file tree
/
Copy pathTransactionUtility.java
More file actions
170 lines (166 loc) · 7.79 KB
/
TransactionUtility.java
File metadata and controls
170 lines (166 loc) · 7.79 KB
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
162
163
164
165
166
167
168
169
170
/*
* Curecoin 2.0.0a Source Code
* Copyright (c) 2015 Curecoin Developers
* Distributed under MIT License
* Requires Apache Commons Library
* Supports Java 1.7+
*/
import java.util.*;
/**
* TransactionUtility simplifies a few basic tasks dealing with transaction parsing and verification.
*/
public class TransactionUtility
{
/**
* Tests whether a transaction is valid. Doesn't test account balances, but tests formatting and signature verification.
*
* @param transaction Transaction String to test
*
* @return boolean Whether the transaction is formatted and signed correctly
*/
public static boolean isTransactionValid(String transaction)
{
System.out.println("Checking transaction: " + transaction);
MerkleAddressUtility merkleAddressUtility = new MerkleAddressUtility();
try
{
String[] transactionParts = transaction.split(";");
if (transactionParts.length % 2 != 0 || transactionParts.length < 6)
{
return false; //Each address should line up with an output, and no explicit transaction is possible with fewer than six parts (see above)
}
for (int j = 0; j < transactionParts.length - 2; j+=2) //Last two parts are signatureData and signatureIndex,respectively
{
if (!merkleAddressUtility.isAddressFormattedCorrectly(transactionParts[j]))
{
return false; //Address in transaction is misformatted
}
}
long inputAmount = Long.parseLong(transactionParts[1]);
long outputAmount = 0L;
for (int j = 3; j < transactionParts.length - 2; j+=2) //Element 3 (4th element) and each subsequent odd-numbered index up to transactionParts should be an output amount.
{
if (Long.parseLong(transactionParts[j]) <= 0)
{
return false;
}
outputAmount += Long.parseLong(transactionParts[j]);
}
if (inputAmount - outputAmount < 0)
{
return false; //Coins can't be created out of thin air!
}
String transactionData = "";
for (int j = 0; j < transactionParts.length - 2; j++)
{
transactionData += transactionParts[j] + ";";
}
transactionData = transactionData.substring(0, transactionData.length() - 1);
if (!merkleAddressUtility.verifyMerkleSignature(transactionData, transactionParts[transactionParts.length - 2], transactionParts[0], Long.parseLong(transactionParts[transactionParts.length - 1])))
{
return false; //Siganture doesn't match
}
} catch (Exception e) //Likely an error parsing a Long or performing some String manipulation task. Maybe array bounds exceptions.
{
return false;
}
return true;
}
/**
* Transactions on the Curecoin 2.0 network from the same address must occur in a certain order, dictated by the signature index.
* As such, We want to order all transactions from the same address in order.
* The order of transactions from different addresses does not matter--coins will not be received and spent in the same transaction.
*
* @param transactionsToSort ArrayList<String> containing String representations of all the addresses to sort
*
* @return ArrayList<String> All of the transactions sorted in order for block inclusion, with any self-invalidating transactions removed.
*/
public static ArrayList<String> sortTransactionsBySignatureIndex(ArrayList<String> transactionsToSort)
{
for (int i = 0; i < transactionsToSort.size(); i++)
{
if (!isTransactionValid(transactionsToSort.get(i)))
{
transactionsToSort.remove(i);
i--; //Compensate for changing ArrayList size
}
}
ArrayList<String> sortedTransactions = new ArrayList<String>();
for (int i = 0; i < transactionsToSort.size(); i++)
{
System.out.println("spin1");
if (sortedTransactions.size() == 0)
{
sortedTransactions.add(transactionsToSort.get(0));
}
else
{
String address = transactionsToSort.get(i).split(";")[0];
long index = Long.parseLong(transactionsToSort.get(i).split(";")[transactionsToSort.get(i).split(";").length - 1]);
boolean added = false;
for (int j = 0; j < sortedTransactions.size(); j++)
{
System.out.println("spin2");
if (sortedTransactions.get(j).split(";")[0].equals(address))
{
String[] parts = sortedTransactions.get(j).split(";");
int indexToGrab = parts.length - 1;
String sigIndexToParse = sortedTransactions.get(j).split(";")[indexToGrab];
long existingSigIndex = Long.parseLong(sigIndexToParse);
if (index < existingSigIndex)
{
//Insertion should occur before the currently-studied element
sortedTransactions.add(j, transactionsToSort.get(i));
added = true;
break;
}
else if (index == existingSigIndex)
{
//This should never happen--double-signed transaction. Discard the new one!
j = sortedTransactions.size();
}
}
}
if (!added)
{
sortedTransactions.add(transactionsToSort.get(i));
}
}
}
return sortedTransactions;
}
/**
* Signs a Transaction built with the provided sending address and amount, and destination address(es) and amount(s).
*
* @param privateKey The private key for inputAddress
* @param inputAddress Address to send coins from
* @param inputAmount Total amount to send
* @param outputAddresses Addresses to send coins to
* @param outputAmounts Amounts lined up with addresses to send
* @param signatureIndex The signature index to use
*
* @return String The full transaction, formatted for use in the Curecoin 2.0 network, including the signature and signature index. Returns null if transaction is incorrect for any reason.
*/
public static String signTransaction(String privateKey, String inputAddress, long inputAmount, ArrayList<String> outputAddresses, ArrayList<Long> outputAmounts, long index)
{
if (inputAddress == null || outputAddresses == null || inputAmount <= 0) //Immediate red flags
{
return null;
}
if (outputAddresses.size() != outputAmounts.size()) //Output addresses and amounts go together, and so each ArrayList must be the same size
{
return null;
}
String fullTransaction = inputAddress + ";" + inputAmount; //The start of the Transaction
for (int i = 0; i < outputAddresses.size(); i++) //Didn't bother doing address checks here, as they will be conducted in isTransactionValid()
{
fullTransaction += ";" + outputAddresses.get(i) + ";" + outputAmounts.get(i);
}
fullTransaction += ";" + new MerkleAddressUtility().getMerkleSignature(fullTransaction, privateKey, index, inputAddress) + ";" + index; //Now it's actually the 'full transaction'
if (isTransactionValid(fullTransaction))
{
return fullTransaction;
}
return null;
}
}