/****************************************************************************** * 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; import nxt.NxtException.ValidationException; import nxt.util.Convert; import org.json.simple.JSONArray; import org.json.simple.JSONObject; import java.nio.ByteBuffer; import java.util.Arrays; /** * Class for handling phasing parameters shared between {@link Appendix.Phasing} and {@link AccountRestrictions.PhasingOnly} */ public final class PhasingParams { private final long quorum; private final long[] whitelist; private final VoteWeighting voteWeighting; PhasingParams(ByteBuffer buffer) { byte votingModel = buffer.get(); quorum = buffer.getLong(); long minBalance = buffer.getLong(); byte whitelistSize = buffer.get(); if (whitelistSize > 0) { whitelist = new long[whitelistSize]; for (int i = 0; i < whitelistSize; i++) { whitelist[i] = buffer.getLong(); } } else { whitelist = Convert.EMPTY_LONG; } long holdingId = buffer.getLong(); byte minBalanceModel = buffer.get(); voteWeighting = new VoteWeighting(votingModel, holdingId, minBalance, minBalanceModel); } PhasingParams(JSONObject attachmentData) { quorum = Convert.parseLong(attachmentData.get("phasingQuorum")); long minBalance = Convert.parseLong(attachmentData.get("phasingMinBalance")); byte votingModel = ((Long) attachmentData.get("phasingVotingModel")).byteValue(); long holdingId = Convert.parseUnsignedLong((String) attachmentData.get("phasingHolding")); JSONArray whitelistJson = (JSONArray) (attachmentData.get("phasingWhitelist")); if (whitelistJson != null && whitelistJson.size() > 0) { whitelist = new long[whitelistJson.size()]; for (int i = 0; i < whitelist.length; i++) { whitelist[i] = Convert.parseUnsignedLong((String) whitelistJson.get(i)); } } else { whitelist = Convert.EMPTY_LONG; } byte minBalanceModel = ((Long) attachmentData.get("phasingMinBalanceModel")).byteValue(); voteWeighting = new VoteWeighting(votingModel, holdingId, minBalance, minBalanceModel); } public PhasingParams(byte votingModel, long holdingId, long quorum, long minBalance, byte minBalanceModel, long[] whitelist) { this.quorum = quorum; this.whitelist = Convert.nullToEmpty(whitelist); if (this.whitelist.length > 0) { Arrays.sort(this.whitelist); } voteWeighting = new VoteWeighting(votingModel, holdingId, minBalance, minBalanceModel); } int getMySize() { return 1 + 8 + 8 + 1 + 8 * whitelist.length + 8 + 1; } void putMyBytes(ByteBuffer buffer) { buffer.put(voteWeighting.getVotingModel().getCode()); buffer.putLong(quorum); buffer.putLong(voteWeighting.getMinBalance()); buffer.put((byte) whitelist.length); for (long account : whitelist) { buffer.putLong(account); } buffer.putLong(voteWeighting.getHoldingId()); buffer.put(voteWeighting.getMinBalanceModel().getCode()); } void putMyJSON(JSONObject json) { json.put("phasingQuorum", quorum); json.put("phasingMinBalance", voteWeighting.getMinBalance()); json.put("phasingVotingModel", voteWeighting.getVotingModel().getCode()); json.put("phasingHolding", Long.toUnsignedString(voteWeighting.getHoldingId())); json.put("phasingMinBalanceModel", voteWeighting.getMinBalanceModel().getCode()); if (whitelist.length > 0) { JSONArray whitelistJson = new JSONArray(); for (long accountId : whitelist) { whitelistJson.add(Long.toUnsignedString(accountId)); } json.put("phasingWhitelist", whitelistJson); } } void validate() throws ValidationException { if (whitelist.length > Constants.MAX_PHASING_WHITELIST_SIZE) { throw new NxtException.NotValidException("Whitelist is too big"); } long previousAccountId = 0; for (long accountId : whitelist) { if (accountId == 0) { throw new NxtException.NotValidException("Invalid accountId 0 in whitelist"); } if (previousAccountId != 0 && accountId < previousAccountId) { throw new NxtException.NotValidException("Whitelist not sorted " + Arrays.toString(whitelist)); } if (accountId == previousAccountId) { throw new NxtException.NotValidException("Duplicate accountId " + Long.toUnsignedString(accountId) + " in whitelist"); } previousAccountId = accountId; } if (quorum <= 0 && voteWeighting.getVotingModel() != VoteWeighting.VotingModel.NONE) { throw new NxtException.NotValidException("quorum <= 0"); } if (voteWeighting.getVotingModel() == VoteWeighting.VotingModel.NONE) { if (quorum != 0) { throw new NxtException.NotValidException("Quorum must be 0 for no-voting phased transaction"); } if (whitelist.length != 0) { throw new NxtException.NotValidException("No whitelist needed for no-voting phased transaction"); } } if (voteWeighting.getVotingModel() == VoteWeighting.VotingModel.ACCOUNT && whitelist.length > 0 && quorum > whitelist.length) { throw new NxtException.NotValidException("Quorum of " + quorum + " cannot be achieved in by-account voting with whitelist of length " + whitelist.length); } voteWeighting.validate(); } public long getQuorum() { return quorum; } public long[] getWhitelist() { return whitelist; } public VoteWeighting getVoteWeighting() { return voteWeighting; } @Override public boolean equals(Object obj) { if (!(obj instanceof PhasingParams)) { return false; } PhasingParams other = (PhasingParams)obj; return other.quorum == this.quorum && other.voteWeighting.equals(this.voteWeighting) && Arrays.equals(other.whitelist, this.whitelist); } @Override public int hashCode() { int hashCode = 17; hashCode = 31 * hashCode + Long.hashCode(quorum); for (long voter : whitelist) { hashCode = 31 * hashCode + Long.hashCode(voter); } hashCode = 31 * hashCode + voteWeighting.hashCode(); return hashCode; } @Override public String toString() { JSONObject resultJson = new JSONObject(); putMyJSON(resultJson); return resultJson.toJSONString(); } }