Merge branch 'Qortal:master' into master

This commit is contained in:
kennycud 2024-02-19 04:56:33 -08:00 committed by GitHub
commit 92d589a1ca
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
52 changed files with 3348 additions and 49 deletions

10
pom.xml
View File

@ -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>

View 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;
}
}

View 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;
}
}

View File

@ -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

View File

@ -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();
}

View 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)));
}
}

View 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)));
}
}

View File

@ -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)
);
}

View File

@ -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>

View File

@ -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);

View File

@ -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 */

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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);

View File

@ -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());
}

View File

@ -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,

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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ä

View File

@ -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é

View File

@ -195,3 +195,5 @@ TRANSACTION_UNKNOWN = עסקה לא ידועה
TX_GROUP_ID_MISMATCH = מזהה הקבוצה של העסקה אינו תואם
TRANSFER_PRIVS_DISABLED = הרשאות העברה מושבתות
TEMPORARY_DISABLED = רישום שמות מושבת זמנית

View File

@ -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

View File

@ -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

View File

@ -195,3 +195,5 @@ TRANSACTION_UNKNOWN = 不明なトランザクション
TX_GROUP_ID_MISMATCH = トランザクションのグループIDが一致しません
TRANSFER_PRIVS_DISABLED = 転送権限が無効になっています
TEMPORARY_DISABLED = 名前の登録が一時的に無効になっています

View File

@ -195,3 +195,5 @@ TRANSACTION_UNKNOWN = 알 수 없는 거래
TX_GROUP_ID_MISMATCH = 트랜잭션의 그룹 ID가 일치하지 않습니다
TRANSFER_PRIVS_DISABLED = 권한 이전이 비활성화되었습니다.
TEMPORARY_DISABLED = 이름 등록이 일시적으로 비활성화되었습니다.

View File

@ -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

View File

@ -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

View File

@ -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ă

View File

@ -195,3 +195,5 @@ TRANSACTION_UNKNOWN = неизвестная транзакция
TX_GROUP_ID_MISMATCH = не соответствие идентификатора группы в хэш транзации
TRANSFER_PRIVS_DISABLED = права на передачу отключены
TEMPORARY_DISABLED = Регистрация имени временно отключена

View File

@ -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

View File

@ -195,3 +195,5 @@ TRANSACTION_UNKNOWN = 未知的交易
TX_GROUP_ID_MISMATCH = 群组ID交易不吻合
TRANSFER_PRIVS_DISABLED = 传输权限已禁用
TEMPORARY_DISABLED = 名称注册暂时禁用

View File

@ -195,3 +195,5 @@ TRANSACTION_UNKNOWN = 未知的交易
TX_GROUP_ID_MISMATCH = 群組ID交易不吻合
TRANSFER_PRIVS_DISABLED = 傳輸權限已停用
TEMPORARY_DISABLED = 名稱註冊暫時停用

View 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;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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,

View 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 }
]
}
}

View 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 }
]
}
}

View File

@ -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,

View File

@ -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
}

View File

@ -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
}