/****************************************************************************** * 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.crypto.HashFunction; import java.math.BigInteger; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.Arrays; import java.util.Collections; import java.util.EnumSet; import java.util.Set; public final class CurrencyMinting { public static final Set<HashFunction> acceptedHashFunctions = Collections.unmodifiableSet(EnumSet.of(HashFunction.SHA256, HashFunction.SHA3, HashFunction.SCRYPT, HashFunction.Keccak25)); public static boolean meetsTarget(long accountId, Currency currency, Attachment.MonetarySystemCurrencyMinting attachment) { byte[] hash = getHash(currency.getAlgorithm(), attachment.getNonce(), attachment.getCurrencyId(), attachment.getUnits(), attachment.getCounter(), accountId); byte[] target = getTarget(currency.getMinDifficulty(), currency.getMaxDifficulty(), attachment.getUnits(), currency.getCurrentSupply() - currency.getReserveSupply(), currency.getMaxSupply() - currency.getReserveSupply()); return meetsTarget(hash, target); } public static boolean meetsTarget(byte[] hash, byte[] target) { for (int i = hash.length - 1; i >= 0; i--) { if ((hash[i] & 0xff) > (target[i] & 0xff)) { return false; } if ((hash[i] & 0xff) < (target[i] & 0xff)) { return true; } } return true; } public static byte[] getHash(byte algorithm, long nonce, long currencyId, long units, long counter, long accountId) { HashFunction hashFunction = HashFunction.getHashFunction(algorithm); return getHash(hashFunction, nonce, currencyId, units, counter, accountId); } public static byte[] getHash(HashFunction hashFunction, long nonce, long currencyId, long units, long counter, long accountId) { ByteBuffer buffer = ByteBuffer.allocate(8 + 8 + 8 + 8 + 8); buffer.order(ByteOrder.LITTLE_ENDIAN); buffer.putLong(nonce); buffer.putLong(currencyId); buffer.putLong(units); buffer.putLong(counter); buffer.putLong(accountId); return hashFunction.hash(buffer.array()); } public static byte[] getTarget(int min, int max, long units, long currentMintableSupply, long totalMintableSupply) { return getTarget(getNumericTarget(min, max, units, currentMintableSupply, totalMintableSupply)); } public static byte[] getTarget(BigInteger numericTarget) { byte[] targetRowBytes = numericTarget.toByteArray(); if (targetRowBytes.length == 32) { return reverse(targetRowBytes); } byte[] targetBytes = new byte[32]; Arrays.fill(targetBytes, 0, 32 - targetRowBytes.length, (byte) 0); System.arraycopy(targetRowBytes, 0, targetBytes, 32 - targetRowBytes.length, targetRowBytes.length); return reverse(targetBytes); } public static BigInteger getNumericTarget(Currency currency, long units) { return getNumericTarget(currency.getMinDifficulty(), currency.getMaxDifficulty(), units, currency.getCurrentSupply() - currency.getReserveSupply(), currency.getMaxSupply() - currency.getReserveSupply()); } public static BigInteger getNumericTarget(int min, int max, long units, long currentMintableSupply, long totalMintableSupply) { if (min < 1 || max > 255) { throw new IllegalArgumentException(String.format("Min: %d, Max: %d, allowed range is 1 to 255", min, max)); } int exp = (int)(256 - min - ((max - min) * currentMintableSupply) / totalMintableSupply); return BigInteger.valueOf(2).pow(exp).subtract(BigInteger.ONE).divide(BigInteger.valueOf(units)); } private static byte[] reverse(byte[] b) { for(int i=0; i < b.length/2; i++) { byte temp = b[i]; b[i] = b[b.length - i - 1]; b[b.length - i - 1] = temp; } return b; } private CurrencyMinting() {} // never }