package qora.transaction;
import java.math.BigDecimal;
import java.math.MathContext;
import java.util.Arrays;
import java.util.List;
import org.json.simple.JSONObject;
import database.DBSet;
import qora.account.Account;
import qora.crypto.Base58;
import settings.Settings;
public abstract class Transaction {
//VALIDATION CODE
public static final int VALIDATE_OKE = 1;
public static final int INVALID_ADDRESS = 2;
public static final int NEGATIVE_AMOUNT = 3;
public static final int NEGATIVE_FEE = 4;
public static final int NO_BALANCE = 5;
public static final int INVALID_REFERENCE = 6;
public static final int INVALID_NAME_LENGTH = 7;
public static final int INVALID_VALUE_LENGTH = 8;
public static final int NAME_ALREADY_REGISTRED = 9;
public static final int NAME_DOES_NOT_EXIST = 10;
public static final int INVALID_NAME_OWNER = 11;
public static final int NAME_ALREADY_FOR_SALE = 12;
public static final int NAME_NOT_FOR_SALE = 13;
public static final int BUYER_ALREADY_OWNER = 14;
public static final int INVALID_AMOUNT = 15;
public static final int INVALID_SELLER = 16;
public static final int NAME_NOT_LOWER_CASE = 17;
public static final int INVALID_DESCRIPTION_LENGTH = 18;
public static final int INVALID_OPTIONS_LENGTH = 19;
public static final int INVALID_OPTION_LENGTH = 20;
public static final int DUPLICATE_OPTION = 21;
public static final int POLL_ALREADY_CREATED = 22;
public static final int POLL_ALREADY_HAS_VOTES = 23;
public static final int POLL_NO_EXISTS = 24;
public static final int OPTION_NO_EXISTS = 25;
public static final int ALREADY_VOTED_FOR_THAT_OPTION = 26;
public static final int INVALID_DATA_LENGTH = 27;
public static final int INVALID_QUANTITY = 28;
public static final int ASSET_DOES_NOT_EXIST = 29;
public static final int INVALID_RETURN = 30;
public static final int HAVE_EQUALS_WANT = 31;
public static final int ORDER_DOES_NOT_EXIST = 32;
public static final int INVALID_ORDER_CREATOR = 33;
public static final int INVALID_PAYMENTS_LENGTH = 34;
public static final int NEGATIVE_PRICE = 35;
public static final int NOT_YET_RELEASED = 1000;
//TYPES
public static final int GENESIS_TRANSACTION = 1;
public static final int PAYMENT_TRANSACTION = 2;
public static final int REGISTER_NAME_TRANSACTION = 3;
public static final int UPDATE_NAME_TRANSACTION = 4;
public static final int SELL_NAME_TRANSACTION = 5;
public static final int CANCEL_SELL_NAME_TRANSACTION = 6;
public static final int BUY_NAME_TRANSACTION = 7;
public static final int CREATE_POLL_TRANSACTION = 8;
public static final int VOTE_ON_POLL_TRANSACTION = 9;
public static final int ARBITRARY_TRANSACTION = 10;
public static final int ISSUE_ASSET_TRANSACTION = 11;
public static final int TRANSFER_ASSET_TRANSACTION = 12;
public static final int CREATE_ORDER_TRANSACTION = 13;
public static final int CANCEL_ORDER_TRANSACTION = 14;
public static final int MULTI_PAYMENT_TRANSACTION = 15;
//MINIMUM FEE
public static final BigDecimal MINIMUM_FEE = BigDecimal.ONE;
//RELEASES
public static final long VOTING_RELEASE = 1403715600000l;
public static final long ARBITRARY_TRANSACTIONS_RELEASE = 1405702800000l;
//public static final long ASSETS_RELEASE = 1411308000000l;
public static final long ASSETS_RELEASE = 0l;
//PROPERTIES LENGTH
protected static final int TYPE_LENGTH = 4;
public static final int TIMESTAMP_LENGTH = 8;
protected static final int REFERENCE_LENGTH = 64;
protected byte[] reference;
protected BigDecimal fee;
protected int type;
protected byte[] signature;
protected long timestamp;
protected Transaction(int type, BigDecimal fee, long timestamp, byte[] reference, byte[] signature)
{
this.fee = fee;
this.type = type;
this.signature = signature;
this.timestamp = timestamp;
this.reference = reference;
}
//GETTERS/SETTERS
public int getType()
{
return this.type;
}
public long getTimestamp()
{
return this.timestamp;
}
public long getDeadline()
{
//24HOUR DEADLINE TO INCLUDE TRANSACTION IN BLOCK
return this.timestamp + (1000*60*60*24);
}
public BigDecimal getFee()
{
return this.fee;
}
public byte[] getSignature()
{
return this.signature;
}
public byte[] getReference()
{
return this.reference;
}
public BigDecimal feePerByte()
{
return this.fee.divide(new BigDecimal(this.getDataLength()), MathContext.DECIMAL32);
}
public boolean hasMinimumFee()
{
return this.fee.compareTo(MINIMUM_FEE) >= 0;
}
public boolean hasMinimumFeePerByte()
{
BigDecimal minFeePerByte = BigDecimal.ONE.divide(BigDecimal.valueOf(Settings.getInstance().getMaxBytePerFee()), MathContext.DECIMAL32);
return this.feePerByte().compareTo(minFeePerByte) >= 0;
}
//PARSE/CONVERT
@SuppressWarnings("unchecked")
protected JSONObject getJsonBase()
{
JSONObject transaction = new JSONObject();
transaction.put("type", this.type);
transaction.put("fee", this.fee.toPlainString());
transaction.put("timestamp", this.timestamp);
transaction.put("reference", Base58.encode(this.reference));
transaction.put("signature", Base58.encode(this.signature));
transaction.put("confirmations", this.getConfirmations());
return transaction;
}
public abstract JSONObject toJson();
public abstract byte[] toBytes();
public abstract int getDataLength();
//VALIDATE
public abstract boolean isSignatureValid();
public int isValid()
{
return this.isValid(DBSet.getInstance());
}
public abstract int isValid(DBSet db);
//PROCESS/ORPHAN
public void process()
{
this.process(DBSet.getInstance());
}
public abstract void process(DBSet db);
public void orphan()
{
this.orphan(DBSet.getInstance());
}
public abstract void orphan(DBSet db);
//REST
public abstract Account getCreator();
public abstract List<Account> getInvolvedAccounts();
public abstract boolean isInvolved(Account account);
public abstract BigDecimal getAmount(Account account);
@Override
public boolean equals(Object object)
{
if(object instanceof Transaction)
{
Transaction transaction = (Transaction) object;
return Arrays.equals(this.getSignature(), transaction.getSignature());
}
return false;
}
public boolean isConfirmed()
{
return this.isConfirmed(DBSet.getInstance());
}
public boolean isConfirmed(DBSet db)
{
return DBSet.getInstance().getTransactionMap().contains(this);
}
public int getConfirmations()
{
//CHECK IF IN TRANSACTIONDATABASE
if(DBSet.getInstance().getTransactionMap().contains(this))
{
return 0;
}
//CALCULATE CONFIRMATIONS
int lastBlockHeight = DBSet.getInstance().getHeightMap().get(DBSet.getInstance().getBlockMap().getLastBlockSignature());
int transactionBlockHeight = DBSet.getInstance().getHeightMap().get(DBSet.getInstance().getTransactionParentMap().getParent(this.signature));
//RETURN
return 1 + lastBlockHeight - transactionBlockHeight;
}
}