forked from Qortal/qortal
Rework distributing block reward to legacy qora holders
Fix related unit tests. Fix assetID support in GENESIS transactions.
This commit is contained in:
parent
62e2fd759c
commit
ebc2ee6ea9
@ -16,6 +16,8 @@ public class Asset {
|
|||||||
|
|
||||||
/** Hard-coded asset representing legacy QORA held in old QORA1 blockchain. */
|
/** Hard-coded asset representing legacy QORA held in old QORA1 blockchain. */
|
||||||
public static final long LEGACY_QORA = 1L;
|
public static final long LEGACY_QORA = 1L;
|
||||||
|
/** Hard-coded asset representing QORT gained from holding legacy QORA. */
|
||||||
|
public static final long QORT_FROM_QORA = 2L;
|
||||||
|
|
||||||
// Other useful constants
|
// Other useful constants
|
||||||
|
|
||||||
|
@ -8,7 +8,9 @@ import java.math.BigInteger;
|
|||||||
import java.math.RoundingMode;
|
import java.math.RoundingMode;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
@ -28,6 +30,7 @@ import org.qora.controller.Controller;
|
|||||||
import org.qora.crypto.Crypto;
|
import org.qora.crypto.Crypto;
|
||||||
import org.qora.data.account.AccountBalanceData;
|
import org.qora.data.account.AccountBalanceData;
|
||||||
import org.qora.data.account.AccountData;
|
import org.qora.data.account.AccountData;
|
||||||
|
import org.qora.data.account.QortFromQoraData;
|
||||||
import org.qora.data.account.RewardShareData;
|
import org.qora.data.account.RewardShareData;
|
||||||
import org.qora.data.at.ATData;
|
import org.qora.data.at.ATData;
|
||||||
import org.qora.data.at.ATStateData;
|
import org.qora.data.at.ATStateData;
|
||||||
@ -37,6 +40,7 @@ import org.qora.data.block.BlockTransactionData;
|
|||||||
import org.qora.data.network.OnlineAccountData;
|
import org.qora.data.network.OnlineAccountData;
|
||||||
import org.qora.data.transaction.TransactionData;
|
import org.qora.data.transaction.TransactionData;
|
||||||
import org.qora.repository.ATRepository;
|
import org.qora.repository.ATRepository;
|
||||||
|
import org.qora.repository.AccountRepository.BalanceOrdering;
|
||||||
import org.qora.repository.DataException;
|
import org.qora.repository.DataException;
|
||||||
import org.qora.repository.Repository;
|
import org.qora.repository.Repository;
|
||||||
import org.qora.repository.TransactionRepository;
|
import org.qora.repository.TransactionRepository;
|
||||||
@ -131,7 +135,6 @@ public class Block {
|
|||||||
final Account mintingAccount;
|
final Account mintingAccount;
|
||||||
final AccountData mintingAccountData;
|
final AccountData mintingAccountData;
|
||||||
final boolean isMinterFounder;
|
final boolean isMinterFounder;
|
||||||
final BigDecimal minterQoraAmount;
|
|
||||||
final int shareBin;
|
final int shareBin;
|
||||||
|
|
||||||
final Account recipientAccount;
|
final Account recipientAccount;
|
||||||
@ -146,12 +149,6 @@ public class Block {
|
|||||||
this.mintingAccount = new PublicKeyAccount(repository, this.rewardShareData.getMinterPublicKey());
|
this.mintingAccount = new PublicKeyAccount(repository, this.rewardShareData.getMinterPublicKey());
|
||||||
this.recipientAccount = new Account(repository, this.rewardShareData.getRecipient());
|
this.recipientAccount = new Account(repository, this.rewardShareData.getRecipient());
|
||||||
|
|
||||||
AccountBalanceData qoraBalanceData = repository.getAccountRepository().getBalance(this.mintingAccount.getAddress(), Asset.LEGACY_QORA);
|
|
||||||
if (qoraBalanceData != null && qoraBalanceData.getBalance() != null && qoraBalanceData.getBalance().compareTo(BigDecimal.ZERO) > 0)
|
|
||||||
this.minterQoraAmount = qoraBalanceData.getBalance();
|
|
||||||
else
|
|
||||||
this.minterQoraAmount = null;
|
|
||||||
|
|
||||||
this.mintingAccountData = repository.getAccountRepository().getAccount(this.mintingAccount.getAddress());
|
this.mintingAccountData = repository.getAccountRepository().getAccount(this.mintingAccount.getAddress());
|
||||||
this.isMinterFounder = Account.isFounder(mintingAccountData.getFlags());
|
this.isMinterFounder = Account.isFounder(mintingAccountData.getFlags());
|
||||||
|
|
||||||
@ -1261,7 +1258,7 @@ public class Block {
|
|||||||
if (reward == null)
|
if (reward == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
distributeByAccountLevel(reward);
|
distributeBlockReward(reward);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void processTransactions() throws DataException {
|
protected void processTransactions() throws DataException {
|
||||||
@ -1344,7 +1341,7 @@ public class Block {
|
|||||||
if (blockFees.compareTo(BigDecimal.ZERO) <= 0)
|
if (blockFees.compareTo(BigDecimal.ZERO) <= 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
distributeByAccountLevel(blockFees);
|
distributeBlockReward(blockFees);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void processAtFeesAndStates() throws DataException {
|
protected void processAtFeesAndStates() throws DataException {
|
||||||
@ -1486,7 +1483,7 @@ public class Block {
|
|||||||
if (reward == null)
|
if (reward == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
distributeByAccountLevel(reward.negate());
|
distributeBlockReward(reward.negate());
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void deductTransactionFees() throws DataException {
|
protected void deductTransactionFees() throws DataException {
|
||||||
@ -1496,7 +1493,7 @@ public class Block {
|
|||||||
if (blockFees.compareTo(BigDecimal.ZERO) <= 0)
|
if (blockFees.compareTo(BigDecimal.ZERO) <= 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
distributeByAccountLevel(blockFees.negate());
|
distributeBlockReward(blockFees.negate());
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void orphanAtFeesAndStates() throws DataException {
|
protected void orphanAtFeesAndStates() throws DataException {
|
||||||
@ -1562,7 +1559,9 @@ public class Block {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void distributeByAccountLevel(BigDecimal totalAmount) throws DataException {
|
protected void distributeBlockReward(BigDecimal totalAmount) throws DataException {
|
||||||
|
LOGGER.trace(() -> String.format("Distributing: %s", totalAmount.toPlainString()));
|
||||||
|
|
||||||
List<ShareByLevel> sharesByLevel = BlockChain.getInstance().getBlockSharesByLevel();
|
List<ShareByLevel> sharesByLevel = BlockChain.getInstance().getBlockSharesByLevel();
|
||||||
List<ExpandedAccount> expandedAccounts = this.getExpandedAccounts();
|
List<ExpandedAccount> expandedAccounts = this.getExpandedAccounts();
|
||||||
|
|
||||||
@ -1593,33 +1592,111 @@ public class Block {
|
|||||||
BigDecimal qoraHoldersAmount = BlockChain.getInstance().getQoraHoldersShare().multiply(totalAmount).setScale(8, RoundingMode.DOWN);
|
BigDecimal qoraHoldersAmount = BlockChain.getInstance().getQoraHoldersShare().multiply(totalAmount).setScale(8, RoundingMode.DOWN);
|
||||||
LOGGER.trace(() -> String.format("Legacy QORA holders share of %s: %s", totalAmount.toPlainString(), qoraHoldersAmount.toPlainString()));
|
LOGGER.trace(() -> String.format("Legacy QORA holders share of %s: %s", totalAmount.toPlainString(), qoraHoldersAmount.toPlainString()));
|
||||||
|
|
||||||
List<ExpandedAccount> qoraHolderAccounts = new ArrayList<>();
|
List<String> assetAddresses = Collections.emptyList();
|
||||||
BigDecimal totalQoraHeld = BigDecimal.ZERO;
|
List<Long> assetIds = Collections.singletonList(Asset.LEGACY_QORA);
|
||||||
for (int i = 0; i < expandedAccounts.size(); ++i) {
|
List<AccountBalanceData> qoraHolders = this.repository.getAccountRepository().getAssetBalances(assetAddresses, assetIds, BalanceOrdering.ASSET_ACCOUNT, true, null, null, null);
|
||||||
ExpandedAccount expandedAccount = expandedAccounts.get(i);
|
|
||||||
if (expandedAccount.minterQoraAmount == null)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
qoraHolderAccounts.add(expandedAccount);
|
// Filter out qoraHolders who have received max QORT due to holding legacy QORA, (ratio from blockchain config)
|
||||||
totalQoraHeld = totalQoraHeld.add(expandedAccount.minterQoraAmount);
|
BigDecimal qoraPerQortReward = BlockChain.getInstance().getQoraPerQortReward();
|
||||||
|
Iterator<AccountBalanceData> qoraHoldersIterator = qoraHolders.iterator();
|
||||||
|
while (qoraHoldersIterator.hasNext()) {
|
||||||
|
AccountBalanceData qoraHolder = qoraHoldersIterator.next();
|
||||||
|
|
||||||
|
Account qoraHolderAccount = new Account(repository, qoraHolder.getAddress());
|
||||||
|
BigDecimal qortFromQora = qoraHolderAccount.getConfirmedBalance(Asset.QORT_FROM_QORA);
|
||||||
|
|
||||||
|
// If we're processing a block, then totalAmount will be positive
|
||||||
|
if (totalAmount.signum() >= 0) {
|
||||||
|
BigDecimal maxQortFromQora = qoraHolder.getBalance().divide(qoraPerQortReward, RoundingMode.DOWN);
|
||||||
|
|
||||||
|
// Disregard qora holders who have already received maximum qort from holding legacy qora
|
||||||
|
if (qortFromQora.compareTo(maxQortFromQora) >= 0)
|
||||||
|
qoraHoldersIterator.remove();
|
||||||
|
} else {
|
||||||
|
// We're orphaning a block
|
||||||
|
// so disregard qora holders whose final block is earlier than this one
|
||||||
|
QortFromQoraData qortFromQoraData = this.repository.getAccountRepository().getQortFromQoraInfo(qoraHolder.getAddress());
|
||||||
|
if (qortFromQoraData == null)
|
||||||
|
throw new IllegalStateException(String.format("Missing QORT-from-QORA data for %s", qoraHolder.getAddress()));
|
||||||
|
|
||||||
|
if (qortFromQoraData.getFinalBlockHeight() != null && qortFromQoraData.getFinalBlockHeight() < this.blockData.getHeight())
|
||||||
|
qoraHoldersIterator.remove();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final BigDecimal finalTotalQoraHeld = totalQoraHeld;
|
BigDecimal totalQoraHeld = BigDecimal.ZERO;
|
||||||
|
for (int i = 0; i < qoraHolders.size(); ++i)
|
||||||
|
totalQoraHeld = totalQoraHeld.add(qoraHolders.get(i).getBalance());
|
||||||
|
|
||||||
|
BigDecimal finalTotalQoraHeld = totalQoraHeld;
|
||||||
LOGGER.trace(() -> String.format("Total legacy QORA held: %s", finalTotalQoraHeld.toPlainString()));
|
LOGGER.trace(() -> String.format("Total legacy QORA held: %s", finalTotalQoraHeld.toPlainString()));
|
||||||
|
|
||||||
for (int h = 0; h < qoraHolderAccounts.size(); ++h) {
|
for (int h = 0; h < qoraHolders.size(); ++h) {
|
||||||
ExpandedAccount expandedAccount = qoraHolderAccounts.get(h);
|
AccountBalanceData qoraHolder = qoraHolders.get(h);
|
||||||
final BigDecimal holderAmount = qoraHoldersAmount.multiply(totalQoraHeld).divide(expandedAccount.minterQoraAmount, RoundingMode.DOWN);
|
|
||||||
LOGGER.trace(() -> String.format("Minter account %s has %s / %s QORA so share: %s",
|
|
||||||
expandedAccount.mintingAccount.getAddress(), expandedAccount.minterQoraAmount, finalTotalQoraHeld, holderAmount.toPlainString()));
|
|
||||||
|
|
||||||
expandedAccount.distribute(holderAmount);
|
final BigDecimal holderReward = qoraHoldersAmount.multiply(totalQoraHeld).divide(qoraHolder.getBalance(), RoundingMode.DOWN).setScale(8, RoundingMode.DOWN);
|
||||||
sharedAmount = sharedAmount.add(holderAmount);
|
LOGGER.trace(() -> String.format("QORA holder %s has %s / %s QORA so share: %s",
|
||||||
|
qoraHolder.getAddress(), qoraHolder.getBalance().toPlainString(), finalTotalQoraHeld, holderReward.toPlainString()));
|
||||||
|
|
||||||
|
Account qoraHolderAccount = new Account(repository, qoraHolder.getAddress());
|
||||||
|
QortFromQoraData qortFromQoraData = this.repository.getAccountRepository().getQortFromQoraInfo(qoraHolder.getAddress());
|
||||||
|
if (qortFromQoraData == null)
|
||||||
|
qortFromQoraData = new QortFromQoraData(qoraHolder.getAddress(), BigDecimal.ZERO.setScale(8), null);
|
||||||
|
|
||||||
|
BigDecimal qortFromQora = holderReward.divide(qoraPerQortReward, RoundingMode.DOWN);
|
||||||
|
|
||||||
|
BigDecimal newQortFromQoraBalance = qoraHolderAccount.getConfirmedBalance(Asset.QORT_FROM_QORA).add(qortFromQora);
|
||||||
|
|
||||||
|
// If processing, make sure we don't overpay
|
||||||
|
if (totalAmount.signum() >= 0) {
|
||||||
|
BigDecimal maxQortFromQora = qoraHolder.getBalance().divide(qoraPerQortReward, RoundingMode.DOWN);
|
||||||
|
|
||||||
|
if (newQortFromQoraBalance.compareTo(maxQortFromQora) >= 0) {
|
||||||
|
// Reduce final QORT-from-QORA payment to match max
|
||||||
|
BigDecimal adjustment = newQortFromQoraBalance.subtract(maxQortFromQora);
|
||||||
|
|
||||||
|
qortFromQora = qortFromQora.subtract(adjustment);
|
||||||
|
newQortFromQoraBalance = newQortFromQoraBalance.subtract(adjustment);
|
||||||
|
|
||||||
|
// This is also qora holders final qort-from-qora block
|
||||||
|
qortFromQoraData.setFinalQortFromQora(qortFromQora);
|
||||||
|
qortFromQoraData.setFinalBlockHeight(this.blockData.getHeight());
|
||||||
|
|
||||||
|
BigDecimal finalQortFromQora = qortFromQora;
|
||||||
|
LOGGER.trace(() -> String.format("QORA holder %s final share %s at height %d",
|
||||||
|
qoraHolder.getAddress(), finalQortFromQora.toPlainString(), this.blockData.getHeight()));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Orphaning
|
||||||
|
if (qortFromQoraData.getFinalBlockHeight() != null) {
|
||||||
|
// Note use of negate() here as qortFromQora will be negative during orphaning,
|
||||||
|
// but final qort-from-qora is stored in repository during processing (and hence positive).
|
||||||
|
BigDecimal adjustment = qortFromQora.subtract(qortFromQoraData.getFinalQortFromQora().negate());
|
||||||
|
|
||||||
|
qortFromQora = qortFromQora.subtract(adjustment);
|
||||||
|
newQortFromQoraBalance = newQortFromQoraBalance.subtract(adjustment);
|
||||||
|
|
||||||
|
qortFromQoraData.setFinalQortFromQora(null);
|
||||||
|
qortFromQoraData.setFinalBlockHeight(null);
|
||||||
|
|
||||||
|
BigDecimal finalQortFromQora = qortFromQora;
|
||||||
|
LOGGER.trace(() -> String.format("QORA holder %s final share %s was at height %d",
|
||||||
|
qoraHolder.getAddress(), finalQortFromQora.toPlainString(), this.blockData.getHeight()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
qoraHolderAccount.setConfirmedBalance(Asset.QORT, qoraHolderAccount.getConfirmedBalance(Asset.QORT).add(qortFromQora));
|
||||||
|
qoraHolderAccount.setConfirmedBalance(Asset.QORT_FROM_QORA, newQortFromQoraBalance);
|
||||||
|
|
||||||
|
this.repository.getAccountRepository().save(qortFromQoraData);
|
||||||
|
|
||||||
|
sharedAmount = sharedAmount.add(holderReward);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Spread remainder across founder accounts
|
// Spread remainder across founder accounts
|
||||||
BigDecimal foundersAmount = totalAmount.subtract(sharedAmount);
|
BigDecimal foundersAmount = totalAmount.subtract(sharedAmount);
|
||||||
LOGGER.debug(String.format("Shared %s of %s, remaining %s to founders", sharedAmount.toPlainString(), totalAmount.toPlainString(), foundersAmount.toPlainString()));
|
BigDecimal finalSharedAmount = sharedAmount;
|
||||||
|
LOGGER.debug(() -> String.format("Shared %s of %s, remaining %s to founders", finalSharedAmount.toPlainString(), totalAmount.toPlainString(), foundersAmount.toPlainString()));
|
||||||
|
|
||||||
List<ExpandedAccount> founderAccounts = expandedAccounts.stream().filter(accountInfo -> accountInfo.isMinterFounder).collect(Collectors.toList());
|
List<ExpandedAccount> founderAccounts = expandedAccounts.stream().filter(accountInfo -> accountInfo.isMinterFounder).collect(Collectors.toList());
|
||||||
if (founderAccounts.isEmpty())
|
if (founderAccounts.isEmpty())
|
||||||
|
@ -109,6 +109,8 @@ public class BlockChain {
|
|||||||
|
|
||||||
/** Share of block reward/fees to legacy QORA coin holders */
|
/** Share of block reward/fees to legacy QORA coin holders */
|
||||||
BigDecimal qoraHoldersShare;
|
BigDecimal qoraHoldersShare;
|
||||||
|
/** How many legacy QORA per 1 QORT of block reward. */
|
||||||
|
BigDecimal qoraPerQortReward;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Number of minted blocks required to reach next level from previous.
|
* Number of minted blocks required to reach next level from previous.
|
||||||
@ -312,6 +314,10 @@ public class BlockChain {
|
|||||||
return this.qoraHoldersShare;
|
return this.qoraHoldersShare;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public BigDecimal getQoraPerQortReward() {
|
||||||
|
return this.qoraPerQortReward;
|
||||||
|
}
|
||||||
|
|
||||||
public int getMinAccountLevelToMint() {
|
public int getMinAccountLevelToMint() {
|
||||||
return this.minAccountLevelToMint;
|
return this.minAccountLevelToMint;
|
||||||
}
|
}
|
||||||
@ -403,6 +409,9 @@ public class BlockChain {
|
|||||||
if (this.qoraHoldersShare == null)
|
if (this.qoraHoldersShare == null)
|
||||||
Settings.throwValidationError("No \"qoraHoldersShare\" entry found in blockchain config");
|
Settings.throwValidationError("No \"qoraHoldersShare\" entry found in blockchain config");
|
||||||
|
|
||||||
|
if (this.qoraPerQortReward == null)
|
||||||
|
Settings.throwValidationError("No \"qoraPerQortReward\" entry found in blockchain config");
|
||||||
|
|
||||||
if (this.blocksNeededByLevel == null)
|
if (this.blocksNeededByLevel == null)
|
||||||
Settings.throwValidationError("No \"blocksNeededByLevel\" entry found in blockchain config");
|
Settings.throwValidationError("No \"blocksNeededByLevel\" entry found in blockchain config");
|
||||||
|
|
||||||
|
@ -360,6 +360,8 @@ public class BlockMinter extends Thread {
|
|||||||
|
|
||||||
// Add to blockchain
|
// Add to blockchain
|
||||||
newBlock.process();
|
newBlock.process();
|
||||||
|
LOGGER.info(String.format("Minted new test block: %d", newBlock.getBlockData().getHeight()));
|
||||||
|
|
||||||
repository.saveChanges();
|
repository.saveChanges();
|
||||||
} finally {
|
} finally {
|
||||||
blockchainLock.unlock();
|
blockchainLock.unlock();
|
||||||
|
52
src/main/java/org/qora/data/account/QortFromQoraData.java
Normal file
52
src/main/java/org/qora/data/account/QortFromQoraData.java
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
package org.qora.data.account;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
|
||||||
|
import javax.xml.bind.annotation.XmlAccessType;
|
||||||
|
import javax.xml.bind.annotation.XmlAccessorType;
|
||||||
|
|
||||||
|
// All properties to be converted to JSON via JAXB
|
||||||
|
@XmlAccessorType(XmlAccessType.FIELD)
|
||||||
|
public class QortFromQoraData {
|
||||||
|
|
||||||
|
// Properties
|
||||||
|
private String address;
|
||||||
|
// Not always present:
|
||||||
|
private BigDecimal finalQortFromQora;
|
||||||
|
private Integer finalBlockHeight;
|
||||||
|
|
||||||
|
// Constructors
|
||||||
|
|
||||||
|
// necessary for JAXB
|
||||||
|
protected QortFromQoraData() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public QortFromQoraData(String address, BigDecimal finalQortFromQora, Integer finalBlockHeight) {
|
||||||
|
this.address = address;
|
||||||
|
this.finalQortFromQora = finalQortFromQora;
|
||||||
|
this.finalBlockHeight = finalBlockHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Getters/Setters
|
||||||
|
|
||||||
|
public String getAddress() {
|
||||||
|
return this.address;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BigDecimal getFinalQortFromQora() {
|
||||||
|
return this.finalQortFromQora;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFinalQortFromQora(BigDecimal finalQortFromQora) {
|
||||||
|
this.finalQortFromQora = finalQortFromQora;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getFinalBlockHeight() {
|
||||||
|
return this.finalBlockHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFinalBlockHeight(Integer finalBlockHeight) {
|
||||||
|
this.finalBlockHeight = finalBlockHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -5,6 +5,7 @@ import java.util.List;
|
|||||||
import org.qora.data.account.AccountBalanceData;
|
import org.qora.data.account.AccountBalanceData;
|
||||||
import org.qora.data.account.AccountData;
|
import org.qora.data.account.AccountData;
|
||||||
import org.qora.data.account.MintingAccountData;
|
import org.qora.data.account.MintingAccountData;
|
||||||
|
import org.qora.data.account.QortFromQoraData;
|
||||||
import org.qora.data.account.RewardShareData;
|
import org.qora.data.account.RewardShareData;
|
||||||
|
|
||||||
public interface AccountRepository {
|
public interface AccountRepository {
|
||||||
@ -138,4 +139,10 @@ public interface AccountRepository {
|
|||||||
/** Delete minting account info, used by BlockMinter, from repository using passed private key. */
|
/** Delete minting account info, used by BlockMinter, from repository using passed private key. */
|
||||||
public int delete(byte[] mintingAccountPrivateKey) throws DataException;
|
public int delete(byte[] mintingAccountPrivateKey) throws DataException;
|
||||||
|
|
||||||
|
// Managing QORT from legacy QORA
|
||||||
|
|
||||||
|
public QortFromQoraData getQortFromQoraInfo(String address) throws DataException;
|
||||||
|
|
||||||
|
public void save(QortFromQoraData qortFromQoraData) throws DataException;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,7 @@ import java.util.List;
|
|||||||
import org.qora.data.account.AccountBalanceData;
|
import org.qora.data.account.AccountBalanceData;
|
||||||
import org.qora.data.account.AccountData;
|
import org.qora.data.account.AccountData;
|
||||||
import org.qora.data.account.MintingAccountData;
|
import org.qora.data.account.MintingAccountData;
|
||||||
|
import org.qora.data.account.QortFromQoraData;
|
||||||
import org.qora.data.account.RewardShareData;
|
import org.qora.data.account.RewardShareData;
|
||||||
import org.qora.repository.AccountRepository;
|
import org.qora.repository.AccountRepository;
|
||||||
import org.qora.repository.DataException;
|
import org.qora.repository.DataException;
|
||||||
@ -660,4 +661,38 @@ public class HSQLDBAccountRepository implements AccountRepository {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Managing QORT from legacy QORA
|
||||||
|
|
||||||
|
public QortFromQoraData getQortFromQoraInfo(String address) throws DataException {
|
||||||
|
String sql = "SELECT final_qort_from_qora, final_block_height FROM AccountQortFromQoraInfo WHERE account = ?";
|
||||||
|
|
||||||
|
try (ResultSet resultSet = this.repository.checkedExecute(sql, address)) {
|
||||||
|
if (resultSet == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
BigDecimal finalQortFromQora = resultSet.getBigDecimal(1);
|
||||||
|
Integer finalBlockHeight = resultSet.getInt(2);
|
||||||
|
if (finalBlockHeight == 0 && resultSet.wasNull())
|
||||||
|
finalBlockHeight = null;
|
||||||
|
|
||||||
|
return new QortFromQoraData(address, finalQortFromQora, finalBlockHeight);
|
||||||
|
} catch (SQLException e) {
|
||||||
|
throw new DataException("Unable to fetch account qort-from-qora info from repository", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void save(QortFromQoraData qortFromQoraData) throws DataException {
|
||||||
|
HSQLDBSaver saveHelper = new HSQLDBSaver("AccountQortFromQoraInfo");
|
||||||
|
|
||||||
|
saveHelper.bind("account", qortFromQoraData.getAddress())
|
||||||
|
.bind("final_qort_from_qora", qortFromQoraData.getFinalQortFromQora())
|
||||||
|
.bind("final_block_height", qortFromQoraData.getFinalBlockHeight());
|
||||||
|
|
||||||
|
try {
|
||||||
|
saveHelper.execute(this.repository);
|
||||||
|
} catch (SQLException e) {
|
||||||
|
throw new DataException("Unable to save account qort-from-qora info into repository", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -841,6 +841,12 @@ public class HSQLDBDatabaseUpdates {
|
|||||||
stmt.execute("DROP INDEX BlockGenerationHeightIndex");
|
stmt.execute("DROP INDEX BlockGenerationHeightIndex");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 59:
|
||||||
|
// Keeping track of QORT gained from holding legacy QORA
|
||||||
|
stmt.execute("CREATE TABLE AccountQortFromQoraInfo (account QoraAddress, final_qort_from_qora QoraAmount, final_block_height INT, "
|
||||||
|
+ "PRIMARY KEY (account), FOREIGN KEY (account) REFERENCES Accounts (account) ON DELETE CASCADE)");
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
// nothing to do
|
// nothing to do
|
||||||
return false;
|
return false;
|
||||||
|
@ -7,7 +7,6 @@ import java.util.List;
|
|||||||
|
|
||||||
import org.qora.account.Account;
|
import org.qora.account.Account;
|
||||||
import org.qora.account.PrivateKeyAccount;
|
import org.qora.account.PrivateKeyAccount;
|
||||||
import org.qora.asset.Asset;
|
|
||||||
import org.qora.crypto.Crypto;
|
import org.qora.crypto.Crypto;
|
||||||
import org.qora.data.transaction.GenesisTransactionData;
|
import org.qora.data.transaction.GenesisTransactionData;
|
||||||
import org.qora.data.transaction.TransactionData;
|
import org.qora.data.transaction.TransactionData;
|
||||||
@ -139,7 +138,7 @@ public class GenesisTransaction extends Transaction {
|
|||||||
Account recipient = new Account(repository, genesisTransactionData.getRecipient());
|
Account recipient = new Account(repository, genesisTransactionData.getRecipient());
|
||||||
|
|
||||||
// Update recipient's balance
|
// Update recipient's balance
|
||||||
recipient.setConfirmedBalance(Asset.QORT, genesisTransactionData.getAmount());
|
recipient.setConfirmedBalance(genesisTransactionData.getAssetId(), genesisTransactionData.getAmount());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -2,6 +2,8 @@ package org.qora.test.common;
|
|||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
|
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
import org.qora.account.PrivateKeyAccount;
|
import org.qora.account.PrivateKeyAccount;
|
||||||
import org.qora.block.Block;
|
import org.qora.block.Block;
|
||||||
import org.qora.block.BlockChain;
|
import org.qora.block.BlockChain;
|
||||||
@ -12,6 +14,8 @@ import org.qora.repository.Repository;
|
|||||||
|
|
||||||
public class BlockUtils {
|
public class BlockUtils {
|
||||||
|
|
||||||
|
private static final Logger LOGGER = LogManager.getLogger(BlockUtils.class);
|
||||||
|
|
||||||
/** Mints a new block using "alice-reward-share" test account. */
|
/** Mints a new block using "alice-reward-share" test account. */
|
||||||
public static void mintBlock(Repository repository) throws DataException {
|
public static void mintBlock(Repository repository) throws DataException {
|
||||||
PrivateKeyAccount mintingAccount = Common.getTestAccount(repository, "alice-reward-share");
|
PrivateKeyAccount mintingAccount = Common.getTestAccount(repository, "alice-reward-share");
|
||||||
@ -26,8 +30,14 @@ public class BlockUtils {
|
|||||||
|
|
||||||
public static void orphanLastBlock(Repository repository) throws DataException {
|
public static void orphanLastBlock(Repository repository) throws DataException {
|
||||||
BlockData blockData = repository.getBlockRepository().getLastBlock();
|
BlockData blockData = repository.getBlockRepository().getLastBlock();
|
||||||
|
|
||||||
|
final int height = blockData.getHeight();
|
||||||
|
|
||||||
Block block = new Block(repository, blockData);
|
Block block = new Block(repository, blockData);
|
||||||
block.orphan();
|
block.orphan();
|
||||||
|
|
||||||
|
LOGGER.info(String.format("Orphaned block: %d", height));
|
||||||
|
|
||||||
repository.saveChanges();
|
repository.saveChanges();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,16 +16,13 @@ import java.util.stream.Collectors;
|
|||||||
|
|
||||||
import org.apache.logging.log4j.LogManager;
|
import org.apache.logging.log4j.LogManager;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
import org.bitcoinj.core.Base58;
|
|
||||||
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
||||||
import org.bouncycastle.jsse.provider.BouncyCastleJsseProvider;
|
import org.bouncycastle.jsse.provider.BouncyCastleJsseProvider;
|
||||||
import org.junit.AfterClass;
|
import org.junit.AfterClass;
|
||||||
import org.junit.BeforeClass;
|
import org.junit.BeforeClass;
|
||||||
import org.qora.block.Block;
|
|
||||||
import org.qora.block.BlockChain;
|
import org.qora.block.BlockChain;
|
||||||
import org.qora.data.account.AccountBalanceData;
|
import org.qora.data.account.AccountBalanceData;
|
||||||
import org.qora.data.asset.AssetData;
|
import org.qora.data.asset.AssetData;
|
||||||
import org.qora.data.block.BlockData;
|
|
||||||
import org.qora.data.group.GroupData;
|
import org.qora.data.group.GroupData;
|
||||||
import org.qora.repository.AccountRepository.BalanceOrdering;
|
import org.qora.repository.AccountRepository.BalanceOrdering;
|
||||||
import org.qora.repository.DataException;
|
import org.qora.repository.DataException;
|
||||||
@ -65,11 +62,6 @@ public class Common {
|
|||||||
private static List<GroupData> initialGroups;
|
private static List<GroupData> initialGroups;
|
||||||
private static List<AccountBalanceData> initialBalances;
|
private static List<AccountBalanceData> initialBalances;
|
||||||
|
|
||||||
// TODO: converts users of these constants to TestAccount schema
|
|
||||||
public static final byte[] v2testPrivateKey = Base58.decode("A9MNsATgQgruBUjxy2rjWY36Yf19uRioKZbiLFT2P7c6");
|
|
||||||
public static final byte[] v2testPublicKey = Base58.decode("2tiMr5LTpaWCgbRvkPK8TFd7k63DyHJMMFFsz9uBf1ZP");
|
|
||||||
public static final String v2testAddress = "QgV4s3xnzLhVBEJxcYui4u4q11yhUHsd9v";
|
|
||||||
|
|
||||||
private static Map<String, TestAccount> testAccountsByName = new HashMap<>();
|
private static Map<String, TestAccount> testAccountsByName = new HashMap<>();
|
||||||
static {
|
static {
|
||||||
testAccountsByName.put("alice", new TestAccount(null, "alice", "A9MNsATgQgruBUjxy2rjWY36Yf19uRioKZbiLFT2P7c6", false));
|
testAccountsByName.put("alice", new TestAccount(null, "alice", "A9MNsATgQgruBUjxy2rjWY36Yf19uRioKZbiLFT2P7c6", false));
|
||||||
@ -131,10 +123,7 @@ public class Common {
|
|||||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||||
// Orphan back to genesis block
|
// Orphan back to genesis block
|
||||||
while (repository.getBlockRepository().getBlockchainHeight() > 1) {
|
while (repository.getBlockRepository().getBlockchainHeight() > 1) {
|
||||||
BlockData blockData = repository.getBlockRepository().getLastBlock();
|
BlockUtils.orphanLastBlock(repository);
|
||||||
Block block = new Block(repository, blockData);
|
|
||||||
block.orphan();
|
|
||||||
repository.saveChanges();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
List<AssetData> remainingAssets = repository.getAssetRepository().getAllAssets();
|
List<AssetData> remainingAssets = repository.getAssetRepository().getAllAssets();
|
||||||
@ -172,6 +161,9 @@ public class Common {
|
|||||||
List<T> remainingClone = new ArrayList<T>(remaining);
|
List<T> remainingClone = new ArrayList<T>(remaining);
|
||||||
remainingClone.removeIf(isInitial);
|
remainingClone.removeIf(isInitial);
|
||||||
|
|
||||||
|
for (T remainingEntry : remainingClone)
|
||||||
|
LOGGER.info(String.format("Non-genesis remaining entry: %s", keyExtractor.apply(remainingEntry)));
|
||||||
|
|
||||||
assertTrue(String.format("Non-genesis %s remains", typeName), remainingClone.isEmpty());
|
assertTrue(String.format("Non-genesis %s remains", typeName), remainingClone.isEmpty());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,23 +48,24 @@ public class RewardTests extends Common {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testRewards() throws DataException {
|
public void testRewards() throws DataException {
|
||||||
|
List<RewardByHeight> rewardsByHeight = BlockChain.getInstance().getBlockRewardsByHeight();
|
||||||
|
|
||||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||||
Map<String, Map<Long, BigDecimal>> initialBalances = AccountUtils.getBalances(repository, Asset.QORT);
|
Map<String, Map<Long, BigDecimal>> initialBalances = AccountUtils.getBalances(repository, Asset.QORT);
|
||||||
|
|
||||||
List<RewardByHeight> rewards = BlockChain.getInstance().getBlockRewardsByHeight();
|
int rewardIndex = rewardsByHeight.size() - 1;
|
||||||
|
|
||||||
int rewardIndex = rewards.size() - 1;
|
RewardByHeight rewardInfo = rewardsByHeight.get(rewardIndex);
|
||||||
|
|
||||||
RewardByHeight rewardInfo = rewards.get(rewardIndex);
|
|
||||||
BigDecimal expectedBalance = initialBalances.get("alice").get(Asset.QORT);
|
BigDecimal expectedBalance = initialBalances.get("alice").get(Asset.QORT);
|
||||||
|
|
||||||
for (int height = rewardInfo.height; height > 1; --height) {
|
for (int height = rewardInfo.height; height > 1; --height) {
|
||||||
if (height < rewardInfo.height) {
|
if (height < rewardInfo.height) {
|
||||||
--rewardIndex;
|
--rewardIndex;
|
||||||
rewardInfo = rewards.get(rewardIndex);
|
rewardInfo = rewardsByHeight.get(rewardIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
BlockUtils.mintBlock(repository);
|
BlockUtils.mintBlock(repository);
|
||||||
|
|
||||||
expectedBalance = expectedBalance.add(rewardInfo.reward);
|
expectedBalance = expectedBalance.add(rewardInfo.reward);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -82,6 +83,7 @@ public class RewardTests extends Common {
|
|||||||
|
|
||||||
Map<String, Map<Long, BigDecimal>> initialBalances = AccountUtils.getBalances(repository, Asset.QORT);
|
Map<String, Map<Long, BigDecimal>> initialBalances = AccountUtils.getBalances(repository, Asset.QORT);
|
||||||
BigDecimal blockReward = BlockUtils.getNextBlockReward(repository);
|
BigDecimal blockReward = BlockUtils.getNextBlockReward(repository);
|
||||||
|
|
||||||
BlockMinter.mintTestingBlock(repository, rewardShareAccount);
|
BlockMinter.mintTestingBlock(repository, rewardShareAccount);
|
||||||
|
|
||||||
// We're expecting reward * 12.8% to Bob, the rest to Alice
|
// We're expecting reward * 12.8% to Bob, the rest to Alice
|
||||||
@ -94,4 +96,47 @@ public class RewardTests extends Common {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testLegacyQoraReward() throws DataException {
|
||||||
|
Common.useSettings("test-settings-v2-qora-holder.json");
|
||||||
|
|
||||||
|
BigDecimal qoraHoldersShare = BlockChain.getInstance().getQoraHoldersShare();
|
||||||
|
BigDecimal qoraPerQort = BlockChain.getInstance().getQoraPerQortReward();
|
||||||
|
|
||||||
|
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||||
|
Map<String, Map<Long, BigDecimal>> initialBalances = AccountUtils.getBalances(repository, Asset.QORT, Asset.QORT_FROM_QORA);
|
||||||
|
|
||||||
|
BigDecimal blockReward = BlockUtils.getNextBlockReward(repository);
|
||||||
|
|
||||||
|
BlockUtils.mintBlock(repository);
|
||||||
|
|
||||||
|
// Expected reward
|
||||||
|
BigDecimal expectedReward = blockReward.multiply(qoraHoldersShare).divide(qoraPerQort, RoundingMode.DOWN);
|
||||||
|
AccountUtils.assertBalance(repository, "chloe", Asset.QORT, initialBalances.get("chloe").get(Asset.QORT).add(expectedReward));
|
||||||
|
|
||||||
|
AccountUtils.assertBalance(repository, "chloe", Asset.QORT_FROM_QORA, initialBalances.get("chloe").get(Asset.QORT_FROM_QORA).add(expectedReward));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMaxLegacyQoraReward() throws DataException {
|
||||||
|
Common.useSettings("test-settings-v2-qora-holder.json");
|
||||||
|
|
||||||
|
BigDecimal qoraPerQort = BlockChain.getInstance().getQoraPerQortReward();
|
||||||
|
|
||||||
|
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||||
|
Map<String, Map<Long, BigDecimal>> initialBalances = AccountUtils.getBalances(repository, Asset.QORT, Asset.LEGACY_QORA, Asset.QORT_FROM_QORA);
|
||||||
|
|
||||||
|
// Mint lots of blocks
|
||||||
|
for (int i = 0; i < 100; ++i)
|
||||||
|
BlockUtils.mintBlock(repository);
|
||||||
|
|
||||||
|
// Expected balances to be limited by Chloe's legacy QORA amount
|
||||||
|
BigDecimal expectedBalance = initialBalances.get("chloe").get(Asset.LEGACY_QORA).divide(qoraPerQort);
|
||||||
|
AccountUtils.assertBalance(repository, "chloe", Asset.QORT, initialBalances.get("chloe").get(Asset.QORT).add(expectedBalance));
|
||||||
|
AccountUtils.assertBalance(repository, "chloe", Asset.QORT_FROM_QORA, initialBalances.get("chloe").get(Asset.QORT_FROM_QORA).add(expectedBalance));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
69
src/test/resources/test-chain-v2-qora-holder.json
Normal file
69
src/test/resources/test-chain-v2-qora-holder.json
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
{
|
||||||
|
"isTestChain": true,
|
||||||
|
"blockTimestampMargin": 500,
|
||||||
|
"transactionExpiryPeriod": 86400000,
|
||||||
|
"maxBlockSize": 2097152,
|
||||||
|
"maxBytesPerUnitFee": 1024,
|
||||||
|
"unitFee": "0.1",
|
||||||
|
"requireGroupForApproval": false,
|
||||||
|
"minAccountLevelToRewardShare": 5,
|
||||||
|
"maxRewardSharesPerMintingAccount": 20,
|
||||||
|
"onlineAccountSignaturesMinLifetime": 3600000,
|
||||||
|
"onlineAccountSignaturesMaxLifetime": 86400000,
|
||||||
|
"rewardsByHeight": [
|
||||||
|
{ "height": 1, "reward": 100 },
|
||||||
|
{ "height": 11, "reward": 10 },
|
||||||
|
{ "height": 21, "reward": 1 }
|
||||||
|
],
|
||||||
|
"sharesByLevel": [
|
||||||
|
{ "levels": [ 1, 2 ], "share": 0.05 },
|
||||||
|
{ "levels": [ 3, 4 ], "share": 0.10 },
|
||||||
|
{ "levels": [ 5, 6 ], "share": 0.15 },
|
||||||
|
{ "levels": [ 7, 8 ], "share": 0.20 },
|
||||||
|
{ "levels": [ 9, 10 ], "share": 0.25 }
|
||||||
|
],
|
||||||
|
"qoraHoldersShare": 0.20,
|
||||||
|
"qoraPerQortReward": 250,
|
||||||
|
"blocksNeededByLevel": [ 10, 20, 30, 40, 50, 60, 70, 80, 90, 100 ],
|
||||||
|
"blockTimingsByHeight": [
|
||||||
|
{ "height": 1, "target": 60000, "deviation": 30000, "power": 0.2 }
|
||||||
|
],
|
||||||
|
"featureTriggers": {
|
||||||
|
"messageHeight": 0,
|
||||||
|
"atHeight": 0,
|
||||||
|
"assetsTimestamp": 0,
|
||||||
|
"votingTimestamp": 0,
|
||||||
|
"arbitraryTimestamp": 0,
|
||||||
|
"powfixTimestamp": 0,
|
||||||
|
"v2Timestamp": 0,
|
||||||
|
"newAssetPricingTimestamp": 0,
|
||||||
|
"groupApprovalTimestamp": 0
|
||||||
|
},
|
||||||
|
"genesisInfo": {
|
||||||
|
"version": 4,
|
||||||
|
"timestamp": 0,
|
||||||
|
"transactions": [
|
||||||
|
{ "type": "ISSUE_ASSET", "owner": "QcFmNxSArv5tWEzCtTKb2Lqc5QkKuQ7RNs", "assetName": "QORT", "description": "QORT native coin", "data": "", "quantity": 0, "isDivisible": true, "fee": 0, "reference": "3Verk6ZKBJc3WTTVfxFC9icSjKdM8b92eeJEpJP8qNizG4ZszNFq8wdDYdSjJXq2iogDFR1njyhsBdVpbvDfjzU7" },
|
||||||
|
{ "type": "ISSUE_ASSET", "owner": "QUwGVHPPxJNJ2dq95abQNe79EyBN2K26zM", "assetName": "Legacy-QORA", "description": "Representative legacy QORA", "quantity": 0, "isDivisible": true, "data": "{}", "isUnspendable": true },
|
||||||
|
{ "type": "ISSUE_ASSET", "owner": "QUwGVHPPxJNJ2dq95abQNe79EyBN2K26zM", "assetName": "QORT-from-QORA", "description": "QORT gained from holding legacy QORA", "quantity": 0, "isDivisible": true, "data": "{}", "isUnspendable": true },
|
||||||
|
|
||||||
|
{ "type": "GENESIS", "recipient": "QgV4s3xnzLhVBEJxcYui4u4q11yhUHsd9v", "amount": "1000000000" },
|
||||||
|
{ "type": "GENESIS", "recipient": "QixPbJUwsaHsVEofJdozU9zgVqkK6aYhrK", "amount": "1000000" },
|
||||||
|
{ "type": "GENESIS", "recipient": "QaUpHNhT3Ygx6avRiKobuLdusppR5biXjL", "amount": "1000000" },
|
||||||
|
{ "type": "GENESIS", "recipient": "Qci5m9k4rcwe4ruKrZZQKka4FzUUMut3er", "amount": "1000000" },
|
||||||
|
|
||||||
|
{ "type": "GENESIS", "recipient": "QaUpHNhT3Ygx6avRiKobuLdusppR5biXjL", "amount": "100", "assetId": 1 },
|
||||||
|
|
||||||
|
{ "type": "CREATE_GROUP", "creatorPublicKey": "2tiMr5LTpaWCgbRvkPK8TFd7k63DyHJMMFFsz9uBf1ZP", "owner": "QgV4s3xnzLhVBEJxcYui4u4q11yhUHsd9v", "groupName": "dev-group", "description": "developer group", "isOpen": false, "approvalThreshold": "PCT100", "minimumBlockDelay": 0, "maximumBlockDelay": 1440 },
|
||||||
|
|
||||||
|
{ "type": "ISSUE_ASSET", "owner": "QgV4s3xnzLhVBEJxcYui4u4q11yhUHsd9v", "assetName": "TEST", "description": "test asset", "data": "", "quantity": 1000000, "isDivisible": true, "fee": 0 },
|
||||||
|
{ "type": "ISSUE_ASSET", "owner": "QixPbJUwsaHsVEofJdozU9zgVqkK6aYhrK", "assetName": "OTHER", "description": "other test asset", "data": "", "quantity": 1000000, "isDivisible": true, "fee": 0 },
|
||||||
|
{ "type": "ISSUE_ASSET", "owner": "QgV4s3xnzLhVBEJxcYui4u4q11yhUHsd9v", "assetName": "GOLD", "description": "gold test asset", "data": "", "quantity": 1000000, "isDivisible": true, "fee": 0 },
|
||||||
|
|
||||||
|
{ "type": "ACCOUNT_FLAGS", "target": "QgV4s3xnzLhVBEJxcYui4u4q11yhUHsd9v", "andMask": -1, "orMask": 1, "xorMask": 0 },
|
||||||
|
{ "type": "REWARD_SHARE", "minterPublicKey": "2tiMr5LTpaWCgbRvkPK8TFd7k63DyHJMMFFsz9uBf1ZP", "recipient": "QgV4s3xnzLhVBEJxcYui4u4q11yhUHsd9v", "rewardSharePublicKey": "7PpfnvLSG7y4HPh8hE7KoqAjLCkv7Ui6xw4mKAkbZtox", "sharePercent": 100 },
|
||||||
|
|
||||||
|
{ "type": "ACCOUNT_LEVEL", "target": "Qci5m9k4rcwe4ruKrZZQKka4FzUUMut3er", "level": 8 }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
@ -23,6 +23,7 @@
|
|||||||
{ "levels": [ 9, 10 ], "share": 0.25 }
|
{ "levels": [ 9, 10 ], "share": 0.25 }
|
||||||
],
|
],
|
||||||
"qoraHoldersShare": 0.20,
|
"qoraHoldersShare": 0.20,
|
||||||
|
"qoraPerQortReward": 250,
|
||||||
"blocksNeededByLevel": [ 10, 20, 30, 40, 50, 60, 70, 80, 90, 100 ],
|
"blocksNeededByLevel": [ 10, 20, 30, 40, 50, 60, 70, 80, 90, 100 ],
|
||||||
"blockTimingsByHeight": [
|
"blockTimingsByHeight": [
|
||||||
{ "height": 1, "target": 60000, "deviation": 30000, "power": 0.2 }
|
{ "height": 1, "target": 60000, "deviation": 30000, "power": 0.2 }
|
||||||
@ -42,18 +43,25 @@
|
|||||||
"version": 4,
|
"version": 4,
|
||||||
"timestamp": 0,
|
"timestamp": 0,
|
||||||
"transactions": [
|
"transactions": [
|
||||||
{ "type": "ISSUE_ASSET", "owner": "QcFmNxSArv5tWEzCtTKb2Lqc5QkKuQ7RNs", "assetName": "QORA", "description": "QORA native coin", "data": "", "quantity": 10000000000, "isDivisible": true, "fee": 0, "reference": "3Verk6ZKBJc3WTTVfxFC9icSjKdM8b92eeJEpJP8qNizG4ZszNFq8wdDYdSjJXq2iogDFR1njyhsBdVpbvDfjzU7" },
|
{ "type": "ISSUE_ASSET", "owner": "QcFmNxSArv5tWEzCtTKb2Lqc5QkKuQ7RNs", "assetName": "QORT", "description": "QORT native coin", "data": "", "quantity": 0, "isDivisible": true, "fee": 0, "reference": "3Verk6ZKBJc3WTTVfxFC9icSjKdM8b92eeJEpJP8qNizG4ZszNFq8wdDYdSjJXq2iogDFR1njyhsBdVpbvDfjzU7" },
|
||||||
{ "type": "GENESIS", "recipient": "QgV4s3xnzLhVBEJxcYui4u4q11yhUHsd9v", "amount": "1000000000", "fee": 0 },
|
{ "type": "ISSUE_ASSET", "owner": "QUwGVHPPxJNJ2dq95abQNe79EyBN2K26zM", "assetName": "Legacy-QORA", "description": "Representative legacy QORA", "quantity": 0, "isDivisible": true, "data": "{}", "isUnspendable": true },
|
||||||
{ "type": "GENESIS", "recipient": "QixPbJUwsaHsVEofJdozU9zgVqkK6aYhrK", "amount": "1000000", "fee": 0 },
|
{ "type": "ISSUE_ASSET", "owner": "QUwGVHPPxJNJ2dq95abQNe79EyBN2K26zM", "assetName": "QORT-from-QORA", "description": "QORT gained from holding legacy QORA", "quantity": 0, "isDivisible": true, "data": "{}", "isUnspendable": true },
|
||||||
{ "type": "GENESIS", "recipient": "QaUpHNhT3Ygx6avRiKobuLdusppR5biXjL", "amount": "1000000", "fee": 0 },
|
|
||||||
{ "type": "GENESIS", "recipient": "Qci5m9k4rcwe4ruKrZZQKka4FzUUMut3er", "amount": "1000000", "fee": 0 },
|
{ "type": "GENESIS", "recipient": "QgV4s3xnzLhVBEJxcYui4u4q11yhUHsd9v", "amount": "1000000000" },
|
||||||
|
{ "type": "GENESIS", "recipient": "QixPbJUwsaHsVEofJdozU9zgVqkK6aYhrK", "amount": "1000000" },
|
||||||
|
{ "type": "GENESIS", "recipient": "QaUpHNhT3Ygx6avRiKobuLdusppR5biXjL", "amount": "1000000" },
|
||||||
|
{ "type": "GENESIS", "recipient": "Qci5m9k4rcwe4ruKrZZQKka4FzUUMut3er", "amount": "1000000" },
|
||||||
|
|
||||||
{ "type": "CREATE_GROUP", "creatorPublicKey": "2tiMr5LTpaWCgbRvkPK8TFd7k63DyHJMMFFsz9uBf1ZP", "owner": "QgV4s3xnzLhVBEJxcYui4u4q11yhUHsd9v", "groupName": "dev-group", "description": "developer group", "isOpen": false, "approvalThreshold": "PCT100", "minimumBlockDelay": 0, "maximumBlockDelay": 1440 },
|
{ "type": "CREATE_GROUP", "creatorPublicKey": "2tiMr5LTpaWCgbRvkPK8TFd7k63DyHJMMFFsz9uBf1ZP", "owner": "QgV4s3xnzLhVBEJxcYui4u4q11yhUHsd9v", "groupName": "dev-group", "description": "developer group", "isOpen": false, "approvalThreshold": "PCT100", "minimumBlockDelay": 0, "maximumBlockDelay": 1440 },
|
||||||
|
|
||||||
{ "type": "ISSUE_ASSET", "owner": "QgV4s3xnzLhVBEJxcYui4u4q11yhUHsd9v", "assetName": "TEST", "description": "test asset", "data": "", "quantity": 1000000, "isDivisible": true, "fee": 0 },
|
{ "type": "ISSUE_ASSET", "owner": "QgV4s3xnzLhVBEJxcYui4u4q11yhUHsd9v", "assetName": "TEST", "description": "test asset", "data": "", "quantity": 1000000, "isDivisible": true, "fee": 0 },
|
||||||
{ "type": "ISSUE_ASSET", "owner": "QixPbJUwsaHsVEofJdozU9zgVqkK6aYhrK", "assetName": "OTHER", "description": "other test asset", "data": "", "quantity": 1000000, "isDivisible": true, "fee": 0 },
|
{ "type": "ISSUE_ASSET", "owner": "QixPbJUwsaHsVEofJdozU9zgVqkK6aYhrK", "assetName": "OTHER", "description": "other test asset", "data": "", "quantity": 1000000, "isDivisible": true, "fee": 0 },
|
||||||
{ "type": "ISSUE_ASSET", "owner": "QgV4s3xnzLhVBEJxcYui4u4q11yhUHsd9v", "assetName": "GOLD", "description": "gold test asset", "data": "", "quantity": 1000000, "isDivisible": true, "fee": 0 },
|
{ "type": "ISSUE_ASSET", "owner": "QgV4s3xnzLhVBEJxcYui4u4q11yhUHsd9v", "assetName": "GOLD", "description": "gold test asset", "data": "", "quantity": 1000000, "isDivisible": true, "fee": 0 },
|
||||||
|
|
||||||
{ "type": "ACCOUNT_FLAGS", "target": "QgV4s3xnzLhVBEJxcYui4u4q11yhUHsd9v", "andMask": -1, "orMask": 1, "xorMask": 0 },
|
{ "type": "ACCOUNT_FLAGS", "target": "QgV4s3xnzLhVBEJxcYui4u4q11yhUHsd9v", "andMask": -1, "orMask": 1, "xorMask": 0 },
|
||||||
{ "type": "ACCOUNT_LEVEL", "target": "Qci5m9k4rcwe4ruKrZZQKka4FzUUMut3er", "level": 8 },
|
{ "type": "REWARD_SHARE", "minterPublicKey": "2tiMr5LTpaWCgbRvkPK8TFd7k63DyHJMMFFsz9uBf1ZP", "recipient": "QgV4s3xnzLhVBEJxcYui4u4q11yhUHsd9v", "rewardSharePublicKey": "7PpfnvLSG7y4HPh8hE7KoqAjLCkv7Ui6xw4mKAkbZtox", "sharePercent": 100 },
|
||||||
{ "type": "REWARD_SHARE", "minterPublicKey": "2tiMr5LTpaWCgbRvkPK8TFd7k63DyHJMMFFsz9uBf1ZP", "recipient": "QgV4s3xnzLhVBEJxcYui4u4q11yhUHsd9v", "rewardSharePublicKey": "7PpfnvLSG7y4HPh8hE7KoqAjLCkv7Ui6xw4mKAkbZtox", "sharePercent": 100 }
|
|
||||||
|
{ "type": "ACCOUNT_LEVEL", "target": "Qci5m9k4rcwe4ruKrZZQKka4FzUUMut3er", "level": 8 }
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
6
src/test/resources/test-settings-v2-qora-holder.json
Normal file
6
src/test/resources/test-settings-v2-qora-holder.json
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"restrictedApi": false,
|
||||||
|
"blockchainConfig": "src/test/resources/test-chain-v2-qora-holder.json",
|
||||||
|
"wipeUnconfirmedOnStart": false,
|
||||||
|
"minPeers": 0
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user