/******************************************************************************
* Copyright © 2013-2016 The Nxt Core Developers. *
* *
* See the AUTHORS.txt, DEVELOPER-AGREEMENT.txt and LICENSE.txt files at *
* the top-level directory of this distribution for the individual copyright *
* holder information and the developer policies on copyright and licensing. *
* *
* Unless otherwise agreed in a custom licensing agreement, no part of the *
* Nxt software, including this file, may be copied, modified, propagated, *
* or distributed except according to the terms contained in the LICENSE.txt *
* file. *
* *
* Removal or modification of this copyright notice is prohibited. *
* *
******************************************************************************/
package nxt;
public final class VoteWeighting {
public enum VotingModel {
NONE(-1) {
@Override
public final boolean acceptsVotes() {
return false;
}
@Override
public final long calcWeight(VoteWeighting voteWeighting, long voterId, int height) {
throw new UnsupportedOperationException("No voting possible for VotingModel.NONE");
}
@Override
public final MinBalanceModel getMinBalanceModel() {
return MinBalanceModel.NONE;
}
},
ACCOUNT(0) {
@Override
public final long calcWeight(VoteWeighting voteWeighting, long voterId, int height) {
return (voteWeighting.minBalance == 0 || voteWeighting.minBalanceModel.getBalance(voteWeighting, voterId, height) >= voteWeighting.minBalance) ? 1 : 0;
}
@Override
public final MinBalanceModel getMinBalanceModel() {
return MinBalanceModel.NONE;
}
},
NQT(1) {
@Override
public final long calcWeight(VoteWeighting voteWeighting, long voterId, int height) {
long nqtBalance = Account.getAccount(voterId, height).getBalanceNQT();
return nqtBalance >= voteWeighting.minBalance ? nqtBalance : 0;
}
@Override
public final MinBalanceModel getMinBalanceModel() {
return MinBalanceModel.NQT;
}
},
ASSET(2) {
@Override
public final long calcWeight(VoteWeighting voteWeighting, long voterId, int height) {
long qntBalance = Account.getAssetBalanceQNT(voterId, voteWeighting.holdingId, height);
return qntBalance >= voteWeighting.minBalance ? qntBalance : 0;
}
@Override
public final MinBalanceModel getMinBalanceModel() {
return MinBalanceModel.ASSET;
}
},
CURRENCY(3) {
@Override
public final long calcWeight(VoteWeighting voteWeighting, long voterId, int height) {
long units = Account.getCurrencyUnits(voterId, voteWeighting.holdingId, height);
return units >= voteWeighting.minBalance ? units : 0;
}
@Override
public final MinBalanceModel getMinBalanceModel() {
return MinBalanceModel.CURRENCY;
}
},
TRANSACTION(4) {
@Override
public final boolean acceptsVotes() {
return false;
}
@Override
public final long calcWeight(VoteWeighting voteWeighting, long voterId, int height) {
throw new UnsupportedOperationException("No voting possible for VotingModel.TRANSACTION");
}
@Override
public final MinBalanceModel getMinBalanceModel() {
return MinBalanceModel.NONE;
}
},
HASH(5) {
@Override
public final long calcWeight(VoteWeighting voteWeighting, long voterId, int height) {
return 1;
}
@Override
public final MinBalanceModel getMinBalanceModel() {
return MinBalanceModel.NONE;
}
};
private final byte code;
VotingModel(int code) {
this.code = (byte)code;
}
public byte getCode() {
return code;
}
public abstract long calcWeight(VoteWeighting voteWeighting, long voterId, int height);
public abstract MinBalanceModel getMinBalanceModel();
public boolean acceptsVotes() {
return true;
}
public static VotingModel get(byte code) {
for (VotingModel votingModel : values()) {
if (votingModel.getCode() == code) {
return votingModel;
}
}
throw new IllegalArgumentException("Invalid votingModel " + code);
}
}
public enum MinBalanceModel {
NONE(0) {
@Override
public final long getBalance(VoteWeighting voteWeighting, long voterId, int height) {
throw new UnsupportedOperationException();
}
},
NQT(1) {
@Override
public final long getBalance(VoteWeighting voteWeighting, long voterId, int height) {
return Account.getAccount(voterId, height).getBalanceNQT();
}
},
ASSET(2) {
@Override
public final long getBalance(VoteWeighting voteWeighting, long voterId, int height) {
return Account.getAssetBalanceQNT(voterId, voteWeighting.holdingId, height);
}
},
CURRENCY(3) {
@Override
public final long getBalance(VoteWeighting voteWeighting, long voterId, int height) {
return Account.getCurrencyUnits(voterId, voteWeighting.holdingId, height);
}
};
private final byte code;
MinBalanceModel(int code) {
this.code = (byte)code;
}
public byte getCode() {
return code;
}
public abstract long getBalance(VoteWeighting voteWeighting, long voterId, int height);
public static MinBalanceModel get(byte code) {
for (MinBalanceModel minBalanceModel : values()) {
if (minBalanceModel.getCode() == code) {
return minBalanceModel;
}
}
throw new IllegalArgumentException("Invalid minBalanceModel " + code);
}
}
private final VotingModel votingModel;
private final long holdingId; //either asset id or MS coin id
private final long minBalance;
private final MinBalanceModel minBalanceModel;
public VoteWeighting(byte votingModel, long holdingId, long minBalance, byte minBalanceModel) {
this.votingModel = VotingModel.get(votingModel);
this.holdingId = holdingId;
this.minBalance = minBalance;
this.minBalanceModel = MinBalanceModel.get(minBalanceModel);
}
public VotingModel getVotingModel() {
return votingModel;
}
public long getMinBalance() {
return minBalance;
}
public long getHoldingId() {
return holdingId;
}
public MinBalanceModel getMinBalanceModel() {
return minBalanceModel;
}
public void validate() throws NxtException.ValidationException {
if (votingModel == null) {
throw new NxtException.NotValidException("Invalid voting model");
}
if (minBalanceModel == null) {
throw new NxtException.NotValidException("Invalid min balance model");
}
if ((votingModel == VotingModel.ASSET || votingModel == VotingModel.CURRENCY) && holdingId == 0) {
throw new NxtException.NotValidException("No holdingId provided");
}
if (votingModel == VotingModel.CURRENCY && Currency.getCurrency(holdingId) == null) {
throw new NxtException.NotCurrentlyValidException("Currency " + Long.toUnsignedString(holdingId) + " not found");
}
if (votingModel == VotingModel.ASSET && Asset.getAsset(holdingId) == null) {
throw new NxtException.NotCurrentlyValidException("Asset " + Long.toUnsignedString(holdingId) + " not found");
}
if (minBalance < 0) {
throw new NxtException.NotValidException("Invalid minBalance " + minBalance);
}
if (minBalance > 0) {
if (minBalanceModel == MinBalanceModel.NONE) {
throw new NxtException.NotValidException("Invalid min balance model " + minBalanceModel);
}
if (votingModel.getMinBalanceModel() != MinBalanceModel.NONE && votingModel.getMinBalanceModel() != minBalanceModel) {
throw new NxtException.NotValidException("Invalid min balance model: " + minBalanceModel + " for voting model " + votingModel);
}
if ((minBalanceModel == MinBalanceModel.ASSET || minBalanceModel == MinBalanceModel.CURRENCY) && holdingId == 0) {
throw new NxtException.NotValidException("No holdingId provided");
}
if (minBalanceModel == MinBalanceModel.ASSET && Asset.getAsset(holdingId) == null) {
throw new NxtException.NotCurrentlyValidException("Invalid min balance asset: " + Long.toUnsignedString(holdingId));
}
if (minBalanceModel == MinBalanceModel.CURRENCY && Currency.getCurrency(holdingId) == null) {
throw new NxtException.NotCurrentlyValidException("Invalid min balance currency: " + Long.toUnsignedString(holdingId));
}
}
if (minBalance == 0 && votingModel == VotingModel.ACCOUNT && holdingId != 0) {
throw new NxtException.NotValidException("HoldingId cannot be used in by account voting with no min balance");
}
if ((votingModel == VotingModel.NQT || minBalanceModel == MinBalanceModel.NQT) && holdingId != 0) {
throw new NxtException.NotValidException("HoldingId cannot be used in by balance voting or with min balance in NQT");
}
if ((!votingModel.acceptsVotes() || votingModel == VotingModel.HASH) && (holdingId != 0 || minBalance != 0 || minBalanceModel != MinBalanceModel.NONE)) {
throw new NxtException.NotValidException("With VotingModel " + votingModel + " no holdingId, minBalance, or minBalanceModel should be specified");
}
}
public boolean isBalanceIndependent() {
return (votingModel == VotingModel.ACCOUNT && minBalance == 0) || !votingModel.acceptsVotes() || votingModel == VotingModel.HASH;
}
public boolean acceptsVotes() {
return votingModel.acceptsVotes();
}
@Override
public boolean equals(Object o) {
if (! (o instanceof VoteWeighting)) {
return false;
}
VoteWeighting other = (VoteWeighting)o;
return other.votingModel == this.votingModel
&& other.minBalanceModel == this.minBalanceModel
&& other.holdingId == this.holdingId
&& other.minBalance == this.minBalance;
}
@Override
public int hashCode() {
int hashCode = 17;
hashCode = 31 * hashCode + Long.hashCode(holdingId);
hashCode = 31 * hashCode + Long.hashCode(minBalance);
hashCode = 31 * hashCode + minBalanceModel.hashCode();
hashCode = 31 * hashCode + votingModel.hashCode();
return hashCode;
}
}