forked from Qortal/qortal
Merge branch 'Qortal:master' into master
This commit is contained in:
commit
92d589a1ca
10
pom.xml
10
pom.xml
@ -3,7 +3,7 @@
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>org.qortal</groupId>
|
||||
<artifactId>qortal</artifactId>
|
||||
<version>4.5.0</version>
|
||||
<version>4.5.1</version>
|
||||
<packaging>jar</packaging>
|
||||
<properties>
|
||||
<skipTests>true</skipTests>
|
||||
@ -21,7 +21,7 @@
|
||||
<dagger.version>1.2.2</dagger.version>
|
||||
<extendedset.version>0.12.3</extendedset.version>
|
||||
<git-commit-id-plugin.version>4.9.10</git-commit-id-plugin.version>
|
||||
<grpc.version>1.61.0</grpc.version>
|
||||
<grpc.version>1.61.1</grpc.version>
|
||||
<guava.version>33.0.0-jre</guava.version>
|
||||
<hamcrest-library.version>2.2</hamcrest-library.version>
|
||||
<homoglyph.version>1.2.1</homoglyph.version>
|
||||
@ -31,9 +31,9 @@
|
||||
<javax.servlet-api.version>4.0.1</javax.servlet-api.version>
|
||||
<jaxb-runtime.version>2.3.9</jaxb-runtime.version>
|
||||
<jersey.version>2.41</jersey.version>
|
||||
<jetty.version>9.4.53.v20231009</jetty.version>
|
||||
<jetty.version>9.4.54.v20240208</jetty.version>
|
||||
<json-simple.version>1.1.1</json-simple.version>
|
||||
<json.version>20231013</json.version>
|
||||
<json.version>20240205</json.version>
|
||||
<jsoup.version>1.17.2</jsoup.version>
|
||||
<junit-jupiter-engine.version>5.10.0</junit-jupiter-engine.version>
|
||||
<lifecycle-mapping.version>1.0.0</lifecycle-mapping.version>
|
||||
@ -46,7 +46,7 @@
|
||||
<maven-surefire-plugin.version>3.2.5</maven-surefire-plugin.version>
|
||||
<package-info-maven-plugin.version>1.1.0</package-info-maven-plugin.version>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<protobuf.version>3.25.1</protobuf.version>
|
||||
<protobuf.version>3.25.2</protobuf.version>
|
||||
<replacer.version>1.5.3</replacer.version>
|
||||
<reproducible-build-maven-plugin.version>0.16</reproducible-build-maven-plugin.version>
|
||||
<simplemagic.version>1.17</simplemagic.version>
|
||||
|
249
src/main/java/org/qortal/account/SelfSponsorshipAlgoV2.java
Normal file
249
src/main/java/org/qortal/account/SelfSponsorshipAlgoV2.java
Normal file
@ -0,0 +1,249 @@
|
||||
package org.qortal.account;
|
||||
|
||||
import org.qortal.api.resource.TransactionsResource;
|
||||
import org.qortal.asset.Asset;
|
||||
import org.qortal.block.BlockChain;
|
||||
import org.qortal.data.account.AccountData;
|
||||
import org.qortal.data.transaction.*;
|
||||
import org.qortal.repository.DataException;
|
||||
import org.qortal.repository.Repository;
|
||||
import org.qortal.transaction.Transaction.TransactionType;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class SelfSponsorshipAlgoV2 {
|
||||
|
||||
private final long snapshotTimestampV1 = BlockChain.getInstance().getSelfSponsorshipAlgoV1SnapshotTimestamp();
|
||||
private final long snapshotTimestampV2 = BlockChain.getInstance().getSelfSponsorshipAlgoV2SnapshotTimestamp();
|
||||
private final long referenceTimestamp = BlockChain.getInstance().getReferenceTimestampBlock();
|
||||
|
||||
private final boolean override;
|
||||
private final Repository repository;
|
||||
private final String address;
|
||||
|
||||
private int recentAssetSendCount = 0;
|
||||
private int recentSponsorshipCount = 0;
|
||||
|
||||
private final Set<String> assetAddresses = new LinkedHashSet<>();
|
||||
private final Set<String> penaltyAddresses = new LinkedHashSet<>();
|
||||
private final Set<String> sponsorAddresses = new LinkedHashSet<>();
|
||||
|
||||
private List<RewardShareTransactionData> sponsorshipRewardShares = new ArrayList<>();
|
||||
private List<TransferAssetTransactionData> transferAssetForAddress = new ArrayList<>();
|
||||
|
||||
public SelfSponsorshipAlgoV2(Repository repository, String address, boolean override) {
|
||||
this.repository = repository;
|
||||
this.address = address;
|
||||
this.override = override;
|
||||
}
|
||||
|
||||
public String getAddress() {
|
||||
return this.address;
|
||||
}
|
||||
|
||||
public Set<String> getPenaltyAddresses() {
|
||||
return this.penaltyAddresses;
|
||||
}
|
||||
|
||||
public void run() throws DataException {
|
||||
if (!override) {
|
||||
this.getAccountPrivs(this.address);
|
||||
}
|
||||
|
||||
if (override) {
|
||||
this.fetchTransferAssetForAddress(this.address);
|
||||
this.findRecentAssetSendCount();
|
||||
|
||||
if (this.recentAssetSendCount >= 6) {
|
||||
this.penaltyAddresses.add(this.address);
|
||||
this.penaltyAddresses.addAll(this.assetAddresses);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void getAccountPrivs(String address) throws DataException {
|
||||
AccountData accountData = this.repository.getAccountRepository().getAccount(address);
|
||||
List<TransactionData> transferPrivsTransactions = fetchTransferPrivsForAddress(address);
|
||||
transferPrivsTransactions.removeIf(t -> t.getTimestamp() > this.referenceTimestamp || accountData.getAddress().equals(t.getRecipient()));
|
||||
|
||||
if (transferPrivsTransactions.isEmpty()) {
|
||||
// Nothing to do
|
||||
return;
|
||||
}
|
||||
|
||||
for (TransactionData transactionData : transferPrivsTransactions) {
|
||||
TransferPrivsTransactionData transferPrivsTransactionData = (TransferPrivsTransactionData) transactionData;
|
||||
this.penaltyAddresses.add(transferPrivsTransactionData.getRecipient());
|
||||
this.fetchSponsorshipRewardShares(transferPrivsTransactionData.getRecipient());
|
||||
this.findRecentSponsorshipCount();
|
||||
|
||||
if (this.recentSponsorshipCount >= 1) {
|
||||
this.penaltyAddresses.addAll(this.sponsorAddresses);
|
||||
}
|
||||
|
||||
String newAddress = this.getDestinationAccount(transferPrivsTransactionData.getRecipient());
|
||||
|
||||
while (newAddress != null) {
|
||||
// Found destination account
|
||||
this.penaltyAddresses.add(newAddress);
|
||||
this.fetchSponsorshipRewardShares(newAddress);
|
||||
this.findRecentSponsorshipCount();
|
||||
|
||||
if (this.recentSponsorshipCount >= 1) {
|
||||
this.penaltyAddresses.addAll(this.sponsorAddresses);
|
||||
}
|
||||
|
||||
newAddress = this.getDestinationAccount(newAddress);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String getDestinationAccount(String address) throws DataException {
|
||||
AccountData accountData = this.repository.getAccountRepository().getAccount(address);
|
||||
List<TransactionData> transferPrivsTransactions = fetchTransferPrivsForAddress(address);
|
||||
transferPrivsTransactions.removeIf(t -> t.getTimestamp() > this.referenceTimestamp || accountData.getAddress().equals(t.getRecipient()));
|
||||
|
||||
if (transferPrivsTransactions.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (accountData == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
for (TransactionData transactionData : transferPrivsTransactions) {
|
||||
TransferPrivsTransactionData transferPrivsTransactionData = (TransferPrivsTransactionData) transactionData;
|
||||
if (Arrays.equals(transferPrivsTransactionData.getSenderPublicKey(), accountData.getPublicKey())) {
|
||||
return transferPrivsTransactionData.getRecipient();
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private void fetchSponsorshipRewardShares(String address) throws DataException {
|
||||
AccountData accountDataRs = this.repository.getAccountRepository().getAccount(address);
|
||||
List<RewardShareTransactionData> sponsorshipRewardShares = new ArrayList<>();
|
||||
|
||||
// Define relevant transactions
|
||||
List<TransactionType> txTypes = List.of(TransactionType.REWARD_SHARE);
|
||||
List<TransactionData> transactionDataList = fetchTransactions(repository, txTypes, address, false);
|
||||
|
||||
for (TransactionData transactionData : transactionDataList) {
|
||||
if (transactionData.getType() != TransactionType.REWARD_SHARE) {
|
||||
continue;
|
||||
}
|
||||
|
||||
RewardShareTransactionData rewardShareTransactionData = (RewardShareTransactionData) transactionData;
|
||||
|
||||
// Skip removals
|
||||
if (rewardShareTransactionData.getSharePercent() < 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Skip if not sponsored by this account
|
||||
if (!Arrays.equals(rewardShareTransactionData.getCreatorPublicKey(), accountDataRs.getPublicKey())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Skip self shares
|
||||
if (Objects.equals(rewardShareTransactionData.getRecipient(), address)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
boolean duplicateFound = false;
|
||||
for (RewardShareTransactionData existingRewardShare : sponsorshipRewardShares) {
|
||||
if (Objects.equals(existingRewardShare.getRecipient(), rewardShareTransactionData.getRecipient())) {
|
||||
// Duplicate
|
||||
duplicateFound = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!duplicateFound) {
|
||||
sponsorshipRewardShares.add(rewardShareTransactionData);
|
||||
this.sponsorAddresses.add(rewardShareTransactionData.getRecipient());
|
||||
}
|
||||
}
|
||||
|
||||
this.sponsorshipRewardShares = sponsorshipRewardShares;
|
||||
}
|
||||
|
||||
private void fetchTransferAssetForAddress(String address) throws DataException {
|
||||
List<TransferAssetTransactionData> transferAssetForAddress = new ArrayList<>();
|
||||
|
||||
// Define relevant transactions
|
||||
List<TransactionType> txTypes = List.of(TransactionType.TRANSFER_ASSET);
|
||||
List<TransactionData> transactionDataList = fetchTransactions(repository, txTypes, address, false);
|
||||
transactionDataList.removeIf(t -> t.getTimestamp() <= this.snapshotTimestampV1 || t.getTimestamp() >= this.snapshotTimestampV2);
|
||||
|
||||
for (TransactionData transactionData : transactionDataList) {
|
||||
if (transactionData.getType() != TransactionType.TRANSFER_ASSET) {
|
||||
continue;
|
||||
}
|
||||
|
||||
TransferAssetTransactionData transferAssetTransactionData = (TransferAssetTransactionData) transactionData;
|
||||
|
||||
if (transferAssetTransactionData.getAssetId() == Asset.QORT) {
|
||||
if (!Objects.equals(transferAssetTransactionData.getRecipient(), address)) {
|
||||
// Outgoing transfer asset for this account
|
||||
transferAssetForAddress.add(transferAssetTransactionData);
|
||||
this.assetAddresses.add(transferAssetTransactionData.getRecipient());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.transferAssetForAddress = transferAssetForAddress;
|
||||
}
|
||||
|
||||
private void findRecentSponsorshipCount() {
|
||||
int recentSponsorshipCount = 0;
|
||||
|
||||
for (RewardShareTransactionData rewardShare : sponsorshipRewardShares) {
|
||||
if (rewardShare.getTimestamp() >= this.snapshotTimestampV1) {
|
||||
recentSponsorshipCount++;
|
||||
}
|
||||
}
|
||||
|
||||
this.recentSponsorshipCount = recentSponsorshipCount;
|
||||
}
|
||||
|
||||
private void findRecentAssetSendCount() {
|
||||
int recentAssetSendCount = 0;
|
||||
|
||||
for (TransferAssetTransactionData assetSend : transferAssetForAddress) {
|
||||
if (assetSend.getTimestamp() >= this.snapshotTimestampV1) {
|
||||
recentAssetSendCount++;
|
||||
}
|
||||
}
|
||||
|
||||
this.recentAssetSendCount = recentAssetSendCount;
|
||||
}
|
||||
|
||||
private List<TransactionData> fetchTransferPrivsForAddress(String address) throws DataException {
|
||||
return fetchTransactions(repository,
|
||||
List.of(TransactionType.TRANSFER_PRIVS),
|
||||
address, true);
|
||||
}
|
||||
|
||||
private static List<TransactionData> fetchTransactions(Repository repository, List<TransactionType> txTypes, String address, boolean reverse) throws DataException {
|
||||
// Fetch all relevant transactions for this account
|
||||
List<byte[]> signatures = repository.getTransactionRepository()
|
||||
.getSignaturesMatchingCriteria(null, null, null, txTypes,
|
||||
null, null, address, TransactionsResource.ConfirmationStatus.CONFIRMED,
|
||||
null, null, reverse);
|
||||
|
||||
List<TransactionData> transactionDataList = new ArrayList<>();
|
||||
|
||||
for (byte[] signature : signatures) {
|
||||
// Fetch transaction data
|
||||
TransactionData transactionData = repository.getTransactionRepository().fromSignature(signature);
|
||||
if (transactionData == null) {
|
||||
continue;
|
||||
}
|
||||
transactionDataList.add(transactionData);
|
||||
}
|
||||
|
||||
return transactionDataList;
|
||||
}
|
||||
}
|
370
src/main/java/org/qortal/account/SelfSponsorshipAlgoV3.java
Normal file
370
src/main/java/org/qortal/account/SelfSponsorshipAlgoV3.java
Normal file
@ -0,0 +1,370 @@
|
||||
package org.qortal.account;
|
||||
|
||||
import org.qortal.api.resource.TransactionsResource;
|
||||
import org.qortal.asset.Asset;
|
||||
import org.qortal.data.account.AccountData;
|
||||
import org.qortal.data.naming.NameData;
|
||||
import org.qortal.data.transaction.*;
|
||||
import org.qortal.repository.DataException;
|
||||
import org.qortal.repository.Repository;
|
||||
import org.qortal.transaction.Transaction.TransactionType;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class SelfSponsorshipAlgoV3 {
|
||||
|
||||
private final Repository repository;
|
||||
private final String address;
|
||||
private final AccountData accountData;
|
||||
private final long snapshotTimestampV1;
|
||||
private final long snapshotTimestampV3;
|
||||
private final boolean override;
|
||||
|
||||
private int registeredNameCount = 0;
|
||||
private int suspiciousCount = 0;
|
||||
private int suspiciousPercent = 0;
|
||||
private int consolidationCount = 0;
|
||||
private int bulkIssuanceCount = 0;
|
||||
private int recentSponsorshipCount = 0;
|
||||
|
||||
private List<RewardShareTransactionData> sponsorshipRewardShares = new ArrayList<>();
|
||||
private final Map<String, List<TransactionData>> paymentsByAddress = new HashMap<>();
|
||||
private final Set<String> sponsees = new LinkedHashSet<>();
|
||||
private Set<String> consolidatedAddresses = new LinkedHashSet<>();
|
||||
private final Set<String> zeroTransactionAddreses = new LinkedHashSet<>();
|
||||
private final Set<String> penaltyAddresses = new LinkedHashSet<>();
|
||||
|
||||
public SelfSponsorshipAlgoV3(Repository repository, String address, long snapshotTimestampV1, long snapshotTimestampV3, boolean override) throws DataException {
|
||||
this.repository = repository;
|
||||
this.address = address;
|
||||
this.accountData = this.repository.getAccountRepository().getAccount(this.address);
|
||||
this.snapshotTimestampV1 = snapshotTimestampV1;
|
||||
this.snapshotTimestampV3 = snapshotTimestampV3;
|
||||
this.override = override;
|
||||
}
|
||||
|
||||
public String getAddress() {
|
||||
return this.address;
|
||||
}
|
||||
|
||||
public Set<String> getPenaltyAddresses() {
|
||||
return this.penaltyAddresses;
|
||||
}
|
||||
|
||||
|
||||
public void run() throws DataException {
|
||||
if (this.accountData == null) {
|
||||
// Nothing to do
|
||||
return;
|
||||
}
|
||||
|
||||
this.fetchSponsorshipRewardShares();
|
||||
if (this.sponsorshipRewardShares.isEmpty()) {
|
||||
// Nothing to do
|
||||
return;
|
||||
}
|
||||
|
||||
this.findConsolidatedRewards();
|
||||
this.findBulkIssuance();
|
||||
this.findRegisteredNameCount();
|
||||
this.findRecentSponsorshipCount();
|
||||
|
||||
int score = this.calculateScore();
|
||||
if (score <= 0 && !override) {
|
||||
return;
|
||||
}
|
||||
|
||||
String newAddress = this.getDestinationAccount(this.address);
|
||||
while (newAddress != null) {
|
||||
// Found destination account
|
||||
this.penaltyAddresses.add(newAddress);
|
||||
|
||||
// Run algo for this address, but in "override" mode because it has already been flagged
|
||||
SelfSponsorshipAlgoV3 algoV3 = new SelfSponsorshipAlgoV3(this.repository, newAddress, this.snapshotTimestampV1, this.snapshotTimestampV3, true);
|
||||
algoV3.run();
|
||||
this.penaltyAddresses.addAll(algoV3.getPenaltyAddresses());
|
||||
|
||||
newAddress = this.getDestinationAccount(newAddress);
|
||||
}
|
||||
|
||||
this.penaltyAddresses.add(this.address);
|
||||
|
||||
if (this.override || this.recentSponsorshipCount < 20) {
|
||||
this.penaltyAddresses.addAll(this.consolidatedAddresses);
|
||||
this.penaltyAddresses.addAll(this.zeroTransactionAddreses);
|
||||
}
|
||||
else {
|
||||
this.penaltyAddresses.addAll(this.sponsees);
|
||||
}
|
||||
}
|
||||
|
||||
private String getDestinationAccount(String address) throws DataException {
|
||||
List<TransactionData> transferPrivsTransactions = fetchTransferPrivsForAddress(address);
|
||||
if (transferPrivsTransactions.isEmpty()) {
|
||||
// No TRANSFER_PRIVS transactions for this address
|
||||
return null;
|
||||
}
|
||||
|
||||
AccountData accountData = this.repository.getAccountRepository().getAccount(address);
|
||||
if (accountData == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
for (TransactionData transactionData : transferPrivsTransactions) {
|
||||
TransferPrivsTransactionData transferPrivsTransactionData = (TransferPrivsTransactionData) transactionData;
|
||||
if (Arrays.equals(transferPrivsTransactionData.getSenderPublicKey(), accountData.getPublicKey())) {
|
||||
return transferPrivsTransactionData.getRecipient();
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private void findConsolidatedRewards() throws DataException {
|
||||
List<String> sponseesThatSentRewards = new ArrayList<>();
|
||||
Map<String, Integer> paymentRecipients = new HashMap<>();
|
||||
|
||||
// Collect outgoing payments of each sponsee
|
||||
for (String sponseeAddress : this.sponsees) {
|
||||
|
||||
// Firstly fetch all payments for address, since the functions below depend on this data
|
||||
this.fetchPaymentsForAddress(sponseeAddress);
|
||||
|
||||
// Check if the address has zero relevant transactions
|
||||
if (this.hasZeroTransactions(sponseeAddress)) {
|
||||
this.zeroTransactionAddreses.add(sponseeAddress);
|
||||
}
|
||||
|
||||
// Get payment recipients
|
||||
List<String> allPaymentRecipients = this.fetchOutgoingPaymentRecipientsForAddress(sponseeAddress);
|
||||
if (allPaymentRecipients.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
sponseesThatSentRewards.add(sponseeAddress);
|
||||
|
||||
List<String> addressesPaidByThisSponsee = new ArrayList<>();
|
||||
for (String paymentRecipient : allPaymentRecipients) {
|
||||
if (addressesPaidByThisSponsee.contains(paymentRecipient)) {
|
||||
// We already tracked this association - don't allow multiple to stack up
|
||||
continue;
|
||||
}
|
||||
addressesPaidByThisSponsee.add(paymentRecipient);
|
||||
|
||||
// Increment count for this recipient, or initialize to 1 if not present
|
||||
if (paymentRecipients.computeIfPresent(paymentRecipient, (k, v) -> v + 1) == null) {
|
||||
paymentRecipients.put(paymentRecipient, 1);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Exclude addresses with a low number of payments
|
||||
Map<String, Integer> filteredPaymentRecipients = paymentRecipients.entrySet().stream()
|
||||
.filter(p -> p.getValue() != null && p.getValue() >= 10)
|
||||
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
|
||||
|
||||
// Now check how many sponsees have sent to this subset of addresses
|
||||
Map<String, Integer> sponseesThatConsolidatedRewards = new HashMap<>();
|
||||
for (String sponseeAddress : sponseesThatSentRewards) {
|
||||
List<String> allPaymentRecipients = this.fetchOutgoingPaymentRecipientsForAddress(sponseeAddress);
|
||||
// Remove any that aren't to one of the flagged recipients (i.e. consolidation)
|
||||
allPaymentRecipients.removeIf(r -> !filteredPaymentRecipients.containsKey(r));
|
||||
|
||||
int count = allPaymentRecipients.size();
|
||||
if (count == 0) {
|
||||
continue;
|
||||
}
|
||||
if (sponseesThatConsolidatedRewards.computeIfPresent(sponseeAddress, (k, v) -> v + count) == null) {
|
||||
sponseesThatConsolidatedRewards.put(sponseeAddress, count);
|
||||
}
|
||||
}
|
||||
|
||||
// Remove sponsees that have only sent a low number of payments to the filtered addresses
|
||||
Map<String, Integer> filteredSponseesThatConsolidatedRewards = sponseesThatConsolidatedRewards.entrySet().stream()
|
||||
.filter(p -> p.getValue() != null && p.getValue() >= 2)
|
||||
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
|
||||
|
||||
this.consolidationCount = sponseesThatConsolidatedRewards.size();
|
||||
this.consolidatedAddresses = new LinkedHashSet<>(filteredSponseesThatConsolidatedRewards.keySet());
|
||||
this.suspiciousCount = this.consolidationCount + this.zeroTransactionAddreses.size();
|
||||
this.suspiciousPercent = (int)(this.suspiciousCount / (float) this.sponsees.size() * 100);
|
||||
}
|
||||
|
||||
private void findBulkIssuance() {
|
||||
Long lastTimestamp = null;
|
||||
for (RewardShareTransactionData rewardShareTransactionData : sponsorshipRewardShares) {
|
||||
long timestamp = rewardShareTransactionData.getTimestamp();
|
||||
if (timestamp >= this.snapshotTimestampV3) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (lastTimestamp != null) {
|
||||
if (timestamp - lastTimestamp < 3*60*1000L) {
|
||||
this.bulkIssuanceCount++;
|
||||
}
|
||||
}
|
||||
lastTimestamp = timestamp;
|
||||
}
|
||||
}
|
||||
|
||||
private void findRegisteredNameCount() throws DataException {
|
||||
int registeredNameCount = 0;
|
||||
for (String sponseeAddress : sponsees) {
|
||||
List<NameData> names = repository.getNameRepository().getNamesByOwner(sponseeAddress);
|
||||
for (NameData name : names) {
|
||||
if (name.getRegistered() < this.snapshotTimestampV3) {
|
||||
registeredNameCount++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
this.registeredNameCount = registeredNameCount;
|
||||
}
|
||||
|
||||
private void findRecentSponsorshipCount() {
|
||||
int recentSponsorshipCount = 0;
|
||||
for (RewardShareTransactionData rewardShare : sponsorshipRewardShares) {
|
||||
if (rewardShare.getTimestamp() >= this.snapshotTimestampV1) {
|
||||
recentSponsorshipCount++;
|
||||
}
|
||||
}
|
||||
this.recentSponsorshipCount = recentSponsorshipCount;
|
||||
}
|
||||
|
||||
private int calculateScore() {
|
||||
final int suspiciousMultiplier = (this.suspiciousCount >= 100) ? this.suspiciousPercent : 1;
|
||||
final int nameMultiplier = (this.sponsees.size() >= 25 && this.registeredNameCount <= 1) ? 21 :
|
||||
(this.sponsees.size() >= 15 && this.registeredNameCount <= 1) ? 11 :
|
||||
(this.sponsees.size() >= 5 && this.registeredNameCount <= 1) ? 5 : 1;
|
||||
final int consolidationMultiplier = Math.max(this.consolidationCount, 1);
|
||||
final int bulkIssuanceMultiplier = Math.max(this.bulkIssuanceCount / 2, 1);
|
||||
final int offset = 20;
|
||||
return suspiciousMultiplier * nameMultiplier * consolidationMultiplier * bulkIssuanceMultiplier - offset;
|
||||
}
|
||||
|
||||
private void fetchSponsorshipRewardShares() throws DataException {
|
||||
List<RewardShareTransactionData> sponsorshipRewardShares = new ArrayList<>();
|
||||
|
||||
// Define relevant transactions
|
||||
List<TransactionType> txTypes = List.of(TransactionType.REWARD_SHARE);
|
||||
List<TransactionData> transactionDataList = fetchTransactions(repository, txTypes, this.address, false);
|
||||
transactionDataList.removeIf(t -> t.getTimestamp() <= this.snapshotTimestampV1 || t.getTimestamp() >= this.snapshotTimestampV3);
|
||||
|
||||
for (TransactionData transactionData : transactionDataList) {
|
||||
if (transactionData.getType() != TransactionType.REWARD_SHARE) {
|
||||
continue;
|
||||
}
|
||||
|
||||
RewardShareTransactionData rewardShareTransactionData = (RewardShareTransactionData) transactionData;
|
||||
|
||||
// Skip removals
|
||||
if (rewardShareTransactionData.getSharePercent() < 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Skip if not sponsored by this account
|
||||
if (!Arrays.equals(rewardShareTransactionData.getCreatorPublicKey(), accountData.getPublicKey())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Skip self shares
|
||||
if (Objects.equals(rewardShareTransactionData.getRecipient(), this.address)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
boolean duplicateFound = false;
|
||||
for (RewardShareTransactionData existingRewardShare : sponsorshipRewardShares) {
|
||||
if (Objects.equals(existingRewardShare.getRecipient(), rewardShareTransactionData.getRecipient())) {
|
||||
// Duplicate
|
||||
duplicateFound = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!duplicateFound) {
|
||||
sponsorshipRewardShares.add(rewardShareTransactionData);
|
||||
this.sponsees.add(rewardShareTransactionData.getRecipient());
|
||||
}
|
||||
}
|
||||
|
||||
this.sponsorshipRewardShares = sponsorshipRewardShares;
|
||||
}
|
||||
|
||||
private List<TransactionData> fetchTransferPrivsForAddress(String address) throws DataException {
|
||||
return fetchTransactions(repository,
|
||||
List.of(TransactionType.TRANSFER_PRIVS),
|
||||
address, true);
|
||||
}
|
||||
|
||||
private void fetchPaymentsForAddress(String address) throws DataException {
|
||||
List<TransactionData> payments = fetchTransactions(repository,
|
||||
Arrays.asList(TransactionType.PAYMENT, TransactionType.TRANSFER_ASSET),
|
||||
address, false);
|
||||
this.paymentsByAddress.put(address, payments);
|
||||
}
|
||||
|
||||
private List<String> fetchOutgoingPaymentRecipientsForAddress(String address) {
|
||||
List<String> outgoingPaymentRecipients = new ArrayList<>();
|
||||
|
||||
List<TransactionData> transactionDataList = this.paymentsByAddress.get(address);
|
||||
if (transactionDataList == null) transactionDataList = new ArrayList<>();
|
||||
transactionDataList.removeIf(t -> t.getTimestamp() <= this.snapshotTimestampV1 || t.getTimestamp() >= this.snapshotTimestampV3);
|
||||
for (TransactionData transactionData : transactionDataList) {
|
||||
switch (transactionData.getType()) {
|
||||
|
||||
case PAYMENT:
|
||||
PaymentTransactionData paymentTransactionData = (PaymentTransactionData) transactionData;
|
||||
if (!Objects.equals(paymentTransactionData.getRecipient(), address)) {
|
||||
// Outgoing payment from this account
|
||||
outgoingPaymentRecipients.add(paymentTransactionData.getRecipient());
|
||||
}
|
||||
break;
|
||||
|
||||
case TRANSFER_ASSET:
|
||||
TransferAssetTransactionData transferAssetTransactionData = (TransferAssetTransactionData) transactionData;
|
||||
if (transferAssetTransactionData.getAssetId() == Asset.QORT) {
|
||||
if (!Objects.equals(transferAssetTransactionData.getRecipient(), address)) {
|
||||
// Outgoing payment from this account
|
||||
outgoingPaymentRecipients.add(transferAssetTransactionData.getRecipient());
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return outgoingPaymentRecipients;
|
||||
}
|
||||
|
||||
private boolean hasZeroTransactions(String address) {
|
||||
List<TransactionData> transactionDataList = this.paymentsByAddress.get(address);
|
||||
if (transactionDataList == null) {
|
||||
return true;
|
||||
}
|
||||
transactionDataList.removeIf(t -> t.getTimestamp() <= this.snapshotTimestampV1 || t.getTimestamp() >= this.snapshotTimestampV3);
|
||||
return transactionDataList.size() == 0;
|
||||
}
|
||||
|
||||
private static List<TransactionData> fetchTransactions(Repository repository, List<TransactionType> txTypes, String address, boolean reverse) throws DataException {
|
||||
// Fetch all relevant transactions for this account
|
||||
List<byte[]> signatures = repository.getTransactionRepository()
|
||||
.getSignaturesMatchingCriteria(null, null, null, txTypes,
|
||||
null, null, address, TransactionsResource.ConfirmationStatus.CONFIRMED,
|
||||
null, null, reverse);
|
||||
|
||||
List<TransactionData> transactionDataList = new ArrayList<>();
|
||||
|
||||
for (byte[] signature : signatures) {
|
||||
// Fetch transaction data
|
||||
TransactionData transactionData = repository.getTransactionRepository().fromSignature(signature);
|
||||
if (transactionData == null) {
|
||||
continue;
|
||||
}
|
||||
transactionDataList.add(transactionData);
|
||||
}
|
||||
|
||||
return transactionDataList;
|
||||
}
|
||||
}
|
@ -1061,8 +1061,10 @@ public class Block {
|
||||
return ValidationResult.MINTER_NOT_ACCEPTED;
|
||||
|
||||
long expectedTimestamp = calcTimestamp(parentBlockData, this.blockData.getMinterPublicKey(), minterLevel);
|
||||
if (this.blockData.getTimestamp() != expectedTimestamp)
|
||||
if (this.blockData.getTimestamp() != expectedTimestamp) {
|
||||
LOGGER.debug(String.format("timestamp mismatch! block had %s but we expected %s", this.blockData.getTimestamp(), expectedTimestamp));
|
||||
return ValidationResult.TIMESTAMP_INCORRECT;
|
||||
}
|
||||
|
||||
return ValidationResult.OK;
|
||||
}
|
||||
@ -1556,6 +1558,14 @@ public class Block {
|
||||
if (this.blockData.getHeight() == BlockChain.getInstance().getSelfSponsorshipAlgoV1Height()) {
|
||||
SelfSponsorshipAlgoV1Block.processAccountPenalties(this);
|
||||
}
|
||||
|
||||
if (this.blockData.getHeight() == BlockChain.getInstance().getSelfSponsorshipAlgoV2Height()) {
|
||||
SelfSponsorshipAlgoV2Block.processAccountPenalties(this);
|
||||
}
|
||||
|
||||
if (this.blockData.getHeight() == BlockChain.getInstance().getSelfSponsorshipAlgoV3Height()) {
|
||||
SelfSponsorshipAlgoV3Block.processAccountPenalties(this);
|
||||
}
|
||||
}
|
||||
|
||||
// We're about to (test-)process a batch of transactions,
|
||||
@ -1849,6 +1859,14 @@ public class Block {
|
||||
SelfSponsorshipAlgoV1Block.orphanAccountPenalties(this);
|
||||
}
|
||||
|
||||
if (this.blockData.getHeight() == BlockChain.getInstance().getSelfSponsorshipAlgoV2Height()) {
|
||||
SelfSponsorshipAlgoV2Block.orphanAccountPenalties(this);
|
||||
}
|
||||
|
||||
if (this.blockData.getHeight() == BlockChain.getInstance().getSelfSponsorshipAlgoV3Height()) {
|
||||
SelfSponsorshipAlgoV3Block.orphanAccountPenalties(this);
|
||||
}
|
||||
|
||||
// Account levels and block rewards are only processed/orphaned on block reward distribution blocks
|
||||
if (this.isRewardDistributionBlock()) {
|
||||
// Block rewards, including transaction fees, removed after transactions undone
|
||||
|
@ -74,6 +74,7 @@ public class BlockChain {
|
||||
onlineAccountMinterLevelValidationHeight,
|
||||
selfSponsorshipAlgoV1Height,
|
||||
selfSponsorshipAlgoV2Height,
|
||||
selfSponsorshipAlgoV3Height,
|
||||
feeValidationFixTimestamp,
|
||||
chatReferenceTimestamp,
|
||||
arbitraryOptionalFeeTimestamp,
|
||||
@ -216,6 +217,12 @@ public class BlockChain {
|
||||
/** Snapshot timestamp for self sponsorship algo V2 */
|
||||
private long selfSponsorshipAlgoV2SnapshotTimestamp;
|
||||
|
||||
/** Snapshot timestamp for self sponsorship algo V3 */
|
||||
private long selfSponsorshipAlgoV3SnapshotTimestamp;
|
||||
|
||||
/** Reference timestamp for self sponsorship algo V1 block height */
|
||||
private long referenceTimestampBlock;
|
||||
|
||||
/** Feature-trigger timestamp to modify behaviour of various transactions that support mempow */
|
||||
private long mempowTransactionUpdatesTimestamp;
|
||||
|
||||
@ -418,6 +425,15 @@ public class BlockChain {
|
||||
return this.selfSponsorshipAlgoV2SnapshotTimestamp;
|
||||
}
|
||||
|
||||
// Self sponsorship algo V3
|
||||
public long getSelfSponsorshipAlgoV3SnapshotTimestamp() {
|
||||
return this.selfSponsorshipAlgoV3SnapshotTimestamp;
|
||||
}
|
||||
|
||||
// Self sponsorship algo V3
|
||||
public long getReferenceTimestampBlock() {
|
||||
return this.referenceTimestampBlock;
|
||||
}
|
||||
// Feature-trigger timestamp to modify behaviour of various transactions that support mempow
|
||||
public long getMemPoWTransactionUpdatesTimestamp() {
|
||||
return this.mempowTransactionUpdatesTimestamp;
|
||||
@ -562,6 +578,10 @@ public class BlockChain {
|
||||
return this.featureTriggers.get(FeatureTrigger.selfSponsorshipAlgoV2Height.name()).intValue();
|
||||
}
|
||||
|
||||
public int getSelfSponsorshipAlgoV3Height() {
|
||||
return this.featureTriggers.get(FeatureTrigger.selfSponsorshipAlgoV3Height.name()).intValue();
|
||||
}
|
||||
|
||||
public long getOnlineAccountMinterLevelValidationHeight() {
|
||||
return this.featureTriggers.get(FeatureTrigger.onlineAccountMinterLevelValidationHeight.name()).intValue();
|
||||
}
|
||||
|
143
src/main/java/org/qortal/block/SelfSponsorshipAlgoV2Block.java
Normal file
143
src/main/java/org/qortal/block/SelfSponsorshipAlgoV2Block.java
Normal file
@ -0,0 +1,143 @@
|
||||
package org.qortal.block;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.qortal.account.SelfSponsorshipAlgoV2;
|
||||
import org.qortal.api.model.AccountPenaltyStats;
|
||||
import org.qortal.crypto.Crypto;
|
||||
import org.qortal.data.account.AccountData;
|
||||
import org.qortal.data.account.AccountPenaltyData;
|
||||
import org.qortal.repository.DataException;
|
||||
import org.qortal.repository.Repository;
|
||||
import org.qortal.utils.Base58;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Self Sponsorship AlgoV2 Block
|
||||
* <p>
|
||||
* Selected block for the initial run on the "self sponsorship detection algorithm"
|
||||
*/
|
||||
public final class SelfSponsorshipAlgoV2Block {
|
||||
|
||||
private static final Logger LOGGER = LogManager.getLogger(SelfSponsorshipAlgoV2Block.class);
|
||||
|
||||
private SelfSponsorshipAlgoV2Block() {
|
||||
/* Do not instantiate */
|
||||
}
|
||||
|
||||
public static void processAccountPenalties(Block block) throws DataException {
|
||||
LOGGER.info("Process Self Sponsorship Algo V2 - this will take a while...");
|
||||
logPenaltyStats(block.repository);
|
||||
long startTime = System.currentTimeMillis();
|
||||
Set<AccountPenaltyData> penalties = getAccountPenalties(block.repository, -5000000);
|
||||
block.repository.getAccountRepository().updateBlocksMintedPenalties(penalties);
|
||||
long totalTime = System.currentTimeMillis() - startTime;
|
||||
String hash = getHash(penalties.stream().map(p -> p.getAddress()).collect(Collectors.toList()));
|
||||
LOGGER.info("{} penalty addresses processed (hash: {}). Total time taken: {} seconds", penalties.size(), hash, (int)(totalTime / 1000.0f));
|
||||
logPenaltyStats(block.repository);
|
||||
|
||||
int updatedCount = updateAccountLevels(block.repository, penalties);
|
||||
LOGGER.info("Account levels updated for {} penalty addresses", updatedCount);
|
||||
}
|
||||
|
||||
public static void orphanAccountPenalties(Block block) throws DataException {
|
||||
LOGGER.info("Orphan Self Sponsorship Algo V2 - this will take a while...");
|
||||
logPenaltyStats(block.repository);
|
||||
long startTime = System.currentTimeMillis();
|
||||
Set<AccountPenaltyData> penalties = getAccountPenalties(block.repository, 5000000);
|
||||
block.repository.getAccountRepository().updateBlocksMintedPenalties(penalties);
|
||||
long totalTime = System.currentTimeMillis() - startTime;
|
||||
String hash = getHash(penalties.stream().map(p -> p.getAddress()).collect(Collectors.toList()));
|
||||
LOGGER.info("{} penalty addresses orphaned (hash: {}). Total time taken: {} seconds", penalties.size(), hash, (int)(totalTime / 1000.0f));
|
||||
logPenaltyStats(block.repository);
|
||||
|
||||
int updatedCount = updateAccountLevels(block.repository, penalties);
|
||||
LOGGER.info("Account levels updated for {} penalty addresses", updatedCount);
|
||||
}
|
||||
|
||||
private static Set<AccountPenaltyData> getAccountPenalties(Repository repository, int penalty) throws DataException {
|
||||
Set<AccountPenaltyData> penalties = new LinkedHashSet<>();
|
||||
List<AccountData> penalizedAddresses = repository.getAccountRepository().getPenaltyAccounts();
|
||||
List<String> assetAddresses = repository.getTransactionRepository().getConfirmedTransferAssetCreators();
|
||||
|
||||
for (AccountData penalizedAddress : penalizedAddresses) {
|
||||
//System.out.println(String.format("address: %s", address));
|
||||
SelfSponsorshipAlgoV2 selfSponsorshipAlgoV2 = new SelfSponsorshipAlgoV2(repository, penalizedAddress.getAddress(), false);
|
||||
selfSponsorshipAlgoV2.run();
|
||||
//System.out.println(String.format("Penalty addresses: %d", selfSponsorshipAlgoV2.getPenaltyAddresses().size()));
|
||||
for (String penaltyAddress : selfSponsorshipAlgoV2.getPenaltyAddresses()) {
|
||||
penalties.add(new AccountPenaltyData(penaltyAddress, penalty));
|
||||
}
|
||||
}
|
||||
|
||||
for (String assetAddress : assetAddresses) {
|
||||
//System.out.println(String.format("address: %s", address));
|
||||
SelfSponsorshipAlgoV2 selfSponsorshipAlgoV2 = new SelfSponsorshipAlgoV2(repository, assetAddress, true);
|
||||
selfSponsorshipAlgoV2.run();
|
||||
//System.out.println(String.format("Penalty addresses: %d", selfSponsorshipAlgoV2.getPenaltyAddresses().size()));
|
||||
for (String penaltyAddress : selfSponsorshipAlgoV2.getPenaltyAddresses()) {
|
||||
penalties.add(new AccountPenaltyData(penaltyAddress, penalty));
|
||||
}
|
||||
}
|
||||
|
||||
return penalties;
|
||||
}
|
||||
|
||||
private static int updateAccountLevels(Repository repository, Set<AccountPenaltyData> accountPenalties) throws DataException {
|
||||
final List<Integer> cumulativeBlocksByLevel = BlockChain.getInstance().getCumulativeBlocksByLevel();
|
||||
final int maximumLevel = cumulativeBlocksByLevel.size() - 1;
|
||||
|
||||
int updatedCount = 0;
|
||||
|
||||
for (AccountPenaltyData penaltyData : accountPenalties) {
|
||||
AccountData accountData = repository.getAccountRepository().getAccount(penaltyData.getAddress());
|
||||
final int effectiveBlocksMinted = accountData.getBlocksMinted() + accountData.getBlocksMintedAdjustment() + accountData.getBlocksMintedPenalty();
|
||||
|
||||
// Shortcut for penalties
|
||||
if (effectiveBlocksMinted < 0) {
|
||||
accountData.setLevel(0);
|
||||
repository.getAccountRepository().setLevel(accountData);
|
||||
updatedCount++;
|
||||
LOGGER.trace(() -> String.format("Block minter %s dropped to level %d", accountData.getAddress(), accountData.getLevel()));
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int newLevel = maximumLevel; newLevel >= 0; --newLevel) {
|
||||
if (effectiveBlocksMinted >= cumulativeBlocksByLevel.get(newLevel)) {
|
||||
accountData.setLevel(newLevel);
|
||||
repository.getAccountRepository().setLevel(accountData);
|
||||
updatedCount++;
|
||||
LOGGER.trace(() -> String.format("Block minter %s increased to level %d", accountData.getAddress(), accountData.getLevel()));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return updatedCount;
|
||||
}
|
||||
|
||||
private static void logPenaltyStats(Repository repository) {
|
||||
try {
|
||||
LOGGER.info(getPenaltyStats(repository));
|
||||
|
||||
} catch (DataException e) {}
|
||||
}
|
||||
|
||||
private static AccountPenaltyStats getPenaltyStats(Repository repository) throws DataException {
|
||||
List<AccountData> accounts = repository.getAccountRepository().getPenaltyAccounts();
|
||||
return AccountPenaltyStats.fromAccounts(accounts);
|
||||
}
|
||||
|
||||
public static String getHash(List<String> penaltyAddresses) {
|
||||
if (penaltyAddresses == null || penaltyAddresses.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
Collections.sort(penaltyAddresses);
|
||||
return Base58.encode(Crypto.digest(StringUtils.join(penaltyAddresses).getBytes(StandardCharsets.UTF_8)));
|
||||
}
|
||||
|
||||
}
|
136
src/main/java/org/qortal/block/SelfSponsorshipAlgoV3Block.java
Normal file
136
src/main/java/org/qortal/block/SelfSponsorshipAlgoV3Block.java
Normal file
@ -0,0 +1,136 @@
|
||||
package org.qortal.block;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.qortal.account.SelfSponsorshipAlgoV3;
|
||||
import org.qortal.api.model.AccountPenaltyStats;
|
||||
import org.qortal.crypto.Crypto;
|
||||
import org.qortal.data.account.AccountData;
|
||||
import org.qortal.data.account.AccountPenaltyData;
|
||||
import org.qortal.repository.DataException;
|
||||
import org.qortal.repository.Repository;
|
||||
import org.qortal.utils.Base58;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Self Sponsorship AlgoV3 Block
|
||||
* <p>
|
||||
* Selected block for the initial run on the "self sponsorship detection algorithm"
|
||||
*/
|
||||
public final class SelfSponsorshipAlgoV3Block {
|
||||
|
||||
private static final Logger LOGGER = LogManager.getLogger(SelfSponsorshipAlgoV3Block.class);
|
||||
|
||||
private SelfSponsorshipAlgoV3Block() {
|
||||
/* Do not instantiate */
|
||||
}
|
||||
|
||||
public static void processAccountPenalties(Block block) throws DataException {
|
||||
LOGGER.info("Process Self Sponsorship Algo V3 - this will take a while...");
|
||||
logPenaltyStats(block.repository);
|
||||
long startTime = System.currentTimeMillis();
|
||||
Set<AccountPenaltyData> penalties = getAccountPenalties(block.repository, -5000000);
|
||||
block.repository.getAccountRepository().updateBlocksMintedPenalties(penalties);
|
||||
long totalTime = System.currentTimeMillis() - startTime;
|
||||
String hash = getHash(penalties.stream().map(p -> p.getAddress()).collect(Collectors.toList()));
|
||||
LOGGER.info("{} penalty addresses processed (hash: {}). Total time taken: {} seconds", penalties.size(), hash, (int)(totalTime / 1000.0f));
|
||||
logPenaltyStats(block.repository);
|
||||
|
||||
int updatedCount = updateAccountLevels(block.repository, penalties);
|
||||
LOGGER.info("Account levels updated for {} penalty addresses", updatedCount);
|
||||
}
|
||||
|
||||
public static void orphanAccountPenalties(Block block) throws DataException {
|
||||
LOGGER.info("Orphan Self Sponsorship Algo V3 - this will take a while...");
|
||||
logPenaltyStats(block.repository);
|
||||
long startTime = System.currentTimeMillis();
|
||||
Set<AccountPenaltyData> penalties = getAccountPenalties(block.repository, 5000000);
|
||||
block.repository.getAccountRepository().updateBlocksMintedPenalties(penalties);
|
||||
long totalTime = System.currentTimeMillis() - startTime;
|
||||
String hash = getHash(penalties.stream().map(p -> p.getAddress()).collect(Collectors.toList()));
|
||||
LOGGER.info("{} penalty addresses orphaned (hash: {}). Total time taken: {} seconds", penalties.size(), hash, (int)(totalTime / 1000.0f));
|
||||
logPenaltyStats(block.repository);
|
||||
|
||||
int updatedCount = updateAccountLevels(block.repository, penalties);
|
||||
LOGGER.info("Account levels updated for {} penalty addresses", updatedCount);
|
||||
}
|
||||
|
||||
public static Set<AccountPenaltyData> getAccountPenalties(Repository repository, int penalty) throws DataException {
|
||||
final long snapshotTimestampV1 = BlockChain.getInstance().getSelfSponsorshipAlgoV1SnapshotTimestamp();
|
||||
final long snapshotTimestampV3 = BlockChain.getInstance().getSelfSponsorshipAlgoV3SnapshotTimestamp();
|
||||
Set<AccountPenaltyData> penalties = new LinkedHashSet<>();
|
||||
List<String> addresses = repository.getTransactionRepository().getConfirmedRewardShareCreatorsExcludingSelfShares();
|
||||
for (String address : addresses) {
|
||||
//System.out.println(String.format("address: %s", address));
|
||||
SelfSponsorshipAlgoV3 selfSponsorshipAlgoV3 = new SelfSponsorshipAlgoV3(repository, address, snapshotTimestampV1, snapshotTimestampV3, false);
|
||||
selfSponsorshipAlgoV3.run();
|
||||
//System.out.println(String.format("Penalty addresses: %d", selfSponsorshipAlgoV3.getPenaltyAddresses().size()));
|
||||
|
||||
for (String penaltyAddress : selfSponsorshipAlgoV3.getPenaltyAddresses()) {
|
||||
penalties.add(new AccountPenaltyData(penaltyAddress, penalty));
|
||||
}
|
||||
}
|
||||
return penalties;
|
||||
}
|
||||
|
||||
private static int updateAccountLevels(Repository repository, Set<AccountPenaltyData> accountPenalties) throws DataException {
|
||||
final List<Integer> cumulativeBlocksByLevel = BlockChain.getInstance().getCumulativeBlocksByLevel();
|
||||
final int maximumLevel = cumulativeBlocksByLevel.size() - 1;
|
||||
|
||||
int updatedCount = 0;
|
||||
|
||||
for (AccountPenaltyData penaltyData : accountPenalties) {
|
||||
AccountData accountData = repository.getAccountRepository().getAccount(penaltyData.getAddress());
|
||||
final int effectiveBlocksMinted = accountData.getBlocksMinted() + accountData.getBlocksMintedAdjustment() + accountData.getBlocksMintedPenalty();
|
||||
|
||||
// Shortcut for penalties
|
||||
if (effectiveBlocksMinted < 0) {
|
||||
accountData.setLevel(0);
|
||||
repository.getAccountRepository().setLevel(accountData);
|
||||
updatedCount++;
|
||||
LOGGER.trace(() -> String.format("Block minter %s dropped to level %d", accountData.getAddress(), accountData.getLevel()));
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int newLevel = maximumLevel; newLevel >= 0; --newLevel) {
|
||||
if (effectiveBlocksMinted >= cumulativeBlocksByLevel.get(newLevel)) {
|
||||
accountData.setLevel(newLevel);
|
||||
repository.getAccountRepository().setLevel(accountData);
|
||||
updatedCount++;
|
||||
LOGGER.trace(() -> String.format("Block minter %s increased to level %d", accountData.getAddress(), accountData.getLevel()));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return updatedCount;
|
||||
}
|
||||
|
||||
private static void logPenaltyStats(Repository repository) {
|
||||
try {
|
||||
LOGGER.info(getPenaltyStats(repository));
|
||||
|
||||
} catch (DataException e) {}
|
||||
}
|
||||
|
||||
private static AccountPenaltyStats getPenaltyStats(Repository repository) throws DataException {
|
||||
List<AccountData> accounts = repository.getAccountRepository().getPenaltyAccounts();
|
||||
return AccountPenaltyStats.fromAccounts(accounts);
|
||||
}
|
||||
|
||||
public static String getHash(List<String> penaltyAddresses) {
|
||||
if (penaltyAddresses == null || penaltyAddresses.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
Collections.sort(penaltyAddresses);
|
||||
return Base58.encode(Crypto.digest(StringUtils.join(penaltyAddresses).getBytes(StandardCharsets.UTF_8)));
|
||||
}
|
||||
|
||||
}
|
@ -51,7 +51,12 @@ public class PirateChain extends Bitcoiny {
|
||||
public Collection<Server> getServers() {
|
||||
return Arrays.asList(
|
||||
// Servers chosen on NO BASIS WHATSOEVER from various sources!
|
||||
new Server("lightd.pirate.black", Server.ConnectionType.SSL, 443)
|
||||
new Server("lightd.pirate.black", Server.ConnectionType.SSL, 443),
|
||||
new Server("wallet-arrr1.qortal.online", Server.ConnectionType.SSL, 443),
|
||||
new Server("wallet-arrr2.qortal.online", Server.ConnectionType.SSL, 443),
|
||||
new Server("wallet-arrr3.qortal.online", Server.ConnectionType.SSL, 443),
|
||||
new Server("wallet-arrr4.qortal.online", Server.ConnectionType.SSL, 443),
|
||||
new Server("wallet-arrr5.qortal.online", Server.ConnectionType.SSL, 443)
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -205,6 +205,15 @@ public interface TransactionRepository {
|
||||
*/
|
||||
public List<String> getConfirmedRewardShareCreatorsExcludingSelfShares() throws DataException;
|
||||
|
||||
/**
|
||||
* Returns list of transfer asset transaction creators.
|
||||
* This uses confirmed transactions only.
|
||||
*
|
||||
* @return
|
||||
* @throws DataException
|
||||
*/
|
||||
public List<String> getConfirmedTransferAssetCreators() throws DataException;
|
||||
|
||||
/**
|
||||
* Returns list of transactions pending approval, with optional txGgroupId filtering.
|
||||
* <p>
|
||||
|
@ -1043,6 +1043,33 @@ public class HSQLDBTransactionRepository implements TransactionRepository {
|
||||
}
|
||||
}
|
||||
|
||||
public List<String> getConfirmedTransferAssetCreators() throws DataException {
|
||||
List<String> transferAssetCreators = new ArrayList<>();
|
||||
|
||||
String sql = "SELECT account "
|
||||
+ "FROM TransferAssetTransactions "
|
||||
+ "JOIN Accounts ON Accounts.public_key = TransferAssetTransactions.sender "
|
||||
+ "JOIN Transactions ON Transactions.signature = TransferAssetTransactions.signature "
|
||||
+ "WHERE block_height IS NOT NULL AND TransferAssetTransactions.recipient != Accounts.account "
|
||||
+ "GROUP BY account "
|
||||
+ "ORDER BY account";
|
||||
|
||||
try (ResultSet resultSet = this.repository.checkedExecute(sql)) {
|
||||
if (resultSet == null)
|
||||
return transferAssetCreators;
|
||||
|
||||
do {
|
||||
String address = resultSet.getString(1);
|
||||
|
||||
transferAssetCreators.add(address);
|
||||
} while (resultSet.next());
|
||||
|
||||
return transferAssetCreators;
|
||||
} catch (SQLException e) {
|
||||
throw new DataException("Unable to fetch transfer asset from repository", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<TransactionData> getApprovalPendingTransactions(Integer txGroupId, Integer limit, Integer offset, Boolean reverse) throws DataException {
|
||||
StringBuilder sql = new StringBuilder(512);
|
||||
|
@ -211,7 +211,7 @@ public class Settings {
|
||||
public long recoveryModeTimeout = 9999999999999L;
|
||||
|
||||
/** Minimum peer version number required in order to sync with them */
|
||||
private String minPeerVersion = "4.4.2";
|
||||
private String minPeerVersion = "4.5.0";
|
||||
/** Whether to allow connections with peers below minPeerVersion
|
||||
* If true, we won't sync with them but they can still sync with us, and will show in the peers list
|
||||
* If false, sync will be blocked both ways, and they will not appear in the peers list */
|
||||
|
@ -54,6 +54,10 @@ public class RegisterNameTransaction extends Transaction {
|
||||
Account registrant = getRegistrant();
|
||||
String name = this.registerNameTransactionData.getName();
|
||||
|
||||
int blockchainHeight = this.repository.getBlockRepository().getBlockchainHeight();
|
||||
final int start = BlockChain.getInstance().getSelfSponsorshipAlgoV2Height() - 1180;
|
||||
final int end = BlockChain.getInstance().getSelfSponsorshipAlgoV3Height();
|
||||
|
||||
// Check name size bounds
|
||||
int nameLength = Utf8.encodedLength(name);
|
||||
if (nameLength < Name.MIN_NAME_SIZE || nameLength > Name.MAX_NAME_SIZE)
|
||||
@ -76,6 +80,10 @@ public class RegisterNameTransaction extends Transaction {
|
||||
if (registrant.getConfirmedBalance(Asset.QORT) < this.registerNameTransactionData.getFee())
|
||||
return ValidationResult.NO_BALANCE;
|
||||
|
||||
// Check if we are on algo runs
|
||||
if (blockchainHeight >= start && blockchainHeight <= end)
|
||||
return ValidationResult.TEMPORARY_DISABLED;
|
||||
|
||||
return ValidationResult.OK;
|
||||
}
|
||||
|
||||
|
@ -183,12 +183,28 @@ public class RewardShareTransaction extends Transaction {
|
||||
|
||||
@Override
|
||||
public boolean isConfirmableAtHeight(int height) {
|
||||
final int startV2 = BlockChain.getInstance().getSelfSponsorshipAlgoV2Height() - 15;
|
||||
final int startV3 = BlockChain.getInstance().getSelfSponsorshipAlgoV3Height() - 15;
|
||||
final int endV2 = BlockChain.getInstance().getSelfSponsorshipAlgoV2Height() + 10;
|
||||
final int endV3 = BlockChain.getInstance().getSelfSponsorshipAlgoV3Height() + 10;
|
||||
|
||||
if (height >= BlockChain.getInstance().getUnconfirmableRewardSharesHeight()) {
|
||||
// Not confirmable in online accounts or distribution blocks
|
||||
if (Block.isOnlineAccountsBlock(height) || Block.isBatchRewardDistributionBlock(height)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (height >= startV2 && height <= endV2) {
|
||||
// Not confirmable on algo V2 run
|
||||
return false;
|
||||
}
|
||||
|
||||
if (height >= startV3 && height <= endV3) {
|
||||
// Not confirmable on algo V3 run
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -173,6 +173,7 @@ public abstract class Transaction {
|
||||
INVALID_OPTION_LENGTH(20),
|
||||
DUPLICATE_OPTION(21),
|
||||
POLL_ALREADY_EXISTS(22),
|
||||
POLL_ALREADY_HAS_VOTES(23),
|
||||
POLL_DOES_NOT_EXIST(24),
|
||||
POLL_OPTION_DOES_NOT_EXIST(25),
|
||||
ALREADY_VOTED_FOR_THAT_OPTION(26),
|
||||
@ -247,6 +248,7 @@ public abstract class Transaction {
|
||||
GROUP_APPROVAL_REQUIRED(98),
|
||||
ACCOUNT_NOT_TRANSFERABLE(99),
|
||||
TRANSFER_PRIVS_DISABLED(100),
|
||||
TEMPORARY_DISABLED(101),
|
||||
INVALID_BUT_OK(999),
|
||||
NOT_YET_RELEASED(1000),
|
||||
NOT_SUPPORTED(1001);
|
||||
|
@ -1,6 +1,7 @@
|
||||
package org.qortal.transaction;
|
||||
|
||||
import org.qortal.account.Account;
|
||||
import org.qortal.block.BlockChain;
|
||||
import org.qortal.data.PaymentData;
|
||||
import org.qortal.data.transaction.TransactionData;
|
||||
import org.qortal.data.transaction.TransferAssetTransactionData;
|
||||
@ -51,6 +52,14 @@ public class TransferAssetTransaction extends Transaction {
|
||||
|
||||
@Override
|
||||
public ValidationResult isValid() throws DataException {
|
||||
int blockchainHeight = this.repository.getBlockRepository().getBlockchainHeight();
|
||||
final int start = BlockChain.getInstance().getSelfSponsorshipAlgoV2Height();
|
||||
final int end = BlockChain.getInstance().getSelfSponsorshipAlgoV3Height();
|
||||
|
||||
// Check if we are on algo runs
|
||||
if (blockchainHeight >= start && blockchainHeight <= end)
|
||||
return ValidationResult.ASSET_NOT_SPENDABLE;
|
||||
|
||||
// Wrap asset transfer as a payment and delegate final payment checks to Payment class
|
||||
return new Payment(this.repository).isValid(this.transferAssetTransactionData.getSenderPublicKey(), getPaymentData(), this.transferAssetTransactionData.getFee());
|
||||
}
|
||||
|
@ -30,7 +30,9 @@
|
||||
"onlineAccountSignaturesMaxLifetime": 86400000,
|
||||
"onlineAccountsModulusV2Timestamp": 1659801600000,
|
||||
"selfSponsorshipAlgoV1SnapshotTimestamp": 1670230000000,
|
||||
"selfSponsorshipAlgoV2SnapshotTimestamp": 1706745600000,
|
||||
"selfSponsorshipAlgoV2SnapshotTimestamp": 1708360200000,
|
||||
"selfSponsorshipAlgoV3SnapshotTimestamp": 1708432200000,
|
||||
"referenceTimestampBlock": 1670684455220,
|
||||
"mempowTransactionUpdatesTimestamp": 1693558800000,
|
||||
"blockRewardBatchStartHeight": 1508000,
|
||||
"blockRewardBatchSize": 1000,
|
||||
@ -94,7 +96,8 @@
|
||||
"increaseOnlineAccountsDifficultyTimestamp": 9999999999999,
|
||||
"onlineAccountMinterLevelValidationHeight": 1092000,
|
||||
"selfSponsorshipAlgoV1Height": 1092400,
|
||||
"selfSponsorshipAlgoV2Height": 9999999,
|
||||
"selfSponsorshipAlgoV2Height": 1611200,
|
||||
"selfSponsorshipAlgoV3Height": 1612200,
|
||||
"feeValidationFixTimestamp": 1671918000000,
|
||||
"chatReferenceTimestamp": 1674316800000,
|
||||
"arbitraryOptionalFeeTimestamp": 1680278400000,
|
||||
|
@ -195,3 +195,5 @@ TRANSACTION_UNKNOWN = Transaktion unbekannt
|
||||
TX_GROUP_ID_MISMATCH = die Gruppen-ID der Transaktion stimmt nicht überein
|
||||
|
||||
TRANSFER_PRIVS_DISABLED = Übertragungsberechtigungen deaktiviert
|
||||
|
||||
TEMPORARY_DISABLED = Namensregistrierung vorübergehend deaktiviert
|
||||
|
@ -195,3 +195,5 @@ TRANSACTION_UNKNOWN = transaction unknown
|
||||
TX_GROUP_ID_MISMATCH = transaction's group ID does not match
|
||||
|
||||
TRANSFER_PRIVS_DISABLED = transfer privileges disabled
|
||||
|
||||
TEMPORARY_DISABLED = Name registration temporary disabled
|
||||
|
@ -195,3 +195,5 @@ TRANSACTION_UNKNOWN = transacción desconocida
|
||||
TX_GROUP_ID_MISMATCH = el ID de grupo de la transacción no coincide
|
||||
|
||||
TRANSFER_PRIVS_DISABLED = privilegios de transferencia deshabilitados
|
||||
|
||||
TEMPORARY_DISABLED = Registro de nombre temporalmente deshabilitado
|
||||
|
@ -195,3 +195,5 @@ TRANSACTION_UNKNOWN = tuntematon transaktio
|
||||
TX_GROUP_ID_MISMATCH = transaktion ryhmä-ID:n vastaavuusvirhe
|
||||
|
||||
TRANSFER_PRIVS_DISABLED = siirtooikeudet poistettu käytöstä
|
||||
|
||||
TEMPORARY_DISABLED = Nimen rekisteröinti tilapäisesti poistettu käytöstä
|
||||
|
@ -195,3 +195,5 @@ TRANSACTION_UNKNOWN = transaction inconnue
|
||||
TX_GROUP_ID_MISMATCH = l'identifiant du groupe de transaction ne correspond pas
|
||||
|
||||
TRANSFER_PRIVS_DISABLED = privilèges de transfert désactivés
|
||||
|
||||
TEMPORARY_DISABLED = Enregistrement du nom temporairement désactivé
|
||||
|
@ -195,3 +195,5 @@ TRANSACTION_UNKNOWN = עסקה לא ידועה
|
||||
TX_GROUP_ID_MISMATCH = מזהה הקבוצה של העסקה אינו תואם
|
||||
|
||||
TRANSFER_PRIVS_DISABLED = הרשאות העברה מושבתות
|
||||
|
||||
TEMPORARY_DISABLED = רישום שמות מושבת זמנית
|
||||
|
@ -195,3 +195,5 @@ TRANSACTION_UNKNOWN = ismeretlen tranzakció
|
||||
TX_GROUP_ID_MISMATCH = a tranzakció csoportazonosítója nem egyezik
|
||||
|
||||
TRANSFER_PRIVS_DISABLED = átviteli jogosultságok letiltva
|
||||
|
||||
TEMPORARY_DISABLED = A névregisztráció ideiglenesen le van tiltva
|
||||
|
@ -195,3 +195,5 @@ TRANSACTION_UNKNOWN = transazione sconosciuta
|
||||
TX_GROUP_ID_MISMATCH = identificazione di gruppo della transazione non corrisponde
|
||||
|
||||
TRANSFER_PRIVS_DISABLED = privilegi di trasferimento disabilitati
|
||||
|
||||
TEMPORARY_DISABLED = Registrazione del nome temporaneamente disabilitata
|
||||
|
@ -195,3 +195,5 @@ TRANSACTION_UNKNOWN = 不明なトランザクション
|
||||
TX_GROUP_ID_MISMATCH = トランザクションのグループIDが一致しません
|
||||
|
||||
TRANSFER_PRIVS_DISABLED = 転送権限が無効になっています
|
||||
|
||||
TEMPORARY_DISABLED = 名前の登録が一時的に無効になっています
|
||||
|
@ -195,3 +195,5 @@ TRANSACTION_UNKNOWN = 알 수 없는 거래
|
||||
TX_GROUP_ID_MISMATCH = 트랜잭션의 그룹 ID가 일치하지 않습니다
|
||||
|
||||
TRANSFER_PRIVS_DISABLED = 권한 이전이 비활성화되었습니다.
|
||||
|
||||
TEMPORARY_DISABLED = 이름 등록이 일시적으로 비활성화되었습니다.
|
||||
|
@ -195,3 +195,5 @@ TRANSACTION_UNKNOWN = transactie onbekend
|
||||
TX_GROUP_ID_MISMATCH = groep-ID komt niet overeen
|
||||
|
||||
TRANSFER_PRIVS_DISABLED = overdrachtsrechten uitgeschakeld
|
||||
|
||||
TEMPORARY_DISABLED = Naamregistratie tijdelijk uitgeschakeld
|
||||
|
@ -195,3 +195,5 @@ TRANSACTION_UNKNOWN = transakcja nieznana
|
||||
TX_GROUP_ID_MISMATCH = niezgodność ID grupy transakcji
|
||||
|
||||
TRANSFER_PRIVS_DISABLED = uprawnienia do przenoszenia wyłączone
|
||||
|
||||
TEMPORARY_DISABLED = Rejestracja nazwy tymczasowo wyłączona
|
||||
|
@ -195,3 +195,5 @@ TRANSACTION_UNKNOWN = tranzactie necunoscuta
|
||||
TX_GROUP_ID_MISMATCH = ID-ul de grup al tranzactiei nu se potriveste
|
||||
|
||||
TRANSFER_PRIVS_DISABLED = privilegii de transfer dezactivate
|
||||
|
||||
TEMPORARY_DISABLED = Înregistrarea numelui a fost temporar dezactivată
|
||||
|
@ -195,3 +195,5 @@ TRANSACTION_UNKNOWN = неизвестная транзакция
|
||||
TX_GROUP_ID_MISMATCH = не соответствие идентификатора группы в хэш транзации
|
||||
|
||||
TRANSFER_PRIVS_DISABLED = права на передачу отключены
|
||||
|
||||
TEMPORARY_DISABLED = Регистрация имени временно отключена
|
||||
|
@ -195,3 +195,5 @@ TRANSACTION_UNKNOWN = okänd transaktion
|
||||
TX_GROUP_ID_MISMATCH = transaktionens grupp-ID matchar inte
|
||||
|
||||
TRANSFER_PRIVS_DISABLED = överföringsprivilegier inaktiverade
|
||||
|
||||
TEMPORARY_DISABLED = Namnregistrering tillfälligt inaktiverad
|
||||
|
@ -195,3 +195,5 @@ TRANSACTION_UNKNOWN = 未知的交易
|
||||
TX_GROUP_ID_MISMATCH = 群组ID交易不吻合
|
||||
|
||||
TRANSFER_PRIVS_DISABLED = 传输权限已禁用
|
||||
|
||||
TEMPORARY_DISABLED = 名称注册暂时禁用
|
||||
|
@ -195,3 +195,5 @@ TRANSACTION_UNKNOWN = 未知的交易
|
||||
TX_GROUP_ID_MISMATCH = 群組ID交易不吻合
|
||||
|
||||
TRANSFER_PRIVS_DISABLED = 傳輸權限已停用
|
||||
|
||||
TEMPORARY_DISABLED = 名稱註冊暫時停用
|
||||
|
342
src/test/java/org/qortal/test/SelfSponsorshipAlgoV2Tests.java
Normal file
342
src/test/java/org/qortal/test/SelfSponsorshipAlgoV2Tests.java
Normal file
@ -0,0 +1,342 @@
|
||||
package org.qortal.test;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.qortal.account.Account;
|
||||
import org.qortal.account.PrivateKeyAccount;
|
||||
import org.qortal.asset.Asset;
|
||||
import org.qortal.block.Block;
|
||||
import org.qortal.controller.BlockMinter;
|
||||
import org.qortal.data.account.AccountPenaltyData;
|
||||
import org.qortal.data.transaction.*;
|
||||
import org.qortal.repository.DataException;
|
||||
import org.qortal.repository.Repository;
|
||||
import org.qortal.repository.RepositoryManager;
|
||||
import org.qortal.settings.Settings;
|
||||
import org.qortal.test.common.BlockUtils;
|
||||
import org.qortal.test.common.Common;
|
||||
import org.qortal.test.common.TransactionUtils;
|
||||
import org.qortal.utils.NTP;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.qortal.test.common.AccountUtils.fee;
|
||||
|
||||
public class SelfSponsorshipAlgoV2Tests extends Common {
|
||||
|
||||
@Before
|
||||
public void beforeTest() throws DataException {
|
||||
Common.useSettings("test-settings-v2-self-sponsorship-algo-v2.json");
|
||||
NTP.setFixedOffset(Settings.getInstance().getTestNtpOffset());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void tesTransferAssetsQort() throws DataException {
|
||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||
|
||||
// Alice self share online, and will be used to mint the blocks
|
||||
PrivateKeyAccount aliceSelfShare = Common.getTestAccount(repository, "alice-reward-share");
|
||||
List<PrivateKeyAccount> onlineAccounts = new ArrayList<>();
|
||||
onlineAccounts.add(aliceSelfShare);
|
||||
|
||||
PrivateKeyAccount bobAccount = Common.getTestAccount(repository, "bob");
|
||||
PrivateKeyAccount chloeAccount = Common.getTestAccount(repository, "chloe");
|
||||
PrivateKeyAccount dilbertAccount = Common.getTestAccount(repository, "dilbert");
|
||||
|
||||
// Mint blocks
|
||||
Block block = null;
|
||||
for (int i = 0; i <= 1; i++)
|
||||
block = BlockMinter.mintTestingBlock(repository, onlineAccounts.toArray(new PrivateKeyAccount[0]));
|
||||
|
||||
// Ensure that Bob, Chloe and Dilbert are greater than level 0
|
||||
assertTrue(new Account(repository, bobAccount.getAddress()).getLevel() > 0);
|
||||
assertTrue(new Account(repository, chloeAccount.getAddress()).getLevel() > 0);
|
||||
assertTrue(new Account(repository, dilbertAccount.getAddress()).getLevel() > 0);
|
||||
|
||||
// Mint some blocks, until accounts have leveled up
|
||||
for (int i = 0; i <= 5; i++)
|
||||
block = BlockMinter.mintTestingBlock(repository, onlineAccounts.toArray(new PrivateKeyAccount[0]));
|
||||
|
||||
// Ensure that Chloe and Dilbert have more than 20 qort
|
||||
assertTrue(new Account(repository, chloeAccount.getAddress()).getConfirmedBalance(Asset.QORT) > 20); // 10 for transfer asset, 10 for fee
|
||||
assertTrue(new Account(repository, dilbertAccount.getAddress()).getConfirmedBalance(Asset.QORT) > 20); // 10 for transfer asset, 10 for fee
|
||||
|
||||
// Mint until block 10
|
||||
while (block.getBlockData().getHeight() < 10)
|
||||
block = BlockMinter.mintTestingBlock(repository, onlineAccounts.toArray(new PrivateKeyAccount[0]));
|
||||
assertEquals(10, (int) block.getBlockData().getHeight());
|
||||
|
||||
// Chloe transfer assets to Bob and Dilbert
|
||||
transferAssets(repository, chloeAccount, bobAccount);
|
||||
transferAssets(repository, chloeAccount, dilbertAccount);
|
||||
|
||||
// Dilbert transfer assets to Bob and Chloe
|
||||
transferAssets(repository, dilbertAccount, bobAccount);
|
||||
transferAssets(repository, dilbertAccount, chloeAccount);
|
||||
|
||||
// Mint until block 29 (the algo runs at block 30)
|
||||
while (block.getBlockData().getHeight() < 29)
|
||||
block = BlockMinter.mintTestingBlock(repository, onlineAccounts.toArray(new PrivateKeyAccount[0]));
|
||||
assertEquals(29, (int) block.getBlockData().getHeight());
|
||||
|
||||
// Ensure that Bob have no penalties and level 5
|
||||
assertEquals(0, (int) new Account(repository, bobAccount.getAddress()).getBlocksMintedPenalty());
|
||||
assertEquals(5, (int)bobAccount.getLevel());
|
||||
|
||||
// Ensure that Chloe have no penalties and level 5
|
||||
assertEquals(0, (int) new Account(repository, chloeAccount.getAddress()).getBlocksMintedPenalty());
|
||||
assertEquals(5, (int)chloeAccount.getLevel());
|
||||
|
||||
// Ensure that Dilbert have no penalties and level6
|
||||
assertEquals(0, (int) new Account(repository, dilbertAccount.getAddress()).getBlocksMintedPenalty());
|
||||
assertEquals(6, (int)dilbertAccount.getLevel());
|
||||
|
||||
// Mint a block, so the algo runs
|
||||
block = BlockMinter.mintTestingBlock(repository, onlineAccounts.toArray(new PrivateKeyAccount[0]));
|
||||
|
||||
// Ensure that Bob, Chloe and Dilbert are now have penalties
|
||||
assertEquals(-5000000, (int) new Account(repository, bobAccount.getAddress()).getBlocksMintedPenalty());
|
||||
assertEquals(-5000000, (int) new Account(repository, chloeAccount.getAddress()).getBlocksMintedPenalty());
|
||||
assertEquals(-5000000, (int) new Account(repository, dilbertAccount.getAddress()).getBlocksMintedPenalty());
|
||||
|
||||
// Ensure that Bob, Chloe and Dilbert are now level 0
|
||||
assertEquals(0, (int) new Account(repository, bobAccount.getAddress()).getLevel());
|
||||
assertEquals(0, (int) new Account(repository, chloeAccount.getAddress()).getLevel());
|
||||
assertEquals(0, (int) new Account(repository, dilbertAccount.getAddress()).getLevel());
|
||||
|
||||
// Orphan last block
|
||||
BlockUtils.orphanLastBlock(repository);
|
||||
|
||||
// Ensure that Bob, Chloe and Dilbert are now greater than level 0
|
||||
assertTrue(new Account(repository, bobAccount.getAddress()).getLevel() > 0);
|
||||
assertTrue(new Account(repository, chloeAccount.getAddress()).getLevel() > 0);
|
||||
assertTrue(new Account(repository, dilbertAccount.getAddress()).getLevel() > 0);
|
||||
|
||||
// Ensure that Bob, Chloe and Dilbert have no penalties again
|
||||
assertEquals(0, (int) new Account(repository, bobAccount.getAddress()).getBlocksMintedPenalty());
|
||||
assertEquals(0, (int) new Account(repository, chloeAccount.getAddress()).getBlocksMintedPenalty());
|
||||
assertEquals(0, (int) new Account(repository, dilbertAccount.getAddress()).getBlocksMintedPenalty());
|
||||
|
||||
// Run orphan check - this can't be in afterTest() because some tests access the live db
|
||||
Common.orphanCheck();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSingleTransferPrivsBeforeAlgoBlock() throws DataException {
|
||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||
|
||||
// Alice self share online, and will be used to mint the blocks
|
||||
PrivateKeyAccount aliceSelfShare = Common.getTestAccount(repository, "alice-reward-share");
|
||||
List<PrivateKeyAccount> onlineAccounts = new ArrayList<>();
|
||||
onlineAccounts.add(aliceSelfShare);
|
||||
|
||||
PrivateKeyAccount bobAccount = Common.getTestAccount(repository, "bob");
|
||||
|
||||
// Mint blocks
|
||||
Block block = null;
|
||||
for (int i = 0; i <= 5; i++)
|
||||
block = BlockMinter.mintTestingBlock(repository, onlineAccounts.toArray(new PrivateKeyAccount[0]));
|
||||
|
||||
// Ensure that Bob have more than 20 qort
|
||||
assertTrue(new Account(repository, bobAccount.getAddress()).getConfirmedBalance(Asset.QORT) > 20);
|
||||
|
||||
// Mint until block 17 (the algo runs at block 20)
|
||||
while (block.getBlockData().getHeight() < 26)
|
||||
block = BlockMinter.mintTestingBlock(repository, onlineAccounts.toArray(new PrivateKeyAccount[0]));
|
||||
assertEquals(26, (int) block.getBlockData().getHeight());
|
||||
|
||||
// Bob then issues a TRANSFER_PRIVS
|
||||
PrivateKeyAccount recipientAccount = randomTransferPrivs(repository, bobAccount);
|
||||
|
||||
// Ensure recipient has no level (actually, no account record) at this point (pre-confirmation)
|
||||
assertNull(recipientAccount.getLevel());
|
||||
|
||||
// Mint a block, so that the TRANSFER_PRIVS confirms
|
||||
block = BlockMinter.mintTestingBlock(repository, onlineAccounts.toArray(new PrivateKeyAccount[0]));
|
||||
|
||||
// Now ensure that the TRANSFER_PRIVS recipient has inherited Bob's level, and Bob is at level 0
|
||||
assertTrue(recipientAccount.getLevel() > 0);
|
||||
assertEquals(0, (int)bobAccount.getLevel());
|
||||
assertEquals(0, (int) new Account(repository, recipientAccount.getAddress()).getBlocksMintedPenalty());
|
||||
|
||||
// Mint a block, so that we can penalize Bob after transfer privs
|
||||
block = BlockMinter.mintTestingBlock(repository, onlineAccounts.toArray(new PrivateKeyAccount[0]));
|
||||
|
||||
// Update blocks minted penalty for Bob
|
||||
Set<AccountPenaltyData> penalties = new LinkedHashSet<>();
|
||||
penalties.add(new AccountPenaltyData(bobAccount.getAddress(), -5000000));
|
||||
repository.getAccountRepository().updateBlocksMintedPenalties(penalties);
|
||||
|
||||
// Mint a block, so that we check if Bob got penalized before algo runs
|
||||
block = BlockMinter.mintTestingBlock(repository, onlineAccounts.toArray(new PrivateKeyAccount[0]));
|
||||
|
||||
// Ensure Bob got penalized
|
||||
assertEquals(-5000000, (int) new Account(repository, bobAccount.getAddress()).getBlocksMintedPenalty());
|
||||
|
||||
// Mint a block, so the algo runs
|
||||
block = BlockMinter.mintTestingBlock(repository, onlineAccounts.toArray(new PrivateKeyAccount[0]));
|
||||
|
||||
// Ensure recipient account has penalty too
|
||||
assertEquals(-5000000, (int) new Account(repository, recipientAccount.getAddress()).getBlocksMintedPenalty());
|
||||
assertEquals(0, (int) new Account(repository, recipientAccount.getAddress()).getLevel());
|
||||
|
||||
// Orphan last block
|
||||
BlockUtils.orphanLastBlock(repository);
|
||||
|
||||
// Ensure recipient account has no penalty again and has a level greater than 0
|
||||
assertEquals(0, (int) new Account(repository, recipientAccount.getAddress()).getBlocksMintedPenalty());
|
||||
assertTrue(new Account(repository, recipientAccount.getAddress()).getLevel() > 0);
|
||||
|
||||
// Run orphan check - this can't be in afterTest() because some tests access the live db
|
||||
Common.orphanCheck();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultipleTransferPrivsBeforeAlgoBlock() throws DataException {
|
||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||
|
||||
// Alice self share online, and will be used to mint the blocks
|
||||
PrivateKeyAccount aliceSelfShare = Common.getTestAccount(repository, "alice-reward-share");
|
||||
List<PrivateKeyAccount> onlineAccounts = new ArrayList<>();
|
||||
onlineAccounts.add(aliceSelfShare);
|
||||
|
||||
PrivateKeyAccount bobAccount = Common.getTestAccount(repository, "bob");
|
||||
PrivateKeyAccount chloeAccount = Common.getTestAccount(repository, "chloe");
|
||||
PrivateKeyAccount dilbertAccount = Common.getTestAccount(repository, "dilbert");
|
||||
|
||||
// Mint blocks
|
||||
Block block = null;
|
||||
for (int i = 0; i <= 5; i++)
|
||||
block = BlockMinter.mintTestingBlock(repository, onlineAccounts.toArray(new PrivateKeyAccount[0]));
|
||||
|
||||
// Ensure that Bob, Chloe and Dilbert have more than 20 qort
|
||||
assertTrue(new Account(repository, bobAccount.getAddress()).getConfirmedBalance(Asset.QORT) > 20);
|
||||
assertTrue(new Account(repository, chloeAccount.getAddress()).getConfirmedBalance(Asset.QORT) > 20);
|
||||
assertTrue(new Account(repository, dilbertAccount.getAddress()).getConfirmedBalance(Asset.QORT) > 20);
|
||||
|
||||
// Mint until block 12 (the algo runs at block 20)
|
||||
while (block.getBlockData().getHeight() < 22)
|
||||
block = BlockMinter.mintTestingBlock(repository, onlineAccounts.toArray(new PrivateKeyAccount[0]));
|
||||
assertEquals(22, (int) block.getBlockData().getHeight());
|
||||
|
||||
// Bob then issues a TRANSFER_PRIVS
|
||||
PrivateKeyAccount recipientAccount1 = randomTransferPrivs(repository, bobAccount);
|
||||
|
||||
// Ensure Bob's recipient has no level (actually, no account record) at this point (pre-confirmation)
|
||||
assertNull(recipientAccount1.getLevel());
|
||||
|
||||
// Mint a block, so that Bob's TRANSFER_PRIVS confirms
|
||||
block = BlockMinter.mintTestingBlock(repository, onlineAccounts.toArray(new PrivateKeyAccount[0]));
|
||||
|
||||
// Now ensure that the Bob's TRANSFER_PRIVS recipient has inherited Bob's level, and Bob is at level 0
|
||||
assertTrue(recipientAccount1.getLevel() > 0);
|
||||
assertEquals(0, (int)bobAccount.getLevel());
|
||||
assertEquals(0, (int) new Account(repository, recipientAccount1.getAddress()).getBlocksMintedPenalty());
|
||||
|
||||
// Mint a block, so that Chloe can issue a TRANSFER_PRIVS
|
||||
block = BlockMinter.mintTestingBlock(repository, onlineAccounts.toArray(new PrivateKeyAccount[0]));
|
||||
|
||||
// Chloe then issues a TRANSFER_PRIVS
|
||||
PrivateKeyAccount recipientAccount2 = randomTransferPrivs(repository, chloeAccount);
|
||||
|
||||
// Ensure Chloe's recipient has no level (actually, no account record) at this point (pre-confirmation)
|
||||
assertNull(recipientAccount2.getLevel());
|
||||
|
||||
// Mint a block, so that Chloe's TRANSFER_PRIVS confirms
|
||||
block = BlockMinter.mintTestingBlock(repository, onlineAccounts.toArray(new PrivateKeyAccount[0]));
|
||||
|
||||
// Now ensure that the Chloe's TRANSFER_PRIVS recipient has inherited Chloe's level, and Chloe is at level 0
|
||||
assertTrue(recipientAccount2.getLevel() > 0);
|
||||
assertEquals(0, (int)chloeAccount.getLevel());
|
||||
assertEquals(0, (int) new Account(repository, recipientAccount2.getAddress()).getBlocksMintedPenalty());
|
||||
|
||||
// Mint a block, so that Dilbert can issue a TRANSFER_PRIVS
|
||||
block = BlockMinter.mintTestingBlock(repository, onlineAccounts.toArray(new PrivateKeyAccount[0]));
|
||||
|
||||
// Dilbert then issues a TRANSFER_PRIVS
|
||||
PrivateKeyAccount recipientAccount3 = randomTransferPrivs(repository, dilbertAccount);
|
||||
|
||||
// Ensure Dilbert's recipient has no level (actually, no account record) at this point (pre-confirmation)
|
||||
assertNull(recipientAccount3.getLevel());
|
||||
|
||||
// Mint a block, so that Dilbert's TRANSFER_PRIVS confirms
|
||||
block = BlockMinter.mintTestingBlock(repository, onlineAccounts.toArray(new PrivateKeyAccount[0]));
|
||||
|
||||
// Now ensure that the Dilbert's TRANSFER_PRIVS recipient has inherited Dilbert's level, and Dilbert is at level 0
|
||||
assertTrue(recipientAccount3.getLevel() > 0);
|
||||
assertEquals(0, (int)dilbertAccount.getLevel());
|
||||
assertEquals(0, (int) new Account(repository, recipientAccount3.getAddress()).getBlocksMintedPenalty());
|
||||
|
||||
// Mint a block, so that we can penalize Bob, Chloe and Dilbert after transfer privs
|
||||
block = BlockMinter.mintTestingBlock(repository, onlineAccounts.toArray(new PrivateKeyAccount[0]));
|
||||
|
||||
// Update blocks minted penalty for Bob, Chloe and Dilbert
|
||||
Set<AccountPenaltyData> penalties = new LinkedHashSet<>();
|
||||
penalties.add(new AccountPenaltyData(bobAccount.getAddress(), -5000000));
|
||||
penalties.add(new AccountPenaltyData(chloeAccount.getAddress(), -5000000));
|
||||
penalties.add(new AccountPenaltyData(dilbertAccount.getAddress(), -5000000));
|
||||
repository.getAccountRepository().updateBlocksMintedPenalties(penalties);
|
||||
|
||||
// Mint a block, so that we check if Bob, Chloe and Dilbert got penalized before algo runs
|
||||
block = BlockMinter.mintTestingBlock(repository, onlineAccounts.toArray(new PrivateKeyAccount[0]));
|
||||
|
||||
// Ensure Bob, Chloe and Dilbert got penalized
|
||||
assertEquals(-5000000, (int) new Account(repository, bobAccount.getAddress()).getBlocksMintedPenalty());
|
||||
assertEquals(-5000000, (int) new Account(repository, chloeAccount.getAddress()).getBlocksMintedPenalty());
|
||||
assertEquals(-5000000, (int) new Account(repository, dilbertAccount.getAddress()).getBlocksMintedPenalty());
|
||||
|
||||
// Mint a block, so the algo runs
|
||||
block = BlockMinter.mintTestingBlock(repository, onlineAccounts.toArray(new PrivateKeyAccount[0]));
|
||||
|
||||
// Ensure recipients accounts has penalty too
|
||||
assertEquals(-5000000, (int) new Account(repository, recipientAccount1.getAddress()).getBlocksMintedPenalty());
|
||||
assertEquals(-5000000, (int) new Account(repository, recipientAccount2.getAddress()).getBlocksMintedPenalty());
|
||||
assertEquals(-5000000, (int) new Account(repository, recipientAccount3.getAddress()).getBlocksMintedPenalty());
|
||||
assertEquals(0, (int) new Account(repository, recipientAccount1.getAddress()).getLevel());
|
||||
assertEquals(0, (int) new Account(repository, recipientAccount2.getAddress()).getLevel());
|
||||
assertEquals(0, (int) new Account(repository, recipientAccount3.getAddress()).getLevel());
|
||||
|
||||
// Orphan last block
|
||||
BlockUtils.orphanLastBlock(repository);
|
||||
|
||||
// Ensure recipients accounts has no penalty again and has a level greater than 0
|
||||
assertEquals(0, (int) new Account(repository, recipientAccount1.getAddress()).getBlocksMintedPenalty());
|
||||
assertEquals(0, (int) new Account(repository, recipientAccount2.getAddress()).getBlocksMintedPenalty());
|
||||
assertEquals(0, (int) new Account(repository, recipientAccount3.getAddress()).getBlocksMintedPenalty());
|
||||
assertTrue(new Account(repository, recipientAccount1.getAddress()).getLevel() > 0);
|
||||
assertTrue(new Account(repository, recipientAccount2.getAddress()).getLevel() > 0);
|
||||
assertTrue(new Account(repository, recipientAccount3.getAddress()).getLevel() > 0);
|
||||
|
||||
// Run orphan check - this can't be in afterTest() because some tests access the live db
|
||||
Common.orphanCheck();
|
||||
}
|
||||
}
|
||||
|
||||
private static void transferAssets(Repository repository, PrivateKeyAccount senderAccount, PrivateKeyAccount recipientAccount) throws DataException {
|
||||
for (int i = 0; i < 5; i++) {
|
||||
// Generate new asset transfers from sender to recipient
|
||||
BaseTransactionData baseTransactionData = new BaseTransactionData(NTP.getTime(), 0, senderAccount.getLastReference(), senderAccount.getPublicKey(), fee, null);
|
||||
TransactionData transactionData;
|
||||
transactionData = new TransferAssetTransactionData(baseTransactionData, recipientAccount.getAddress(), 1, 0);
|
||||
TransactionUtils.signAndImportValid(repository, transactionData, senderAccount); // updates paymentData's signature
|
||||
}
|
||||
}
|
||||
|
||||
private static PrivateKeyAccount randomTransferPrivs(Repository repository, PrivateKeyAccount senderAccount) throws DataException {
|
||||
// Generate random recipient account
|
||||
byte[] randomPrivateKey = new byte[32];
|
||||
new Random().nextBytes(randomPrivateKey);
|
||||
PrivateKeyAccount recipientAccount = new PrivateKeyAccount(repository, randomPrivateKey);
|
||||
|
||||
BaseTransactionData baseTransactionData = new BaseTransactionData(NTP.getTime(), 0, senderAccount.getLastReference(), senderAccount.getPublicKey(), fee, null);
|
||||
TransactionData transactionData = new TransferPrivsTransactionData(baseTransactionData, recipientAccount.getAddress());
|
||||
|
||||
TransactionUtils.signAndImportValid(repository, transactionData, senderAccount);
|
||||
|
||||
return recipientAccount;
|
||||
}
|
||||
|
||||
}
|
1578
src/test/java/org/qortal/test/SelfSponsorshipAlgoV3Tests.java
Normal file
1578
src/test/java/org/qortal/test/SelfSponsorshipAlgoV3Tests.java
Normal file
File diff suppressed because it is too large
Load Diff
@ -19,6 +19,8 @@
|
||||
"onlineAccountSignaturesMaxLifetime": 86400000,
|
||||
"selfSponsorshipAlgoV1SnapshotTimestamp": 9999999999999,
|
||||
"selfSponsorshipAlgoV2SnapshotTimestamp": 9999999999999,
|
||||
"selfSponsorshipAlgoV3SnapshotTimestamp": 9999999999999,
|
||||
"referenceTimestampBlock": 9999999999999,
|
||||
"mempowTransactionUpdatesTimestamp": 0,
|
||||
"blockRewardBatchStartHeight": 999999000,
|
||||
"blockRewardBatchSize": 10,
|
||||
@ -82,14 +84,14 @@
|
||||
"increaseOnlineAccountsDifficultyTimestamp": 9999999999999,
|
||||
"onlineAccountMinterLevelValidationHeight": 0,
|
||||
"selfSponsorshipAlgoV1Height": 999999999,
|
||||
"selfSponsorshipAlgoV2Height": 999999999,
|
||||
"selfSponsorshipAlgoV3Height": 999999999,
|
||||
"feeValidationFixTimestamp": 0,
|
||||
"chatReferenceTimestamp": 0,
|
||||
"arbitraryOptionalFeeTimestamp": 0,
|
||||
"unconfirmableRewardSharesHeight": 99999999,
|
||||
"selfSponsorshipAlgoV2Height": 9999999,
|
||||
"disableTransferPrivsTimestamp": 9999999999500,
|
||||
"enableTransferPrivsTimestamp": 9999999999950,
|
||||
"penaltyFixHeight": 9999999
|
||||
"enableTransferPrivsTimestamp": 9999999999950
|
||||
},
|
||||
"genesisInfo": {
|
||||
"version": 4,
|
||||
|
@ -23,6 +23,8 @@
|
||||
"onlineAccountSignaturesMaxLifetime": 86400000,
|
||||
"selfSponsorshipAlgoV1SnapshotTimestamp": 9999999999999,
|
||||
"selfSponsorshipAlgoV2SnapshotTimestamp": 9999999999999,
|
||||
"selfSponsorshipAlgoV3SnapshotTimestamp": 9999999999999,
|
||||
"referenceTimestampBlock": 9999999999999,
|
||||
"mempowTransactionUpdatesTimestamp": 0,
|
||||
"blockRewardBatchStartHeight": 999999000,
|
||||
"blockRewardBatchSize": 10,
|
||||
@ -85,14 +87,14 @@
|
||||
"increaseOnlineAccountsDifficultyTimestamp": 9999999999999,
|
||||
"onlineAccountMinterLevelValidationHeight": 0,
|
||||
"selfSponsorshipAlgoV1Height": 999999999,
|
||||
"selfSponsorshipAlgoV2Height": 999999999,
|
||||
"selfSponsorshipAlgoV3Height": 999999999,
|
||||
"feeValidationFixTimestamp": 0,
|
||||
"chatReferenceTimestamp": 0,
|
||||
"arbitraryOptionalFeeTimestamp": 0,
|
||||
"unconfirmableRewardSharesHeight": 99999999,
|
||||
"selfSponsorshipAlgoV2Height": 9999999,
|
||||
"disableTransferPrivsTimestamp": 9999999999500,
|
||||
"enableTransferPrivsTimestamp": 9999999999950,
|
||||
"penaltyFixHeight": 9999999
|
||||
"enableTransferPrivsTimestamp": 9999999999950
|
||||
},
|
||||
"genesisInfo": {
|
||||
"version": 4,
|
||||
|
@ -24,6 +24,8 @@
|
||||
"onlineAccountsModulusV2Timestamp": 9999999999999,
|
||||
"selfSponsorshipAlgoV1SnapshotTimestamp": 9999999999999,
|
||||
"selfSponsorshipAlgoV2SnapshotTimestamp": 9999999999999,
|
||||
"selfSponsorshipAlgoV3SnapshotTimestamp": 9999999999999,
|
||||
"referenceTimestampBlock": 9999999999999,
|
||||
"mempowTransactionUpdatesTimestamp": 0,
|
||||
"blockRewardBatchStartHeight": 999999000,
|
||||
"blockRewardBatchSize": 10,
|
||||
@ -86,14 +88,14 @@
|
||||
"increaseOnlineAccountsDifficultyTimestamp": 9999999999999,
|
||||
"onlineAccountMinterLevelValidationHeight": 0,
|
||||
"selfSponsorshipAlgoV1Height": 999999999,
|
||||
"selfSponsorshipAlgoV2Height": 999999999,
|
||||
"selfSponsorshipAlgoV3Height": 999999999,
|
||||
"feeValidationFixTimestamp": 0,
|
||||
"chatReferenceTimestamp": 0,
|
||||
"arbitraryOptionalFeeTimestamp": 0,
|
||||
"unconfirmableRewardSharesHeight": 99999999,
|
||||
"selfSponsorshipAlgoV2Height": 9999999,
|
||||
"disableTransferPrivsTimestamp": 9999999999500,
|
||||
"enableTransferPrivsTimestamp": 9999999999950,
|
||||
"penaltyFixHeight": 9999999
|
||||
"enableTransferPrivsTimestamp": 9999999999950
|
||||
},
|
||||
"genesisInfo": {
|
||||
"version": 4,
|
||||
|
@ -24,6 +24,8 @@
|
||||
"onlineAccountsModulusV2Timestamp": 9999999999999,
|
||||
"selfSponsorshipAlgoV1SnapshotTimestamp": 9999999999999,
|
||||
"selfSponsorshipAlgoV2SnapshotTimestamp": 9999999999999,
|
||||
"selfSponsorshipAlgoV3SnapshotTimestamp": 9999999999999,
|
||||
"referenceTimestampBlock": 9999999999999,
|
||||
"mempowTransactionUpdatesTimestamp": 0,
|
||||
"blockRewardBatchStartHeight": 999999000,
|
||||
"blockRewardBatchSize": 10,
|
||||
@ -86,14 +88,14 @@
|
||||
"increaseOnlineAccountsDifficultyTimestamp": 9999999999999,
|
||||
"onlineAccountMinterLevelValidationHeight": 0,
|
||||
"selfSponsorshipAlgoV1Height": 999999999,
|
||||
"selfSponsorshipAlgoV2Height": 999999999,
|
||||
"selfSponsorshipAlgoV3Height": 999999999,
|
||||
"feeValidationFixTimestamp": 0,
|
||||
"chatReferenceTimestamp": 0,
|
||||
"arbitraryOptionalFeeTimestamp": 0,
|
||||
"unconfirmableRewardSharesHeight": 99999999,
|
||||
"selfSponsorshipAlgoV2Height": 9999999,
|
||||
"disableTransferPrivsTimestamp": 9999999999500,
|
||||
"enableTransferPrivsTimestamp": 9999999999950,
|
||||
"penaltyFixHeight": 9999999
|
||||
"enableTransferPrivsTimestamp": 9999999999950
|
||||
},
|
||||
"genesisInfo": {
|
||||
"version": 4,
|
||||
|
@ -24,6 +24,8 @@
|
||||
"onlineAccountsModulusV2Timestamp": 9999999999999,
|
||||
"selfSponsorshipAlgoV1SnapshotTimestamp": 9999999999999,
|
||||
"selfSponsorshipAlgoV2SnapshotTimestamp": 9999999999999,
|
||||
"selfSponsorshipAlgoV3SnapshotTimestamp": 9999999999999,
|
||||
"referenceTimestampBlock": 9999999999999,
|
||||
"mempowTransactionUpdatesTimestamp": 9999999999999,
|
||||
"blockRewardBatchStartHeight": 999999000,
|
||||
"blockRewardBatchSize": 10,
|
||||
@ -86,14 +88,14 @@
|
||||
"increaseOnlineAccountsDifficultyTimestamp": 9999999999999,
|
||||
"onlineAccountMinterLevelValidationHeight": 0,
|
||||
"selfSponsorshipAlgoV1Height": 999999999,
|
||||
"selfSponsorshipAlgoV2Height": 999999999,
|
||||
"selfSponsorshipAlgoV3Height": 999999999,
|
||||
"feeValidationFixTimestamp": 0,
|
||||
"chatReferenceTimestamp": 0,
|
||||
"arbitraryOptionalFeeTimestamp": 9999999999999,
|
||||
"unconfirmableRewardSharesHeight": 99999999,
|
||||
"selfSponsorshipAlgoV2Height": 9999999,
|
||||
"disableTransferPrivsTimestamp": 9999999999500,
|
||||
"enableTransferPrivsTimestamp": 9999999999950,
|
||||
"penaltyFixHeight": 9999999
|
||||
"enableTransferPrivsTimestamp": 9999999999950
|
||||
},
|
||||
"genesisInfo": {
|
||||
"version": 4,
|
||||
|
@ -24,6 +24,8 @@
|
||||
"onlineAccountsModulusV2Timestamp": 9999999999999,
|
||||
"selfSponsorshipAlgoV1SnapshotTimestamp": 9999999999999,
|
||||
"selfSponsorshipAlgoV2SnapshotTimestamp": 9999999999999,
|
||||
"selfSponsorshipAlgoV3SnapshotTimestamp": 9999999999999,
|
||||
"referenceTimestampBlock": 9999999999999,
|
||||
"mempowTransactionUpdatesTimestamp": 0,
|
||||
"blockRewardBatchStartHeight": 999999000,
|
||||
"blockRewardBatchSize": 10,
|
||||
@ -86,14 +88,14 @@
|
||||
"increaseOnlineAccountsDifficultyTimestamp": 9999999999999,
|
||||
"onlineAccountMinterLevelValidationHeight": 0,
|
||||
"selfSponsorshipAlgoV1Height": 999999999,
|
||||
"selfSponsorshipAlgoV2Height": 999999999,
|
||||
"selfSponsorshipAlgoV3Height": 999999999,
|
||||
"feeValidationFixTimestamp": 0,
|
||||
"chatReferenceTimestamp": 0,
|
||||
"arbitraryOptionalFeeTimestamp": 0,
|
||||
"unconfirmableRewardSharesHeight": 99999999,
|
||||
"selfSponsorshipAlgoV2Height": 9999999,
|
||||
"disableTransferPrivsTimestamp": 9999999999500,
|
||||
"enableTransferPrivsTimestamp": 9999999999950,
|
||||
"penaltyFixHeight": 9999999
|
||||
"enableTransferPrivsTimestamp": 9999999999950
|
||||
},
|
||||
"genesisInfo": {
|
||||
"version": 4,
|
||||
|
@ -24,6 +24,8 @@
|
||||
"onlineAccountsModulusV2Timestamp": 9999999999999,
|
||||
"selfSponsorshipAlgoV1SnapshotTimestamp": 9999999999999,
|
||||
"selfSponsorshipAlgoV2SnapshotTimestamp": 9999999999999,
|
||||
"selfSponsorshipAlgoV3SnapshotTimestamp": 9999999999999,
|
||||
"referenceTimestampBlock": 9999999999999,
|
||||
"mempowTransactionUpdatesTimestamp": 0,
|
||||
"blockRewardBatchStartHeight": 999999000,
|
||||
"blockRewardBatchSize": 10,
|
||||
@ -87,14 +89,14 @@
|
||||
"increaseOnlineAccountsDifficultyTimestamp": 9999999999999,
|
||||
"onlineAccountMinterLevelValidationHeight": 0,
|
||||
"selfSponsorshipAlgoV1Height": 999999999,
|
||||
"selfSponsorshipAlgoV2Height": 999999999,
|
||||
"selfSponsorshipAlgoV3Height": 999999999,
|
||||
"feeValidationFixTimestamp": 0,
|
||||
"chatReferenceTimestamp": 0,
|
||||
"arbitraryOptionalFeeTimestamp": 0,
|
||||
"unconfirmableRewardSharesHeight": 99999999,
|
||||
"selfSponsorshipAlgoV2Height": 9999999,
|
||||
"disableTransferPrivsTimestamp": 9999999999500,
|
||||
"enableTransferPrivsTimestamp": 9999999999950,
|
||||
"penaltyFixHeight": 9999999
|
||||
"enableTransferPrivsTimestamp": 9999999999950
|
||||
},
|
||||
"genesisInfo": {
|
||||
"version": 4,
|
||||
|
@ -24,6 +24,8 @@
|
||||
"onlineAccountsModulusV2Timestamp": 9999999999999,
|
||||
"selfSponsorshipAlgoV1SnapshotTimestamp": 9999999999999,
|
||||
"selfSponsorshipAlgoV2SnapshotTimestamp": 9999999999999,
|
||||
"selfSponsorshipAlgoV3SnapshotTimestamp": 9999999999999,
|
||||
"referenceTimestampBlock": 9999999999999,
|
||||
"mempowTransactionUpdatesTimestamp": 0,
|
||||
"blockRewardBatchStartHeight": 999999000,
|
||||
"blockRewardBatchSize": 10,
|
||||
@ -86,14 +88,14 @@
|
||||
"increaseOnlineAccountsDifficultyTimestamp": 9999999999999,
|
||||
"onlineAccountMinterLevelValidationHeight": 0,
|
||||
"selfSponsorshipAlgoV1Height": 999999999,
|
||||
"selfSponsorshipAlgoV2Height": 999999999,
|
||||
"selfSponsorshipAlgoV3Height": 999999999,
|
||||
"feeValidationFixTimestamp": 0,
|
||||
"chatReferenceTimestamp": 0,
|
||||
"arbitraryOptionalFeeTimestamp": 0,
|
||||
"unconfirmableRewardSharesHeight": 99999999,
|
||||
"selfSponsorshipAlgoV2Height": 9999999,
|
||||
"disableTransferPrivsTimestamp": 9999999999500,
|
||||
"enableTransferPrivsTimestamp": 9999999999950,
|
||||
"penaltyFixHeight": 9999999
|
||||
"enableTransferPrivsTimestamp": 9999999999950
|
||||
},
|
||||
"genesisInfo": {
|
||||
"version": 4,
|
||||
|
@ -24,6 +24,8 @@
|
||||
"onlineAccountsModulusV2Timestamp": 9999999999999,
|
||||
"selfSponsorshipAlgoV1SnapshotTimestamp": 9999999999999,
|
||||
"selfSponsorshipAlgoV2SnapshotTimestamp": 9999999999999,
|
||||
"selfSponsorshipAlgoV3SnapshotTimestamp": 9999999999999,
|
||||
"referenceTimestampBlock": 9999999999999,
|
||||
"mempowTransactionUpdatesTimestamp": 0,
|
||||
"blockRewardBatchStartHeight": 999999000,
|
||||
"blockRewardBatchSize": 10,
|
||||
@ -86,14 +88,14 @@
|
||||
"increaseOnlineAccountsDifficultyTimestamp": 9999999999999,
|
||||
"onlineAccountMinterLevelValidationHeight": 0,
|
||||
"selfSponsorshipAlgoV1Height": 999999999,
|
||||
"selfSponsorshipAlgoV2Height": 999999999,
|
||||
"selfSponsorshipAlgoV3Height": 999999999,
|
||||
"feeValidationFixTimestamp": 0,
|
||||
"chatReferenceTimestamp": 0,
|
||||
"arbitraryOptionalFeeTimestamp": 0,
|
||||
"unconfirmableRewardSharesHeight": 99999999,
|
||||
"selfSponsorshipAlgoV2Height": 9999999,
|
||||
"disableTransferPrivsTimestamp": 9999999999500,
|
||||
"enableTransferPrivsTimestamp": 9999999999950,
|
||||
"penaltyFixHeight": 9999999
|
||||
"enableTransferPrivsTimestamp": 9999999999950
|
||||
},
|
||||
"genesisInfo": {
|
||||
"version": 4,
|
||||
|
@ -24,6 +24,8 @@
|
||||
"onlineAccountsModulusV2Timestamp": 9999999999999,
|
||||
"selfSponsorshipAlgoV1SnapshotTimestamp": 9999999999999,
|
||||
"selfSponsorshipAlgoV2SnapshotTimestamp": 9999999999999,
|
||||
"selfSponsorshipAlgoV3SnapshotTimestamp": 9999999999999,
|
||||
"referenceTimestampBlock": 9999999999999,
|
||||
"mempowTransactionUpdatesTimestamp": 0,
|
||||
"blockRewardBatchStartHeight": 999999000,
|
||||
"blockRewardBatchSize": 10,
|
||||
@ -86,14 +88,14 @@
|
||||
"increaseOnlineAccountsDifficultyTimestamp": 9999999999999,
|
||||
"onlineAccountMinterLevelValidationHeight": 0,
|
||||
"selfSponsorshipAlgoV1Height": 999999999,
|
||||
"selfSponsorshipAlgoV2Height": 999999999,
|
||||
"selfSponsorshipAlgoV3Height": 999999999,
|
||||
"feeValidationFixTimestamp": 0,
|
||||
"chatReferenceTimestamp": 0,
|
||||
"arbitraryOptionalFeeTimestamp": 0,
|
||||
"unconfirmableRewardSharesHeight": 500,
|
||||
"selfSponsorshipAlgoV2Height": 9999999,
|
||||
"disableTransferPrivsTimestamp": 9999999999500,
|
||||
"enableTransferPrivsTimestamp": 9999999999950,
|
||||
"penaltyFixHeight": 9999999
|
||||
"enableTransferPrivsTimestamp": 9999999999950
|
||||
},
|
||||
"genesisInfo": {
|
||||
"version": 4,
|
||||
|
@ -23,6 +23,8 @@
|
||||
"onlineAccountSignaturesMaxLifetime": 86400000,
|
||||
"selfSponsorshipAlgoV1SnapshotTimestamp": 9999999999999,
|
||||
"selfSponsorshipAlgoV2SnapshotTimestamp": 9999999999999,
|
||||
"selfSponsorshipAlgoV3SnapshotTimestamp": 9999999999999,
|
||||
"referenceTimestampBlock": 9999999999999,
|
||||
"mempowTransactionUpdatesTimestamp": 0,
|
||||
"blockRewardBatchStartHeight": 999999000,
|
||||
"blockRewardBatchSize": 10,
|
||||
@ -86,14 +88,14 @@
|
||||
"increaseOnlineAccountsDifficultyTimestamp": 9999999999999,
|
||||
"onlineAccountMinterLevelValidationHeight": 0,
|
||||
"selfSponsorshipAlgoV1Height": 999999999,
|
||||
"selfSponsorshipAlgoV2Height": 999999999,
|
||||
"selfSponsorshipAlgoV3Height": 999999999,
|
||||
"feeValidationFixTimestamp": 0,
|
||||
"chatReferenceTimestamp": 0,
|
||||
"arbitraryOptionalFeeTimestamp": 0,
|
||||
"unconfirmableRewardSharesHeight": 99999999,
|
||||
"selfSponsorshipAlgoV2Height": 9999999,
|
||||
"disableTransferPrivsTimestamp": 9999999999500,
|
||||
"enableTransferPrivsTimestamp": 9999999999950,
|
||||
"penaltyFixHeight": 9999999
|
||||
"enableTransferPrivsTimestamp": 9999999999950
|
||||
},
|
||||
"genesisInfo": {
|
||||
"version": 4,
|
||||
|
@ -24,6 +24,8 @@
|
||||
"onlineAccountsModulusV2Timestamp": 9999999999999,
|
||||
"selfSponsorshipAlgoV1SnapshotTimestamp": 9999999999999,
|
||||
"selfSponsorshipAlgoV2SnapshotTimestamp": 9999999999999,
|
||||
"selfSponsorshipAlgoV3SnapshotTimestamp": 9999999999999,
|
||||
"referenceTimestampBlock": 9999999999999,
|
||||
"mempowTransactionUpdatesTimestamp": 0,
|
||||
"blockRewardBatchStartHeight": 999999000,
|
||||
"blockRewardBatchSize": 10,
|
||||
@ -86,14 +88,14 @@
|
||||
"increaseOnlineAccountsDifficultyTimestamp": 9999999999999,
|
||||
"onlineAccountMinterLevelValidationHeight": 0,
|
||||
"selfSponsorshipAlgoV1Height": 20,
|
||||
"selfSponsorshipAlgoV2Height": 999999999,
|
||||
"selfSponsorshipAlgoV3Height": 999999999,
|
||||
"feeValidationFixTimestamp": 0,
|
||||
"chatReferenceTimestamp": 0,
|
||||
"arbitraryOptionalFeeTimestamp": 0,
|
||||
"unconfirmableRewardSharesHeight": 99999999,
|
||||
"selfSponsorshipAlgoV2Height": 9999999,
|
||||
"disableTransferPrivsTimestamp": 9999999999500,
|
||||
"enableTransferPrivsTimestamp": 9999999999950,
|
||||
"penaltyFixHeight": 9999999
|
||||
"enableTransferPrivsTimestamp": 9999999999950
|
||||
},
|
||||
"genesisInfo": {
|
||||
"version": 4,
|
||||
|
132
src/test/resources/test-chain-v2-self-sponsorship-algo-v2.json
Normal file
132
src/test/resources/test-chain-v2-self-sponsorship-algo-v2.json
Normal file
@ -0,0 +1,132 @@
|
||||
{
|
||||
"isTestChain": true,
|
||||
"blockTimestampMargin": 500,
|
||||
"transactionExpiryPeriod": 86400000,
|
||||
"maxBlockSize": 2097152,
|
||||
"maxBytesPerUnitFee": 0,
|
||||
"unitFees": [
|
||||
{ "timestamp": 0, "fee": "0.00000001" }
|
||||
],
|
||||
"nameRegistrationUnitFees": [
|
||||
{ "timestamp": 0, "fee": "0.00000001" },
|
||||
{ "timestamp": 1645372800000, "fee": "5" }
|
||||
],
|
||||
"requireGroupForApproval": false,
|
||||
"minAccountLevelToRewardShare": 5,
|
||||
"maxRewardSharesPerFounderMintingAccount": 20,
|
||||
"maxRewardSharesByTimestamp": [
|
||||
{ "timestamp": 0, "maxShares": 20 },
|
||||
{ "timestamp": 9999999999999, "maxShares": 3 }
|
||||
],
|
||||
"founderEffectiveMintingLevel": 10,
|
||||
"onlineAccountSignaturesMinLifetime": 3600000,
|
||||
"onlineAccountSignaturesMaxLifetime": 86400000,
|
||||
"onlineAccountsModulusV2Timestamp": 9999999999999,
|
||||
"selfSponsorshipAlgoV1SnapshotTimestamp": 0,
|
||||
"selfSponsorshipAlgoV2SnapshotTimestamp": 9999999999999,
|
||||
"selfSponsorshipAlgoV3SnapshotTimestamp": 9999999999999,
|
||||
"referenceTimestampBlock": 9999999999999,
|
||||
"mempowTransactionUpdatesTimestamp": 0,
|
||||
"blockRewardBatchStartHeight": 999999000,
|
||||
"blockRewardBatchSize": 10,
|
||||
"blockRewardBatchAccountsBlockCount": 3,
|
||||
"rewardsByHeight": [
|
||||
{ "height": 1, "reward": 100 },
|
||||
{ "height": 11, "reward": 10 },
|
||||
{ "height": 21, "reward": 1 }
|
||||
],
|
||||
"sharesByLevelV1": [
|
||||
{ "id": 1, "levels": [ 1, 2 ], "share": 0.05 },
|
||||
{ "id": 2, "levels": [ 3, 4 ], "share": 0.10 },
|
||||
{ "id": 3, "levels": [ 5, 6 ], "share": 0.15 },
|
||||
{ "id": 4, "levels": [ 7, 8 ], "share": 0.20 },
|
||||
{ "id": 5, "levels": [ 9, 10 ], "share": 0.25 }
|
||||
],
|
||||
"sharesByLevelV2": [
|
||||
{ "id": 1, "levels": [ 1, 2 ], "share": 0.06 },
|
||||
{ "id": 2, "levels": [ 3, 4 ], "share": 0.13 },
|
||||
{ "id": 3, "levels": [ 5, 6 ], "share": 0.19 },
|
||||
{ "id": 4, "levels": [ 7, 8 ], "share": 0.26 },
|
||||
{ "id": 5, "levels": [ 9, 10 ], "share": 0.32 }
|
||||
],
|
||||
"qoraHoldersShareByHeight": [
|
||||
{ "height": 1, "share": 0.20 },
|
||||
{ "height": 1000000, "share": 0.01 }
|
||||
],
|
||||
"qoraPerQortReward": 250,
|
||||
"minAccountsToActivateShareBin": 0,
|
||||
"shareBinActivationMinLevel": 7,
|
||||
"blocksNeededByLevel": [ 5, 20, 30, 40, 50, 60, 18, 80, 90, 100 ],
|
||||
"blockTimingsByHeight": [
|
||||
{ "height": 1, "target": 60000, "deviation": 30000, "power": 0.2 }
|
||||
],
|
||||
"ciyamAtSettings": {
|
||||
"feePerStep": "0.0001",
|
||||
"maxStepsPerRound": 500,
|
||||
"stepsPerFunctionCall": 10,
|
||||
"minutesPerBlock": 1
|
||||
},
|
||||
"featureTriggers": {
|
||||
"messageHeight": 0,
|
||||
"atHeight": 0,
|
||||
"assetsTimestamp": 0,
|
||||
"votingTimestamp": 0,
|
||||
"arbitraryTimestamp": 0,
|
||||
"powfixTimestamp": 0,
|
||||
"qortalTimestamp": 0,
|
||||
"newAssetPricingTimestamp": 0,
|
||||
"groupApprovalTimestamp": 0,
|
||||
"atFindNextTransactionFix": 0,
|
||||
"newBlockSigHeight": 999999,
|
||||
"shareBinFix": 999999,
|
||||
"sharesByLevelV2Height": 999999,
|
||||
"rewardShareLimitTimestamp": 9999999999999,
|
||||
"calcChainWeightTimestamp": 0,
|
||||
"transactionV5Timestamp": 0,
|
||||
"transactionV6Timestamp": 0,
|
||||
"disableReferenceTimestamp": 9999999999999,
|
||||
"increaseOnlineAccountsDifficultyTimestamp": 9999999999999,
|
||||
"onlineAccountMinterLevelValidationHeight": 0,
|
||||
"selfSponsorshipAlgoV1Height": 999999999,
|
||||
"selfSponsorshipAlgoV2Height": 30,
|
||||
"selfSponsorshipAlgoV3Height": 999999999,
|
||||
"feeValidationFixTimestamp": 0,
|
||||
"chatReferenceTimestamp": 0,
|
||||
"arbitraryOptionalFeeTimestamp": 0,
|
||||
"unconfirmableRewardSharesHeight": 99999999,
|
||||
"disableTransferPrivsTimestamp": 9999999999500,
|
||||
"enableTransferPrivsTimestamp": 9999999999950
|
||||
},
|
||||
"genesisInfo": {
|
||||
"version": 4,
|
||||
"timestamp": 0,
|
||||
"transactions": [
|
||||
{ "type": "ISSUE_ASSET", "assetName": "QORT", "description": "QORT native coin", "data": "", "quantity": 0, "isDivisible": true, "fee": 0 },
|
||||
{ "type": "ISSUE_ASSET", "assetName": "Legacy-QORA", "description": "Representative legacy QORA", "quantity": 0, "isDivisible": true, "data": "{}", "isUnspendable": true },
|
||||
{ "type": "ISSUE_ASSET", "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": "CREATE_GROUP", "creatorPublicKey": "2tiMr5LTpaWCgbRvkPK8TFd7k63DyHJMMFFsz9uBf1ZP", "groupName": "dev-group", "description": "developer group", "isOpen": false, "approvalThreshold": "PCT100", "minimumBlockDelay": 0, "maximumBlockDelay": 1440 },
|
||||
|
||||
{ "type": "UPDATE_GROUP", "ownerPublicKey": "2tiMr5LTpaWCgbRvkPK8TFd7k63DyHJMMFFsz9uBf1ZP", "groupId": 1, "newOwner": "QdSnUy6sUiEnaN87dWmE92g1uQjrvPgrWG", "newDescription": "developer group", "newIsOpen": false, "newApprovalThreshold": "PCT40", "minimumBlockDelay": 10, "maximumBlockDelay": 1440 },
|
||||
|
||||
{ "type": "ISSUE_ASSET", "issuerPublicKey": "2tiMr5LTpaWCgbRvkPK8TFd7k63DyHJMMFFsz9uBf1ZP", "assetName": "TEST", "description": "test asset", "data": "", "quantity": "1000000", "isDivisible": true, "fee": 0 },
|
||||
{ "type": "ISSUE_ASSET", "issuerPublicKey": "C6wuddsBV3HzRrXUtezE7P5MoRXp5m3mEDokRDGZB6ry", "assetName": "OTHER", "description": "other test asset", "data": "", "quantity": "1000000", "isDivisible": true, "fee": 0 },
|
||||
{ "type": "ISSUE_ASSET", "issuerPublicKey": "2tiMr5LTpaWCgbRvkPK8TFd7k63DyHJMMFFsz9uBf1ZP", "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": "QixPbJUwsaHsVEofJdozU9zgVqkK6aYhrK", "level": 5 },
|
||||
{ "type": "REWARD_SHARE", "minterPublicKey": "C6wuddsBV3HzRrXUtezE7P5MoRXp5m3mEDokRDGZB6ry", "recipient": "QixPbJUwsaHsVEofJdozU9zgVqkK6aYhrK", "rewardSharePublicKey": "CcABzvk26TFEHG7Yok84jxyd4oBtLkx8RJdGFVz2csvp", "sharePercent": 100 },
|
||||
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "Qci5m9k4rcwe4ruKrZZQKka4FzUUMut3er", "level": 5 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QaUpHNhT3Ygx6avRiKobuLdusppR5biXjL", "level": 5 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "Qci5m9k4rcwe4ruKrZZQKka4FzUUMut3er", "level": 6 }
|
||||
]
|
||||
}
|
||||
}
|
132
src/test/resources/test-chain-v2-self-sponsorship-algo-v3.json
Normal file
132
src/test/resources/test-chain-v2-self-sponsorship-algo-v3.json
Normal file
@ -0,0 +1,132 @@
|
||||
{
|
||||
"isTestChain": true,
|
||||
"blockTimestampMargin": 500,
|
||||
"transactionExpiryPeriod": 86400000,
|
||||
"maxBlockSize": 2097152,
|
||||
"maxBytesPerUnitFee": 0,
|
||||
"unitFees": [
|
||||
{ "timestamp": 0, "fee": "0.00000001" }
|
||||
],
|
||||
"nameRegistrationUnitFees": [
|
||||
{ "timestamp": 0, "fee": "0.00000001" },
|
||||
{ "timestamp": 1645372800000, "fee": "5" }
|
||||
],
|
||||
"requireGroupForApproval": false,
|
||||
"minAccountLevelToRewardShare": 5,
|
||||
"maxRewardSharesPerFounderMintingAccount": 20,
|
||||
"maxRewardSharesByTimestamp": [
|
||||
{ "timestamp": 0, "maxShares": 20 },
|
||||
{ "timestamp": 9999999999999, "maxShares": 3 }
|
||||
],
|
||||
"founderEffectiveMintingLevel": 10,
|
||||
"onlineAccountSignaturesMinLifetime": 3600000,
|
||||
"onlineAccountSignaturesMaxLifetime": 86400000,
|
||||
"onlineAccountsModulusV2Timestamp": 9999999999999,
|
||||
"selfSponsorshipAlgoV1SnapshotTimestamp": 0,
|
||||
"selfSponsorshipAlgoV2SnapshotTimestamp": 9999999999999,
|
||||
"selfSponsorshipAlgoV3SnapshotTimestamp": 9999999999999,
|
||||
"referenceTimestampBlock": 9999999999999,
|
||||
"mempowTransactionUpdatesTimestamp": 0,
|
||||
"blockRewardBatchStartHeight": 999999000,
|
||||
"blockRewardBatchSize": 10,
|
||||
"blockRewardBatchAccountsBlockCount": 3,
|
||||
"rewardsByHeight": [
|
||||
{ "height": 1, "reward": 100 },
|
||||
{ "height": 11, "reward": 10 },
|
||||
{ "height": 21, "reward": 1 }
|
||||
],
|
||||
"sharesByLevelV1": [
|
||||
{ "id": 1, "levels": [ 1, 2 ], "share": 0.05 },
|
||||
{ "id": 2, "levels": [ 3, 4 ], "share": 0.10 },
|
||||
{ "id": 3, "levels": [ 5, 6 ], "share": 0.15 },
|
||||
{ "id": 4, "levels": [ 7, 8 ], "share": 0.20 },
|
||||
{ "id": 5, "levels": [ 9, 10 ], "share": 0.25 }
|
||||
],
|
||||
"sharesByLevelV2": [
|
||||
{ "id": 1, "levels": [ 1, 2 ], "share": 0.06 },
|
||||
{ "id": 2, "levels": [ 3, 4 ], "share": 0.13 },
|
||||
{ "id": 3, "levels": [ 5, 6 ], "share": 0.19 },
|
||||
{ "id": 4, "levels": [ 7, 8 ], "share": 0.26 },
|
||||
{ "id": 5, "levels": [ 9, 10 ], "share": 0.32 }
|
||||
],
|
||||
"qoraHoldersShareByHeight": [
|
||||
{ "height": 1, "share": 0.20 },
|
||||
{ "height": 1000000, "share": 0.01 }
|
||||
],
|
||||
"qoraPerQortReward": 250,
|
||||
"minAccountsToActivateShareBin": 0,
|
||||
"shareBinActivationMinLevel": 7,
|
||||
"blocksNeededByLevel": [ 5, 20, 30, 40, 50, 60, 28, 80, 90, 100 ],
|
||||
"blockTimingsByHeight": [
|
||||
{ "height": 1, "target": 60000, "deviation": 30000, "power": 0.2 }
|
||||
],
|
||||
"ciyamAtSettings": {
|
||||
"feePerStep": "0.0001",
|
||||
"maxStepsPerRound": 500,
|
||||
"stepsPerFunctionCall": 10,
|
||||
"minutesPerBlock": 1
|
||||
},
|
||||
"featureTriggers": {
|
||||
"messageHeight": 0,
|
||||
"atHeight": 0,
|
||||
"assetsTimestamp": 0,
|
||||
"votingTimestamp": 0,
|
||||
"arbitraryTimestamp": 0,
|
||||
"powfixTimestamp": 0,
|
||||
"qortalTimestamp": 0,
|
||||
"newAssetPricingTimestamp": 0,
|
||||
"groupApprovalTimestamp": 0,
|
||||
"atFindNextTransactionFix": 0,
|
||||
"newBlockSigHeight": 999999,
|
||||
"shareBinFix": 999999,
|
||||
"sharesByLevelV2Height": 999999,
|
||||
"rewardShareLimitTimestamp": 9999999999999,
|
||||
"calcChainWeightTimestamp": 0,
|
||||
"transactionV5Timestamp": 0,
|
||||
"transactionV6Timestamp": 0,
|
||||
"disableReferenceTimestamp": 9999999999999,
|
||||
"increaseOnlineAccountsDifficultyTimestamp": 9999999999999,
|
||||
"onlineAccountMinterLevelValidationHeight": 0,
|
||||
"selfSponsorshipAlgoV1Height": 999999999,
|
||||
"selfSponsorshipAlgoV2Height": 999999999,
|
||||
"selfSponsorshipAlgoV3Height": 30,
|
||||
"feeValidationFixTimestamp": 0,
|
||||
"chatReferenceTimestamp": 0,
|
||||
"arbitraryOptionalFeeTimestamp": 0,
|
||||
"unconfirmableRewardSharesHeight": 99999999,
|
||||
"disableTransferPrivsTimestamp": 9999999999500,
|
||||
"enableTransferPrivsTimestamp": 9999999999950
|
||||
},
|
||||
"genesisInfo": {
|
||||
"version": 4,
|
||||
"timestamp": 0,
|
||||
"transactions": [
|
||||
{ "type": "ISSUE_ASSET", "assetName": "QORT", "description": "QORT native coin", "data": "", "quantity": 0, "isDivisible": true, "fee": 0 },
|
||||
{ "type": "ISSUE_ASSET", "assetName": "Legacy-QORA", "description": "Representative legacy QORA", "quantity": 0, "isDivisible": true, "data": "{}", "isUnspendable": true },
|
||||
{ "type": "ISSUE_ASSET", "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": "CREATE_GROUP", "creatorPublicKey": "2tiMr5LTpaWCgbRvkPK8TFd7k63DyHJMMFFsz9uBf1ZP", "groupName": "dev-group", "description": "developer group", "isOpen": false, "approvalThreshold": "PCT100", "minimumBlockDelay": 0, "maximumBlockDelay": 1440 },
|
||||
|
||||
{ "type": "UPDATE_GROUP", "ownerPublicKey": "2tiMr5LTpaWCgbRvkPK8TFd7k63DyHJMMFFsz9uBf1ZP", "groupId": 1, "newOwner": "QdSnUy6sUiEnaN87dWmE92g1uQjrvPgrWG", "newDescription": "developer group", "newIsOpen": false, "newApprovalThreshold": "PCT40", "minimumBlockDelay": 10, "maximumBlockDelay": 1440 },
|
||||
|
||||
{ "type": "ISSUE_ASSET", "issuerPublicKey": "2tiMr5LTpaWCgbRvkPK8TFd7k63DyHJMMFFsz9uBf1ZP", "assetName": "TEST", "description": "test asset", "data": "", "quantity": "1000000", "isDivisible": true, "fee": 0 },
|
||||
{ "type": "ISSUE_ASSET", "issuerPublicKey": "C6wuddsBV3HzRrXUtezE7P5MoRXp5m3mEDokRDGZB6ry", "assetName": "OTHER", "description": "other test asset", "data": "", "quantity": "1000000", "isDivisible": true, "fee": 0 },
|
||||
{ "type": "ISSUE_ASSET", "issuerPublicKey": "2tiMr5LTpaWCgbRvkPK8TFd7k63DyHJMMFFsz9uBf1ZP", "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": "QixPbJUwsaHsVEofJdozU9zgVqkK6aYhrK", "level": 5 },
|
||||
{ "type": "REWARD_SHARE", "minterPublicKey": "C6wuddsBV3HzRrXUtezE7P5MoRXp5m3mEDokRDGZB6ry", "recipient": "QixPbJUwsaHsVEofJdozU9zgVqkK6aYhrK", "rewardSharePublicKey": "CcABzvk26TFEHG7Yok84jxyd4oBtLkx8RJdGFVz2csvp", "sharePercent": 100 },
|
||||
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "Qci5m9k4rcwe4ruKrZZQKka4FzUUMut3er", "level": 5 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QaUpHNhT3Ygx6avRiKobuLdusppR5biXjL", "level": 5 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "Qci5m9k4rcwe4ruKrZZQKka4FzUUMut3er", "level": 6 }
|
||||
]
|
||||
}
|
||||
}
|
@ -25,6 +25,8 @@
|
||||
"onlineAccountsModulusV2Timestamp": 9999999999999,
|
||||
"selfSponsorshipAlgoV1SnapshotTimestamp": 9999999999999,
|
||||
"selfSponsorshipAlgoV2SnapshotTimestamp": 9999999999999,
|
||||
"selfSponsorshipAlgoV3SnapshotTimestamp": 9999999999999,
|
||||
"referenceTimestampBlock": 9999999999999,
|
||||
"mempowTransactionUpdatesTimestamp": 0,
|
||||
"blockRewardBatchStartHeight": 999999000,
|
||||
"blockRewardBatchSize": 10,
|
||||
@ -87,14 +89,14 @@
|
||||
"increaseOnlineAccountsDifficultyTimestamp": 9999999999999,
|
||||
"onlineAccountMinterLevelValidationHeight": 0,
|
||||
"selfSponsorshipAlgoV1Height": 999999999,
|
||||
"selfSponsorshipAlgoV2Height": 999999999,
|
||||
"selfSponsorshipAlgoV3Height": 999999999,
|
||||
"feeValidationFixTimestamp": 0,
|
||||
"chatReferenceTimestamp": 0,
|
||||
"arbitraryOptionalFeeTimestamp": 0,
|
||||
"unconfirmableRewardSharesHeight": 99999999,
|
||||
"selfSponsorshipAlgoV2Height": 9999999,
|
||||
"disableTransferPrivsTimestamp": 9999999999500,
|
||||
"enableTransferPrivsTimestamp": 9999999999950,
|
||||
"penaltyFixHeight": 9999999
|
||||
"enableTransferPrivsTimestamp": 9999999999950
|
||||
},
|
||||
"genesisInfo": {
|
||||
"version": 4,
|
||||
|
@ -0,0 +1,20 @@
|
||||
{
|
||||
"repositoryPath": "testdb",
|
||||
"bitcoinNet": "TEST3",
|
||||
"litecoinNet": "TEST3",
|
||||
"restrictedApi": false,
|
||||
"blockchainConfig": "src/test/resources/test-chain-v2-self-sponsorship-algo-v2.json",
|
||||
"exportPath": "qortal-backup-test",
|
||||
"bootstrap": false,
|
||||
"wipeUnconfirmedOnStart": false,
|
||||
"testNtpOffset": 0,
|
||||
"minPeers": 0,
|
||||
"pruneBlockLimit": 100,
|
||||
"bootstrapFilenamePrefix": "test-",
|
||||
"dataPath": "data-test",
|
||||
"tempDataPath": "data-test/_temp",
|
||||
"listsPath": "lists-test",
|
||||
"storagePolicy": "FOLLOWED_OR_VIEWED",
|
||||
"maxStorageCapacity": 104857600,
|
||||
"arrrDefaultBirthday": 1900000
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
{
|
||||
"repositoryPath": "testdb",
|
||||
"bitcoinNet": "TEST3",
|
||||
"litecoinNet": "TEST3",
|
||||
"restrictedApi": false,
|
||||
"blockchainConfig": "src/test/resources/test-chain-v2-self-sponsorship-algo-v3.json",
|
||||
"exportPath": "qortal-backup-test",
|
||||
"bootstrap": false,
|
||||
"wipeUnconfirmedOnStart": false,
|
||||
"testNtpOffset": 0,
|
||||
"minPeers": 0,
|
||||
"pruneBlockLimit": 100,
|
||||
"bootstrapFilenamePrefix": "test-",
|
||||
"dataPath": "data-test",
|
||||
"tempDataPath": "data-test/_temp",
|
||||
"listsPath": "lists-test",
|
||||
"storagePolicy": "FOLLOWED_OR_VIEWED",
|
||||
"maxStorageCapacity": 104857600,
|
||||
"arrrDefaultBirthday": 1900000
|
||||
}
|
Loading…
Reference in New Issue
Block a user