/****************************************************************************** * 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.http; import nxt.Account; import nxt.Attachment; import nxt.Constants; import nxt.CurrencyType; import nxt.NxtException; import nxt.util.Convert; import org.json.simple.JSONStreamAware; import javax.servlet.http.HttpServletRequest; /** * Issue a currency on the NXT blockchain * <p> * A currency is the basic block of the NXT Monetary System it can be exchanged with NXT, transferred between accounts, * minted using proof of work methods, reserved and claimed as a crowd funding tool. * <p> * Pass the following parameters in order to issue a currency * <ul> * <li>name - unique identifier of the currency composed of between 3 to 10 latin alphabetic symbols and numbers, name must be * no shorter than code. name and code are mutually unique. * <li>code - unique 3 to 5 letter currency trading symbol composed of upper case latin letters * <li>description - free text description of the currency limited to 1000 characters * <li>type - a numeric value representing a bit vector modeling the currency capabilities (see below) * <li>ruleset - for future use, always set to 0 * <li>maxSupply - the total number of currency units which can be created * <li>initialSupply - the number of currency units created when the currency is issued (pre-mine) * <li>decimals - currency units are divisible to this number of decimals * <li>issuanceHeight - the blockchain height at which the currency would become active * For {@link nxt.CurrencyType#RESERVABLE} currency * <li>minReservePerUnitNQT - the minimum NXT value per unit to allow the currency to become active * For {@link nxt.CurrencyType#RESERVABLE} currency * <li>reserveSupply - the number of units that will be distributed to founders when currency becomes active (less initialSupply) * For {@link nxt.CurrencyType#RESERVABLE} currency * <li>minDifficulty - for mint-able currency, the exponent of the initial difficulty. * For {@link nxt.CurrencyType#MINTABLE} currency * <li>maxDifficulty - for mint-able currency, the exponent of the final difficulty. * For {@link nxt.CurrencyType#MINTABLE} currency * <li>algorithm - the hashing {@link nxt.crypto.HashFunction algorithm} used for minting. * For {@link nxt.CurrencyType#MINTABLE} currency * </ul> * <p> * Constraints * <ul> * <li>A given currency can not be neither {@link nxt.CurrencyType#EXCHANGEABLE} nor {@link nxt.CurrencyType#CLAIMABLE}.<br> * <li>A {@link nxt.CurrencyType#RESERVABLE} currency becomes active once the blockchain height reaches the currency issuance height.<br> * At this time, if the minReservePerUnitNQT has not been reached the currency issuance is cancelled and * funds are returned to the founders.<br> * Otherwise the currency becomes active and remains active until deleted, provided deletion is possible. * When a {@link nxt.CurrencyType#RESERVABLE} becomes active, in case it is {@link nxt.CurrencyType#CLAIMABLE} the NXT used for * reserving the currency are locked until they are claimed back. * When a {@link nxt.CurrencyType#RESERVABLE} becomes active, in case it is non {@link nxt.CurrencyType#CLAIMABLE} the NXT used for * reserving the currency are sent to the issuer account as crowd funding. * <li>When issuing a {@link nxt.CurrencyType#MINTABLE} currency, the number of units per {@link nxt.http.CurrencyMint} cannot exceed 0.01% of the * total supply. Therefore make sure totalSupply > 10000 or otherwise the currency cannot be minted * <li>difficulty is calculated as follows<br> * difficulty of minting the first unit is based on 2^minDifficulty<br> * difficulty of minting the last unit is based on 2^maxDifficulty<br> * difficulty increases linearly from min to max based on the ratio between the current number of units and the total supply<br> * difficulty increases linearly with the number units minted per {@link nxt.http.CurrencyMint}<br> * </ul> * * @see nxt.CurrencyType * @see nxt.crypto.HashFunction */ public final class IssueCurrency extends CreateTransaction { static final IssueCurrency instance = new IssueCurrency(); private IssueCurrency() { super(new APITag[] {APITag.MS, APITag.CREATE_TRANSACTION}, "name", "code", "description", "type", "initialSupply", "reserveSupply", "maxSupply", "issuanceHeight", "minReservePerUnitNQT", "minDifficulty", "maxDifficulty", "ruleset", "algorithm", "decimals"); } @Override JSONStreamAware processRequest(HttpServletRequest req) throws NxtException { String name = Convert.nullToEmpty(req.getParameter("name")); String code = Convert.nullToEmpty(req.getParameter("code")); String description = Convert.nullToEmpty(req.getParameter("description")); if (name.length() < Constants.MIN_CURRENCY_NAME_LENGTH || name.length() > Constants.MAX_CURRENCY_NAME_LENGTH) { return JSONResponses.INCORRECT_CURRENCY_NAME_LENGTH; } if (code.length() < Constants.MIN_CURRENCY_CODE_LENGTH || code.length() > Constants.MAX_CURRENCY_CODE_LENGTH) { return JSONResponses.INCORRECT_CURRENCY_CODE_LENGTH; } if (description.length() > Constants.MAX_CURRENCY_DESCRIPTION_LENGTH) { return JSONResponses.INCORRECT_CURRENCY_DESCRIPTION_LENGTH; } String normalizedName = name.toLowerCase(); for (int i = 0; i < normalizedName.length(); i++) { if (Constants.ALPHABET.indexOf(normalizedName.charAt(i)) < 0) { return JSONResponses.INCORRECT_CURRENCY_NAME; } } for (int i = 0; i < code.length(); i++) { if (Constants.ALLOWED_CURRENCY_CODE_LETTERS.indexOf(code.charAt(i)) < 0) { return JSONResponses.INCORRECT_CURRENCY_CODE; } } int type = 0; if (Convert.emptyToNull(req.getParameter("type")) == null) { for (CurrencyType currencyType : CurrencyType.values()) { if ("1".equals(req.getParameter(currencyType.toString().toLowerCase()))) { type = type | currencyType.getCode(); } } } else { type = ParameterParser.getInt(req, "type", 0, Integer.MAX_VALUE, false); } long maxSupply = ParameterParser.getLong(req, "maxSupply", 1, Constants.MAX_CURRENCY_TOTAL_SUPPLY, false); long reserveSupply = ParameterParser.getLong(req, "reserveSupply", 0, maxSupply, false); long initialSupply = ParameterParser.getLong(req, "initialSupply", 0, maxSupply, false); int issuanceHeight = ParameterParser.getInt(req, "issuanceHeight", 0, Integer.MAX_VALUE, false); long minReservePerUnit = ParameterParser.getLong(req, "minReservePerUnitNQT", 1, Constants.MAX_BALANCE_NQT, false); int minDifficulty = ParameterParser.getInt(req, "minDifficulty", 1, 255, false); int maxDifficulty = ParameterParser.getInt(req, "maxDifficulty", 1, 255, false); byte ruleset = ParameterParser.getByte(req, "ruleset", (byte)0, Byte.MAX_VALUE, false); byte algorithm = ParameterParser.getByte(req, "algorithm", (byte)0, Byte.MAX_VALUE, false); byte decimals = ParameterParser.getByte(req, "decimals", (byte)0, Byte.MAX_VALUE, false); Account account = ParameterParser.getSenderAccount(req); Attachment attachment = new Attachment.MonetarySystemCurrencyIssuance(name, code, description, (byte)type, initialSupply, reserveSupply, maxSupply, issuanceHeight, minReservePerUnit, minDifficulty, maxDifficulty, ruleset, algorithm, decimals); return createTransaction(req, account, attachment); } }