iota.lib.java/src/main/java/jota/model/Bundle.java
Andreas C. Osowski 81e8a09f94
bugfix
2017-10-24 07:48:49 +02:00

233 lines
7.6 KiB
Java

package jota.model;
import jota.pow.ICurl;
import jota.pow.SpongeFactory;
import jota.utils.Converter;
import org.apache.commons.lang3.StringUtils;
import java.util.ArrayList;
import java.util.List;
/**
* This class represents a Bundle, a set of transactions.
*
* @author pinpong
**/
public class Bundle implements Comparable<Bundle> {
public static String EMPTY_HASH = "999999999999999999999999999999999999999999999999999999999999999999999999999999999";
private List<Transaction> transactions;
private int length;
/**
* Initializes a new instance of the Bundle class without transactions.
*/
public Bundle() {
this(new ArrayList<Transaction>(), 0);
}
/**
* Initializes a new instance of the Bundle class.
*/
public Bundle(List<Transaction> transactions, int length) {
this.transactions = transactions;
this.length = length;
}
/**
* Gets the transactions
*
* @return transactions The transactions.
*/
public List<Transaction> getTransactions() {
return transactions;
}
/**
* Gets the length of the bundle
*
* @return length The length.
*/
public int getLength() {
return length;
}
/**
* Sets the length of the bundle
*
* @param length The length.
*/
public void setLength(int length) {
this.length = length;
}
/**
* Adds a bundle entry.
*
* @param signatureMessageLength Length of the signature message.
* @param address The address.
* @param value The value.
* @param tag The tag.
* @param timestamp The timestamp.
*/
public void addEntry(int signatureMessageLength, String address, long value, String tag, long timestamp) {
if (getTransactions() == null) {
this.transactions = new ArrayList<>(getTransactions());
}
for (int i = 0; i < signatureMessageLength; i++) {
Transaction trx = new Transaction(address, i == 0 ? value : 0, tag, timestamp);
transactions.add(trx);
}
}
/**
* Finalizes the bundle using the specified curl implementation,
*
* @param customCurl The custom curl.
*/
public void finalize(ICurl customCurl) {
ICurl curl;
int[] normalizedBundleValue;
int[] hash = new int[243];
int[] obsoleteTagTrits = new int[81];
String hashInTrytes;
boolean valid = true;
curl = customCurl == null ? SpongeFactory.create(SpongeFactory.Mode.KERL) : customCurl;
do {
curl.reset();
for (int i = 0; i < this.getTransactions().size(); i++) {
int[] valueTrits = Converter.trits(this.getTransactions().get(i).getValue(), 81);
int[] timestampTrits = Converter.trits(this.getTransactions().get(i).getTimestamp(), 27);
this.getTransactions().get(i).setCurrentIndex(i);
int[] currentIndexTrits = Converter.trits(this.getTransactions().get(i).getCurrentIndex(), 27);
this.getTransactions().get(i).setLastIndex(this.getTransactions().size() - 1);
int[] lastIndexTrits = Converter.trits(this.getTransactions().get(i).getLastIndex(), 27);
int[] t = Converter.trits(this.getTransactions().get(i).getAddress() + Converter.trytes(valueTrits) + this.getTransactions().get(i).getObsoleteTag() + Converter.trytes(timestampTrits) + Converter.trytes(currentIndexTrits) + Converter.trytes(lastIndexTrits));
curl.absorb(t, 0, t.length);
}
curl.squeeze(hash, 0, hash.length);
hashInTrytes = Converter.trytes(hash);
normalizedBundleValue = normalizedBundle(hashInTrytes);
boolean foundValue = false;
for (int i = 0; i < normalizedBundleValue.length; i++) {
if (normalizedBundleValue[i] == 13) {
foundValue = true;
obsoleteTagTrits = Converter.trits(this.getTransactions().get(0).getObsoleteTag());
Converter.increment(obsoleteTagTrits, 81);
this.getTransactions().get(0).setObsoleteTag(Converter.trytes(obsoleteTagTrits));
}
}
valid = !foundValue;
} while (!valid);
for (int i = 0; i < this.getTransactions().size(); i++) {
this.getTransactions().get(i).setBundle(hashInTrytes);
}
}
/**
* Adds the trytes.
*
* @param signatureFragments The signature fragments.
*/
public void addTrytes(List<String> signatureFragments) {
String emptySignatureFragment = "";
String emptyHash = EMPTY_HASH;
long emptyTimestamp = 999999999l;
emptySignatureFragment = StringUtils.rightPad(emptySignatureFragment, 2187, '9');
for (int i = 0; i < this.getTransactions().size(); i++) {
// Fill empty signatureMessageFragment
this.getTransactions().get(i).setSignatureFragments((signatureFragments.size() <= i || signatureFragments.get(i).isEmpty()) ? emptySignatureFragment : signatureFragments.get(i));
// Fill empty trunkTransaction
this.getTransactions().get(i).setTrunkTransaction(emptyHash);
// Fill empty branchTransaction
this.getTransactions().get(i).setBranchTransaction(emptyHash);
this.getTransactions().get(i).setAttachmentTimestamp(emptyTimestamp);
this.getTransactions().get(i).setAttachmentTimestampLowerBound(emptyTimestamp);
this.getTransactions().get(i).setAttachmentTimestampUpperBound(emptyTimestamp);
// Fill empty nonce
this.getTransactions().get(i).setNonce(StringUtils.rightPad("", 27, "9"));
}
}
/**
* Normalized the bundle.
*
* @param bundleHash The bundle hash.
* @return normalizedBundle A normalized bundle hash.
*/
public int[] normalizedBundle(String bundleHash) {
int[] normalizedBundle = new int[81];
for (int i = 0; i < 3; i++) {
long sum = 0;
for (int j = 0; j < 27; j++) {
sum += (normalizedBundle[i * 27 + j] = Converter.value(Converter.tritsString("" + bundleHash.charAt(i * 27 + j))));
}
if (sum >= 0) {
while (sum-- > 0) {
for (int j = 0; j < 27; j++) {
if (normalizedBundle[i * 27 + j] > -13) {
normalizedBundle[i * 27 + j]--;
break;
}
}
}
} else {
while (sum++ < 0) {
for (int j = 0; j < 27; j++) {
if (normalizedBundle[i * 27 + j] < 13) {
normalizedBundle[i * 27 + j]++;
break;
}
}
}
}
}
return normalizedBundle;
}
/**
* Compares the current object with another object of the same type.
*
* @param o An object to compare with this object.
* @return A value that indicates the relative order of the objects being compared. The return value has the following meanings: Value Meaning Less than zero This object is less than the <paramref name="other" /> parameter.Zero This object is equal to <paramref name="other" />. Greater than zero This object is greater than <paramref name="other" />.
*/
@Override
public int compareTo(Bundle o) {
return Long.compare(this.getTransactions().get(0).getAttachmentTimestamp(), o.getTransactions().get(0).getAttachmentTimestamp());
}
}